NHibernate generuje INSERT a potem UPDATE przy save w transakcji

0

Cześć

Tworzę pierwszy hello world projekt na ASP.NET Core, .NET Framework 4.6.1 NHibernate 5 żeby poznać coś oprócz EF i mam pierwszą zagwozdkę.
Wszystkie modele oparte są na klasie bazowej z właściwościami ModifiedAt i CreatedAt, dodałem też event handlery dla inserta (który ustawia oba pola) i dla update (który ustawia czas modyfikacji). Gdy tworzę sobie przykładowy obiekt i zapisuję go metodą Save z sesji, to wszystko działa ok:

            var account = new Account {Login = "pawel"};
            _session.Save(account);

Natomiast po dodaniu transakcji:

            var transaction = _session.BeginTransaction();
            var account = new Account {Login = "pawel"};
            _session.Save(account);
            transaction.Commit();
            transaction.Dispose();
  1. Po wywołaniu _session.Save() najpierw idzie insert, podczas którego odpalany jest OnPreInsert i ustawiane są obie właściwości
  2. Następnie po wywołaniu transaction.Commit() leci update, podczas którego odpalany jest OnPreUpdate, a więc ustawiana jest tylko właściwość ModifiedAt, a CreatedAt ma pierwotną wartość 0001-01-01 itd.
    Do bazy ostatecznie lecą takie zapytania
    INSERT INTO [Account] (CreatedAt, ModifiedAt, Login) VALUES (?, ?, ?); select SCOPE_IDENTITY()
    UPDATE [Account] SET CreatedAt = ?, ModifiedAt = ?, Login = ? WHERE Id = ?

Czym to jest spowodowane i jak temu zaradzić?

Użyte kody:

public static class SessionFactoryProvider
    {
        public static ISessionFactory CreateSessionFactory(string connectionString)
        {
            return Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2012
                    .ConnectionString(connectionString))
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<EntityBase>())
                .ExposeConfiguration(BuildSchema)
                .BuildSessionFactory();
        }

        public static void BuildSchema(Configuration config)
        {
            var auditListeners = new[] {new AuditListener()};
            config.AppendListeners(ListenerType.PreInsert, auditListeners);
            config.AppendListeners(ListenerType.PreUpdate, auditListeners);

            new SchemaUpdate(config)
                .Execute(false, true);
        }
    }

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            var connectionString = Configuration.GetConnectionString("SofiaContext");
            var sessionFactory = SessionFactoryProvider.CreateSessionFactory(connectionString);
            services.AddTransient<ISession>(provider => sessionFactory.OpenSession(new LoggingInterceptor()));
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
    }

    public class LoggingInterceptor : EmptyInterceptor
    {
        public override SqlString OnPrepareStatement(SqlString sql)
        {
            Console.WriteLine(sql);

            return sql;
        }
    }

public class AuditListener : IPreUpdateEventListener, IPreInsertEventListener
    {
        public Task<bool> OnPreUpdateAsync(PreUpdateEvent @event, CancellationToken cancellationToken)
        {
            Set(@event.Persister, @event.State, "ModifiedAt", DateTime.Now);

            var tcs = new TaskCompletionSource<bool>();
            tcs.SetResult(false);
            return tcs.Task;
        }

        public bool OnPreUpdate(PreUpdateEvent @event)
        {
            Set(@event.Persister, @event.State, "ModifiedAt", DateTime.Now);

            return false;
        }

        public Task<bool> OnPreInsertAsync(PreInsertEvent @event, CancellationToken cancellationToken)
        {
            var time = DateTime.Now;
            Set(@event.Persister, @event.State, "CreatedAt", time);
            Set(@event.Persister, @event.State, "ModifiedAt", time);

            var tcs = new TaskCompletionSource<bool>();
            tcs.SetResult(false);
            return tcs.Task;
        }

        public bool OnPreInsert(PreInsertEvent @event)
        {
            var time = DateTime.Now;
            Set(@event.Persister, @event.State, "CreatedAt", time);
            Set(@event.Persister, @event.State, "ModifiedAt", time);

            return false;
        }

        private void Set(IEntityPersister persister, object[] state, string field, object value)
        {
            var index = Array.IndexOf(persister.PropertyNames, field);
            state[index] = value;
        }
    }
0

@mad_penguin: wiem, że strasznie odkopuję temat ale zaryzykuję.

Wygląda mi to na brak określenia "właściciela" związku za pomocą Inverse() przy definiowaniu mapowania. Problem zapewne już rozwiązałeś ale jeśli to było to, to już niech zostanie dla potomnych.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0