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();
- Po wywołaniu
_session.Save()
najpierw idzie insert, podczas którego odpalany jestOnPreInsert
i ustawiane są obie właściwości - Następnie po wywołaniu
transaction.Commit()
leci update, podczas którego odpalany jestOnPreUpdate
, a więc ustawiana jest tylko właściwośćModifiedAt
, aCreatedAt
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;
}
}