在我阅读附件链接中的一个问题之后,我了解了如何在Entity Framework中设置DateCreated和DateModified列并在我的应用程序中使用它。但是,在旧的SQL方式中,触发方式更受欢迎,因为从DBA的角度来看更安全。
那么关于哪种方式的建议是最佳做法?应该在实体框架中为应用程序完整性而设置吗?或者应该使用触发器,因为从数据安全的角度来看它更有意义吗?或者有没有办法在实体框架中组合触发器?谢谢。
顺便说一句,即使它并不重要,我正在使用ASP.NET MVC C#构建这个应用程序。答案 0 :(得分:23)
意见:触发器就像是隐藏的行为,除非你去寻找它们,否则你通常不会意识到它们在那里。我也喜欢在使用EF时尽量保持DB“哑”,因为我使用的是EF,所以我的团队不需要维护SQL代码。
对于我的解决方案(在另一个也包含DataContext的项目中,将ASP.NET WebForms和MVC与业务逻辑混合在一起):
我最近有一个类似的问题,虽然对于我的情况来说它更复杂(DatabaseFirst,所以需要一个自定义的TT文件),解决方案大致相同。
我创建了一个界面:
public interface ITrackableEntity
{
DateTime CreatedDateTime { get; set; }
int CreatedUserID { get; set; }
DateTime ModifiedDateTime { get; set; }
int ModifiedUserID { get; set; }
}
然后我就在我需要的任何实体上实现了该接口(因为我的解决方案是DatabaseFirst,我更新了TT文件以检查表是否有这四列,如果是这样,则将接口添加到输出中)。
更新:这是我对TT文件所做的更改,我更新了EntityClassOpening()
方法:
public string EntityClassOpening(EntityType entity)
{
var trackableEntityPropNames = new string[] { "CreatedUserID", "CreatedDateTime", "ModifiedUserID", "ModifiedDateTime" };
var propNames = entity.Properties.Select(p => p.Name);
var isTrackable = trackableEntityPropNames.All(s => propNames.Contains(s));
var inherits = new List<string>();
if (!String.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)))
{
inherits.Add(_typeMapper.GetTypeName(entity.BaseType));
}
if (isTrackable)
{
inherits.Add("ITrackableEntity");
}
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", String.Join(", ", inherits)));
}
唯一剩下的就是将以下内容添加到我的部分DataContext类中:
public override int SaveChanges()
{
// fix trackable entities
var trackables = ChangeTracker.Entries<ITrackableEntity>();
if (trackables != null)
{
// added
foreach (var item in trackables.Where(t => t.State == EntityState.Added))
{
item.Entity.CreatedDateTime = System.DateTime.Now;
item.Entity.CreatedUserID = _userID;
item.Entity.ModifiedDateTime = System.DateTime.Now;
item.Entity.ModifiedUserID = _userID;
}
// modified
foreach (var item in trackables.Where(t => t.State == EntityState.Modified))
{
item.Entity.ModifiedDateTime = System.DateTime.Now;
item.Entity.ModifiedUserID = _userID;
}
}
return base.SaveChanges();
}
请注意,每次创建DataContext类时,我都会将当前用户ID保存在私有字段中。
答案 1 :(得分:6)
对于DateCreated
,我只需在该列设置为SYSDATETIME()
的默认约束上添加一个默认约束,该列在向表中插入新行时生效。
对于DateModified
,就个人而言,我可能会对这些表使用触发器。
在我看来,触发方式:
让它更容易;每次我保存实体以设置DateModified
使它“更安全”,因为如果有人找到我的应用程序的方法直接修改数据库中的数据(使用例如Access或Excel或其他东西),它也会应用DateModified
。
答案 2 :(得分:3)
实体框架6有拦截器,可用于设置创建和修改。我写了一篇文章如何做到:http://marisks.net/2016/02/27/entity-framework-soft-delete-and-automatic-created-modified-dates/
答案 3 :(得分:2)
我同意marc_s - 在数据库中使用触发器更安全。在我公司的数据库中,我要求每个字段都有一个Date_Modified
,Date_Created
字段,我甚至还有一个实用程序功能来自动创建必要的触发器。
在使用Entity Framework时,我发现我需要在POCO类中使用 [DatabaseGenerated]
注释:
[Column(TypeName = "datetime2")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? Date_Modified { get; set; }
[Column(TypeName = "datetime2")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? Date_Created { get; set; }
我试图在实体上使用存储过程映射,而EF正在我的插入/更新sprocs上创建@Date_Modified
,@Date_Created
参数,从而获得错误
过程或函数指定了太多参数。
大多数示例都显示使用[NotMapped]
,这将允许select / insert工作,但是当加载该实体时,这些字段将不会显示!
或者,您可以确保任何sprocs包含@Date_Modified
,@Date_Created
参数,但这与首先使用触发器的设计背道而驰。