软删除实体框架$ C C首先$ [英] Soft Delete Entity Framework Code First

查看:208
本文介绍了软删除实体框架$ C C首先$的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我使用实体框架$ C C首先$

So I am using Entity Framework Code First

我重写的SaveChanges 的DbContext 来让我做一个软删除

I override SaveChanges in DbContext to allow me to do a "soft delete"

                        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)

Which is great, so the object knows how to mark itself as a soft delete (In this case it just sets IsDeleted to true)

我的问题是我怎么可以把它这样,当我找回它忽略任何请将isDeleted与对象?所以,如果我说 _db.Users.FirstOrDefault(用户ID = = ID)如果该用户有 ==请将isDeleted真正它会忽略它。基本上我想过滤?

My question is how can I make it such that when I retrieve the object it ignores any with IsDeleted ? So if I said _db.Users.FirstOrDefault(UserId == id) if that user had IsDeleted == true it would ignore it. Essentially I want to filter?

注:我不想只是把和放大器;&安培; ==请将isDeleted真 这就是为什么我打了类与接口,这样的删除知道如何只是工作,我想以某种方式修改检索要懂得只是工作也是基于该接口是present。

Note: I do not want to just put && IsDeleted == true That's why I am marking the classes with an interface so the remove knows how to "Just Work" and I'd like to somehow modify the retrieval to know how to "Just Work" also based on that interface being present.

在此先感谢

推荐答案

我有软删除的工作为我所有的实体和软删除的项目不通过利用的this回答。这包括当你通过导航属性访问实体。

I've got soft delete working for all my entities and soft deleted items are not retrieved via the context using a technique suggested by this answer. That includes when you access the entity via navigation properties.

添加请将isDeleted一个鉴别为可软删除每一个实体。不幸的是我还没有制定出如何做到这一点位基于实体从一个抽象类或接口派生(<一href="http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1272291-support-interfaces-as-entity-and-complex-types"相对=nofollow> EF映射当前不支持接口为实体):

Add an IsDeleted discriminator to every entity that can be soft deleted. Unfortunately I haven't worked out how to do this bit based on the entity deriving from an abstract class or an interface (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并找到所有的条目被删除:

Override SaveChanges and find all the entries to be deleted:

修改 另一种方式来覆盖删除SQL 是改变由EF6产生的存储过程

Edit Another way to override the delete sql is to change the stored procedures generated by 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因为鉴别器列不能被包括在实体:

The SoftDelete method runs sql directly on the database because discriminator columns cannot be included in entities:

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时返回表要被更新为一个实体。它处理所在的表被链接到碱基类型,而不是派生类型的情况。我怀疑我应该检查整个继承层次.... 但是,有计划地提高了元数据API ,如果​​我必须会考虑< A HREF =htt​​p://romiller.com/2013/09/24/ef-$c$c-first-mapping-between-types-tables/相对=nofollow> EF $ C C首先映射在$类型和放大器;表

GetTableName returns the table to be updated for an entity. It handles the case where the table is linked to the BaseType rather than a derived type. I suspect I should be checking the whole inheritance hierarchy.... But there are plans to improve the Metadata API and if I have to will look into 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 );
}

我有$ P $的自然键pviously创建的索引与code迁移看起来是这样的:

I had previously created indexes on natural keys in a migration with code that looked like this:

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

但是,这意味着你不能创建一个新的组织具有相同的名称作为一个已删除的组织。为了让这个我改变了code创建索引,以这样的:

But that means that you can't create a new Organisation with the same name as a deleted Organisation. In order to allow this I changed the code to create the indexes to this:

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

这不包括从索引中删除的项目

And that excludes deleted items from the index

注意 虽然不填充的导航性能,如果相关项目的软删除,外键。 例如:

Note While navigation properties are not populated if the related item is soft deleted, the foreign key is. For example:

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;

PS 投票全局过滤这里 HTTPS :?//entityframework.$c$cplex.com/workitem/945 FocusElement = CommentTextBox#并过滤包含的这里

P.S. Vote for global filtering here https://entityframework.codeplex.com/workitem/945?FocusElement=CommentTextBox# and filtered includes here

这篇关于软删除实体框架$ C C首先$的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆