我使用EF(6,如果重要的话)DbContext
子类,并尝试在将实体写入数据库时实现拦截器来设置或更新审计数据。
我的拦截器类:
public class EntitySaveInterceptor : BaseDbCommandInterceptor
{
private const string CreatedAt = "@CreatedAt";
private const string ModifiedAt = "@ModifiedAt";
//called for update, but not insert
public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
var parameters = command.Parameters
.OfType<DbParameter>()
.ToDictionary(p => p.ParameterName, p => p);
if (parameters.ContainsKey(ModifiedAt))
{
parameters[ModifiedAt].Value = SystemTime.UtcNow();
}
}
//called for insert
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
var parameters = command.Parameters
.OfType<DbParameter>()
.ToDictionary(p => p.ParameterName, p => p);
if (parameters.ContainsKey(CreatedAt))
{
parameters[CreatedAt].Value = SystemTime.UtcNow();
}
if (parameters.ContainsKey(ModifiedAt))
{
parameters[ModifiedAt].Value = SystemTime.UtcNow();
}
}
}
我遇到的问题是这些命令不包含"@CreatedAt"
或"@ModifiedAt"
个参数。相反,CreatedAt
列的值在名为@3
的参数中传递(表示值列表中的零索引位置),以及ModifiedAt
列的值(a {当值DateTime?
时,{1}}值)在生成的SQL中被硬编码为NULL
。
我找到了this answer,但对the SqlCommand
properties的粗略检查表明null
是ODP.Net特定的概念。
很明显,此时我可以检查命令文本,将BindByName
语句中的列名与INSERT
中的参数名称和NULL
匹配子句,并更新VALUES
集合以匹配新SQL。但是,如果可能的话,我更喜欢让我的command.Parameters
使用与列名称匹配的参数名称。
那么 - 我可以指示我的DbContext
或其所依赖的东西在创建DbContext
个对象时使用基于属性或列名的参数名吗?
答案 0 :(得分:2)
另一种方法是覆盖DbContext.SaveChanges
并在那里设置属性值。以下是对具有CreatedAt
属性的任何添加实体的当前日期/时间的类型不可知(但不一定是最有效)应用的示例:
public override int SaveChanges()
{
foreach( var entry in this.ChangeTracker.Entries() )
{
if( entry.State == EntityState.Added &&
entry.CurrentValues.PropertyNames.Contains( "CreatedAt" ) )
{
entry.CurrentValues[ "CreatedAt" ] = DateTime.Now;
}
}
return base.SaveChanges();
}