c# – 如何使用Entity Framework自动过滤掉软删除的实体?

我正在使用实体框架代码。我在DbContext中覆盖SaveChanges以允许我做一个“软删除”:

if (item.State == EntityState.Deleted && typeof(ISoftDelete).IsAssignableFrom(type))
{
    item.State = EntityState.Modified;
    item.Entity.GetType().GetMethod("Delete")
        .Invoke(item.Entity, null);

    continue;
}

这是很好的,所以对象知道如何将自己标记为软删除(在这种情况下,它只是将IsDeleted设置为true)。

我的问题是我如何做到这样,当我检索对象它忽略任何与IsDeleted?所以如果我说_db.Users.FirstOrDefault(UserId == id),如果该用户有IsDeleted == true,它将忽略它。本质上我想过滤?

注意:我不想只是把&& IsDeleted == true
这就是为什么我用一个界面标记类,所以删除知道如何“正常工作”,我想以某种方式修改检索,以了解如何“正常工作”也基于该界面存在。

我已经为所有我的实体进行了软删除工作,而软删除的项目不会通过上下文使用this answer建议的技术来检索。这包括当您通过导航属性访问实体时。

向可以软删除的每个实体添加一个IsDele标识符。不幸的是,我还没有根据从抽象类或接口(EF mapping doesn’t currently support interfaces as an entity)派生的实体来解决这个问题:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<Foo>().Map(m => m.Requires("IsDeleted").HasValue(false));
   modelBuilder.Entity<Bar>().Map(m => m.Requires("IsDeleted").HasValue(false));

   //It's more complicated if you have derived entities. 
   //Here 'Block' derives from 'Property'
   modelBuilder.Entity<Property>()
            .Map<Property>(m =>
            {
                m.Requires("Discriminator").HasValue("Property");
                m.Requires("IsDeleted").HasValue(false);
            })
            .Map<Block>(m =>
            {
                m.Requires("Discriminator").HasValue("Block");
                m.Requires("IsDeleted").HasValue(false);
            });
}

覆盖SaveChanges并查找要删除的所有条目:

编辑
Another way to override the delete sql是改变EF6生成的存储过程

public override int SaveChanges()
{
   foreach (var entry in ChangeTracker.Entries()
             .Where(p => p.State == EntityState.Deleted 
             && p.Entity is ModelBase))//I do have a base class for entities with a single 
                                       //"ID" property - all my entities derive from this, 
                                       //but you could use ISoftDelete here
    SoftDelete(entry);

    return base.SaveChanges();
}

SoftDelete方法直接在数据库上运行sql,因为标识符列不能包含在实体中:

private void SoftDelete(DbEntityEntry entry)
{
    var e = entry.Entity as ModelBase;
    string tableName = GetTableName(e.GetType());
    Database.ExecuteSqlCommand(
             String.Format("UPDATE {0} SET IsDeleted = 1 WHERE ID = @id", tableName)
             , new SqlParameter("id", e.ID));

    //Marking it Unchanged prevents the hard delete
    //entry.State = EntityState.Unchanged;
    //So does setting it to Detached:
    //And that is what EF does when it deletes an item
    //http://msdn.microsoft.com/en-us/data/jj592676.aspx
    entry.State = EntityState.Detached;
}

GetTableName返回要为实体更新的表。它处理表链接到BaseType而不是派生类型的情况。我怀疑我应该检查整个继承层次结构….
但是有计划到improve the Metadata API,如果我必须看看EF Code First Mapping Between Types & Tables

private readonly static Dictionary<Type, EntitySetBase> _mappingCache 
       = new Dictionary<Type, EntitySetBase>();

private ObjectContext _ObjectContext
{
    get { return (this as IObjectContextAdapter).ObjectContext; }
}

private EntitySetBase GetEntitySet(Type type)
{
    type = GetObjectType(type);

    if (_mappingCache.ContainsKey(type))
        return _mappingCache[type];

    string baseTypeName = type.BaseType.Name;
    string typeName = type.Name;

    ObjectContext octx = _ObjectContext;
    var es = octx.MetadataWorkspace
                    .GetItemCollection(DataSpace.SSpace)
                    .GetItems<EntityContainer>()
                    .SelectMany(c => c.BaseEntitySets
                                    .Where(e => e.Name == typeName 
                                    || e.Name == baseTypeName))
                    .FirstOrDefault();

    if (es == null)
        throw new ArgumentException("Entity type not found in GetEntitySet", typeName);

    _mappingCache.Add(type, es);

    return es;
}

internal String GetTableName(Type type)
{
    EntitySetBase es = GetEntitySet(type);

    //if you are using EF6
    return String.Format("[{0}].[{1}]", es.Schema, es.Table);

    //if you have a version prior to EF6
    //return string.Format( "[{0}].[{1}]", 
    //        es.MetadataProperties["Schema"].Value, 
    //        es.MetadataProperties["Table"].Value );
}

我以前在迁移中创建了自然键的索引,代码如下所示:

public override void Up()
{
    CreateIndex("dbo.Organisations", "Name", unique: true, name: "IX_NaturalKey");
}

但这意味着您无法创建与已删除的组织名称相同的新组织。为了允许这个,我改变了代码来创建索引:

public override void Up()
{
    Sql(String.Format("CREATE UNIQUE INDEX {0} ON dbo.Organisations(Name) WHERE IsDeleted = 0", "IX_NaturalKey"));
}

而且从索引中排除已删除的项目

注意
如果相关项目被软删除,则不填充导航属性,则外键为。
例如:

if(foo.BarID != null)  //trying to avoid a database call
   string name = foo.Bar.Name; //will fail because BarID is not null but Bar is

//but this works
if(foo.Bar != null) //a database call because there is a foreign key
   string name = foo.Bar.Name;

美国投票全球过滤这里https://entityframework.codeplex.com/workitem/945?FocusElement=CommentTextBox#,过滤包括here

http://stackoverflow.com/questions/12698793/how-can-i-automatically-filter-out-soft-deleted-entities-with-entity-framework

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c# – 如何使用Entity Framework自动过滤掉软删除的实体?