12

在阅读了附加链接中的一个问题后,我了解了如何在实体框架中设置 DateCreated 和 DateModified 列并在我的应用程序中使用它。但在旧的 SQL 方式中,触发方式更受欢迎,因为从 DBA 的角度来看更安全。

那么关于哪种方式是最佳实践的任何建议?为了应用程序的完整性,是否应该在实体框架中设置它?还是应该使用触发器,因为从数据安全的角度来看它更有意义?或者有没有办法在实体框架中编写触发器?谢谢。

EF CodeFirst:Rails 样式创建和修改的列

顺便说一句,尽管这并不重要,我正在使用 ASP.NET MVC C# 构建这个应用程序。

4

4 回答 4

24

意见:触发器就像隐藏的行为,除非你去寻找它们,否则你通常不会意识到它们就在那里。我还希望在使用 EF 时尽可能保持数据库“哑”,因为我使用的是 EF,所以我的团队不需要维护 SQL 代码。

对于我的解决方案(在 C# 中将 ASP.NET WebForms 和 MVC 与另一个项目中的业务逻辑混合,该项目也包含 DataContext):

我最近遇到了类似的问题,虽然对于我的情况来说它更复杂(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();
    }

请注意,每次创建时,我都将当前用户 ID 保存在 DataContext 类的私有字段中。

于 2013-05-15T04:56:16.800 回答
6

至于DateCreated,我只会在该列上添加一个默认约束SYSDATETIME(),以便在向表中插入新行时生效。

对于DateModified,就个人而言,我可能会在这些表上使用触发器。

在我看来,触发方法:

  • 使它更容易;每次保存实体来设置它时,我都不必担心和记住DateModified

  • 使其“更安全”,因为DateModified如果有人在我的应用程序周围找到一种方法来直接修改数据库中的数据(例如使用 Access 或 Excel 或其他东西),它也将适用。

于 2013-05-15T04:42:25.893 回答
4

Entity Framework 6 具有拦截器,可用于设置创建和修改。我写了一篇文章如何做到这一点:http: //marisks.net/2016/02/27/entity-framework-soft-delete-and-automatic-created-modified-dates/

于 2016-02-27T07:53:07.523 回答
2

我同意 marc_s - 在数据库中有触发器更安全。在我公司的数据库中,我要求每个字段都有一个Date_Modified,Date_Created字段,我什至还有一个实用函数来自动创建必要的触发器。

与 Entity Framework 一起使用时,我发现我需要将[DatabaseGenerated]注释与我的 POCO 类一起使用:

[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 正在创建@Date_Modified@Date_Created我的插入/更新存储过程中的参数得到错误

过程或函数指定的参数过多。

大多数示例显示 using [NotMapped],这将允许 select/insert 工作,但是当加载该实体时,这些字段将不会显示!

或者,您可以确保任何存储过程都包含@Date_Modified,@Date_Created参数,但这与首先使用触发器的设计背道而驰。

于 2016-03-01T17:21:40.413 回答