【问题标题】:Soft Deletes ( IsHistorical column ) with EntityFramework使用实体框架软删除(是历史列)
【发布时间】:2010-12-28 21:22:01
【问题描述】:

我正在使用一个数据库,设计人员决定用 IsHistorical 位列标记每个表。没有考虑适当的建模,也无法更改架构。

这在开发与导航属性交互的 CRUD 屏幕时会引起一些摩擦。我不能简单地获取一个产品,然后编辑它的 EntityCollection,我必须在各处手动编写 IsHistorical 检查,这让我发疯。

添加也很糟糕,因为到目前为止我已经编写了所有手动检查以查看添加是否只是软删除,因此我可以切换 IsHistoric 而不是添加重复的实体。

我考虑过的三个选项是:

  1. 修改 t4 模板以包括 IsHistorical 检查和同步。

  2. 在ObjectContext中拦截删除和添加,切换IsHistorical列,然后同步对象状态。

  3. 订阅 AssociationChanged 事件并在此处切换 IsHistorical 列。

有没有人有这方面的经验或者可以推荐最轻松的方法?

注意:是的,我知道,这是不好的建模。我已经阅读了您所拥有的有关软删除的相同文章。它很臭我必须处理这个要求,但我做到了。我只想要最轻松的方法来处理软删除,而无需为数据库中的每个导航属性编写相同的代码。

注意 #2 LukeLed 的答案在技术上是正确的,尽管它迫使你进入一个非常糟糕的穷人 ORM、无图形模式。问题在于,现在我需要从图中删除所有“已删除”的对象,然后对每个对象调用 Delete 方法。那并不能真正为我节省那么多手动仪式编码。现在我不再编写手动 IsHistoric 检查,而是收集已删除的对象并循环访问它们。

【问题讨论】:

  • 我感受到了你的痛苦,并且会仔细监控这个帖子。希望你能得到答案!
  • 为什么说它真的很差/很差?你能解释更多吗?

标签: entity-framework soft-delete


【解决方案1】:

我在我的代码中使用通用存储库。你可以这样做:

public class Repository<T> : IRepository<T> where T : EntityObject
{
    public void Delete(T obj)
    {
        if (obj is ISoftDelete)
            ((ISoftDelete)obj).IsHistorical = true
        else
            _ctx.DeleteObject(obj);
    }

您的 List() 方法也会按 IsHistorical 过滤。

编辑:

ISoftDelete接口:

public interface ISoftDelete
{
    bool IsHistorical { get; set; }
}

实体类可以很容易地标记为 ISoftDelete,因为它们是部分的。部分类定义需要添加到单独的文件中:

public partial class MyClass : EntityObject, ISoftDelete
{

}

【讨论】:

  • 如果T是一个EntityObject,那么它怎么也继承自接口ISoftDelete呢?这不是多重继承(.Net 中不允许)吗?
  • @Yaakow Ellis:这是允许的。您可以从一个类继承,但您可以实现任意数量的接口。 ISoftDelete 是接口。
  • 也就是说,ISoftDelete 定义了一个属性:IsDeleted。没有一行代码表明class X(本例中为T)继承自ISoftDelete。但是,如果X 具有IsDeleted 属性,那么根据定义,任何实例化var i = new X() 都会返回(i is ISoftDelete) 为真?所以X 实现了接口(通过遵循它的契约),即使它没有从接口继承?这是否使用反射?这样做会极大地影响性能吗?
  • @YaakovEllis:您必须指定,您的实体通过添加部分类定义来实现 ISoftDelete。接口不是通过反射来解析的。
  • 所以是这样的:gist.github.com/197e8ed135e1abe5d09a?这怎么不是多重继承的情况?
【解决方案2】:

我相信您知道,当您无法修改架构时,不会有很好的解决方案来解决这个问题。鉴于您不喜欢 Repository 选项(不过,我想知道您是不是有点草率地忽略它),这是我能想到的最好的:

  1. 处理ObjectContext.SavingChanges
  2. 当该事件触发时,通过ObjectStateManager 查找处于已删除状态的对象。如果他们有 IsHistorical 属性,请设置该属性,并将对象的状态更改为已修改。

当涉及到关联/关系时,这可能会变得很棘手,但我认为它或多或少可以满足您的需求。

【讨论】:

    【解决方案3】:

    我也使用与 LukLed 类似的代码的存储库模式,但我使用反射来查看 IsHistorical 属性是否存在(因为它是商定的命名约定):

    public class Repository<TEntityModel> where TEntityModel : EntityObject, new() 
    {
           public void Delete(TEntityModel entity)
            {
                // see if the object has an "IsHistorical" flag
                if (typeof(TEntityModel).GetProperty("IsHistorical") != null);
                {
                    // perform soft delete
                    var historicalProperty = entity.GetType().GetProperty("IsHistorical");
                    historicalProperty.SetValue(entity, true, null);
                }
                else
                {
                    // perform real delete
                    EntityContext.DeleteObject(entity);
                }
    
                EntityContext.SaveChanges();                
            }
    }
    

    用法很简单:

    using (var fubarRepository = new Repository<Fubar>)
    {
       fubarRepository.Delete(someFubar); 
    }
    

    当然,在实践中,您可以通过传递 PK 而不是实例化实体等来扩展它以允许删除。

    【讨论】:

      猜你喜欢
      • 2016-07-12
      • 2014-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多