c# Nhibernate a Different Object with the same Identifier value was already Associated with the Session

Asked

Viewed 1,938 times

4

I’ve been with that mistake for a while and I can’t find a good solution.

Saving objects works normally, but for changes it returns me this message:

Additional information: a Different Object with the same Identifier value was already Associated with the Session

Class that returns to Session

public class DataAccesLayer {
    private static DataAccesLayer _instance;
    public string ConnectionString { get; set; }

    private DataAccesLayer() {
    }

    private ISessionFactory _sessionFactory;

    private ISessionFactory SessionFactory {
        get {
            return _sessionFactory ?? (_sessionFactory = BuildFactory());
        }
    }

    public static DataAccesLayer Instance {
        get {
            return _instance ?? (_instance = new DataAccesLayer());
        }
    }

    private ISessionFactory BuildFactory() {
        try {
            IPersistenceConfigurer configDB = PostgreSQLConfiguration
                    .PostgreSQL82
                    .ConnectionString(ConnectionString)
                    .ShowSql()
                    .FormatSql()
                    .UseReflectionOptimizer();

            FluentConfiguration FConf = Fluently.Configure()
                .Database(configDB)
                .Mappings(c => c.FluentMappings.AddFromAssemblyOf<System.Retaguarda.Map.UsuarioMap>())
                .ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true));

            return FConf.BuildSessionFactory();
        } catch (Exception ex) {
            Console.WriteLine("Erro ao carregar configurações do banco de dados\nDica: Verifique as configurações de conexão\n" + ex.Message + "\n" + ex.InnerException);
            throw ex;
        }
    }

    public ISession OpenSession() {
        return SessionFactory.OpenSession();
    }
}

Save Method

    ISession session = DataAccesLayer.Instance.OpenSession();

    public void Gravar(T entidade) {
        using (ITransaction transacao = session.BeginTransaction()) {
            try {
                session.SaveOrUpdate(entidade);
                transacao.Commit();
                session.Flush();
            } catch (Exception ex) {
                if (!transacao.WasCommitted) {
                    transacao.Rollback();
                }
                Console.WriteLine("Erro ao gravar:\n" + ex.Message + "\n" + ex.InnerException);
                throw ex;
            }
        }
    }

If you use session.Merge(entidade); it normally saves the changes, but of the problem when inserting new items. I thought of a possible solution, but it got a little strange and I don’t intend to use:

    try {
        session.SaveOrUpdate(entidade);
    } catch (Exception) {
        session.Merge(entidade);
    }

Another possibility I tested was running a session.Clear(); before the SaveOrUpdate, but trying to save a "Person" object with "Address" list returns another error:

Additional information: Illegal Attempt to Associate a Collection with two open Sessions

1 answer

4


Note that the SaveOrUpdate() should only be used under one of these circumstances:

  • The object (entity) is new - that is, the primary key is empty (zeroed, null, etc.) and must be generated; It is equivalent to calling the method Save();
  • The object (entity) has a key that already exists in the database, but has not yet been loaded by ISession. It is equivalent to calling the method Update();

The method Merge() does the same procedure above, adding the following behavior:

  • If the entity has a valid primary key, read the record to check if it exists in the database (equivalent Get()). If it already exists, it copies the data from the updated entity to the newly loaded entity of the database. If it does not exist, it considers Save();

If, however, an entity is already loaded in the session cache (via method Get() or Lazy-load, for example), any of the above methods will fail.

When an entity is already loaded into ISession, there is no need to call any method to effect the change. This is because Nhibernate is a framework that has change tracking (change-tracking) native.

In your case, the update should be summarized simply like this:

// só isto basta - note que isto irá atualizar (UPDATE) 
// todas as entidades cujas propriedades foram alteradas
session.Flush();
  • Interesting explanation, makes perfect sense. But in this way it is changing only in Session, while I have it open (it does not record in the database). What could I be doing wrong?

  • The session.Flush() should in itself fire the(s) UPDATE(s) and any other database change commands. Another way would be to use the ISession within a block using (similar as you did with the transaction), which is recommended by the concept that Nhibernate is based on (Unit-of-work Pattern), triggering all changes made to the database at the end of the using.

  • Just another detail. As the Nhibernate observes its entities (once they have been loaded), it will only fire one UPDATE if a (at least) property has been changed. Entities that have not been changed do not generate any DML command to Flush().

  • So in the research I found something in this sense: O objeto SessionFactory, este sim deve ser criado apenas uma vez e único por banco de dados. (SessionFactory = singleton) O objeto Session tem um custo muito pequeno de criação, e deve ser criado / destruído a cada ciclo (OpenSession()). This way I have a Session for each transaction: using (ISession session = DataAccesLayer.Instance.OpenSession()) {...}

  • In that case the: session.SaveOrUpdate(entidade); works perfectly for all objects. But when trying to load the person object with a list of addresses, it brings only the person and the error when trying to access the address: -failed to lazily initialize a collection of role: Pessoa.Enderecos, no session or session was closed&#Then I realized that if I use .Not.Lazyload() in the attribute Addresses within the Personal Map, it loads normal. It would be correct to use this way, or lose much in performance?

Browser other questions tagged

You are not signed in. Login or sign up in order to post.