使用数据库拦截器的实体框架软删除实现不起作用

Posted

技术标签:

【中文标题】使用数据库拦截器的实体框架软删除实现不起作用【英文标题】:Entity Framework soft delete implementation using database interceptor not working 【发布时间】:2016-04-28 07:15:11 【问题描述】:

我已经使用以下教程实现了数据库软删除(将条目标记为已删除的布尔标志):http://www.codeguru.com/csharp/csharp/soft-deleting-entities-cleanly-using-entity-framework-6-interceptors.html

在我看来这是一个非常好的实现,因为一旦设置了软删除,只需添加 [SoftDelete("IsDeleted")] 注释即可将其应用于模型。问题是到目前为止它不起作用。

来源似乎很可靠,他们甚至发布了他们的解决方案示例:https://github.com/rakeshbabuparuchuri/EFExpensionPoints

如果我在将软删除应用到我的项目时做错了什么,你能看看我的代码吗?

这是模型:

[SoftDelete("IsDeleted")]
public class BC_Instance

    public int ID  get; set; 
    public bool IsDeleted  get; set; 

ApplicationDbContext.cs:

namespace bcplatform2.Models

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        
        

        // Add a DbSet for each one of your Entities
        //public DbSet<VirtualGuest> VirtualGuests  get; set; 
        public DbSet<BC_Instance> BiocloudInstances  get; set; 

        static ApplicationDbContext()
        
            Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
        

        public static ApplicationDbContext Create()
        
            return new ApplicationDbContext();
        

        protected new void OnModelCreating(DbModelBuilder modelBuilder)
        
            var conv = new AttributeToTableAnnotationConvention<SoftDeleteAttribute, string>(
               "SoftDeleteColumnName",
               (type, attributes) => attributes.Single().ColumnName);

            modelBuilder.Conventions.Add(conv);
        
    

ApplicationDbConfiguration.cs

namespace bcplatform2.DAL

    public class ApplicationDbConfiguration : DbConfiguration
    
        public ApplicationDbConfiguration()
        
            AddInterceptor(new SoftDeleteInterceptor());
        
    

SoftDeleteAttribute.cs:

namespace bcplatform2.Helpers

    public class SoftDeleteAttribute : Attribute
    
        public SoftDeleteAttribute(string column)
        
            ColumnName = column;
        

        public string ColumnName  get; set; 

        public static string GetSoftDeleteColumnName(EdmType type)
        
            MetadataProperty annotation = type.MetadataProperties
                .Where(p => p.Name.EndsWith("customannotation:SoftDeleteColumnName"))
                .SingleOrDefault();

            return annotation == null ? null : (string)annotation.Value;
        
    

SoftDeleteInterceptor.cs

我注意到SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.VariableType.EdmType) 没有找到软删除属性并返回null。但我不知道为什么。

namespace bcplatform2.Helpers

    public class SoftDeleteInterceptor : IDbCommandTreeInterceptor
    
        public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
        
            if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace)
            
                var queryCommand = interceptionContext.Result as DbQueryCommandTree;
                if (queryCommand != null)
                
                    var newQuery = queryCommand.Query.Accept(new SoftDeleteQueryVisitor());
                    interceptionContext.Result = new DbQueryCommandTree(
                        queryCommand.MetadataWorkspace,
                        queryCommand.DataSpace,
                        newQuery);
                

                var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree;
                if (deleteCommand != null)
                
                    var column = SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.VariableType.EdmType);
                    if (column != null)
                    
                        var setClauses = new List<DbModificationClause>();
                        var table = (EntityType)deleteCommand.Target.VariableType.EdmType;
                        if (table.Properties.Any(p => p.Name == column))
                        
                            setClauses.Add(DbExpressionBuilder.SetClause(
                                    DbExpressionBuilder.Property(
                                        DbExpressionBuilder.Variable(deleteCommand.Target.VariableType, deleteCommand.Target.VariableName),
                                        column),
                                    DbExpression.FromBoolean(true)));
                        

                        var update = new DbUpdateCommandTree(
                            deleteCommand.MetadataWorkspace,
                            deleteCommand.DataSpace,
                            deleteCommand.Target,
                            deleteCommand.Predicate,
                            setClauses.AsReadOnly(),
                            null);

                        interceptionContext.Result = update;
                    
                
            
        
    

IdentityConfig.cs

public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>

    protected override void Seed(ApplicationDbContext context)
    
        InitializeIdentityForEF(context);
        base.Seed(context);
    

    //Create User=Admin@Admin.com with password=Admin@123456 in the Admin role        
    public static void InitializeIdentityForEF(ApplicationDbContext db)
    
        //Initialize users and roles...
    

【问题讨论】:

删除标志应该是时间戳,而不是布尔值。 【参考方案1】:

ApplicationDbContext.cs 中有一个错误:

protected new void OnModelCreating(DbModelBuilder modelBuilder) ...

您使用的是“new”而不是“override”,因此永远不会执行 OnModelCreating(尝试添加断点来检查它)。所以 AttributeToTableAnnotationConvention 永远不会运行,也永远不会添加实体注释。

改成

protected override void OnModelCreating(DbModelBuilder modelBuilder) ...

会成功的

【讨论】:

如果我重写,那么原始的 OnModelCreating 将不会被调用,这不应该是这种情况。另一方面,new 会调用这两种方法。 这不正确。您应该覆盖然后调用 base.OnModelCreating 以委托给默认方法(如果需要)。只需在当前方法中设置一个断点并测试它是否停止,您会发现它不会。 抱歉,您完全正确。我误解了有关 newoverride 关键字的文档。非常感谢【参考方案2】:

嗯,你的代码对我来说似乎很好。也许有一个小错误正在破坏您的应用程序。你可以试试这个:

    BC_Instance中删除SoftDeleteAttribute

    编辑OnModelCreating 方法

    AttributeToTableAnnotationConvention<SoftDeleteAttribute, string> conv =
       new AttributeToTableAnnotationConvention<SoftDeleteAttribute, string>(
          "SoftDeleteColumnName",
          (type, attributes) => attributes.Single().ColumnName);
    
    modelBuilder.Conventions.Add(conv);
    //this will dynamically add the attribute to all models
    modelBuilder.Types().Configure(delegate(ConventionTypeConfiguration i)
    
        i.HasTableAnnotation("SoftDeleteColumnName", Entity.G etSoftDeleteColumnName());
    );
    

    删除ApplicationDbConfiguration

    编辑上下文的构造函数

    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    
        DbInterception.Add(new SoftDeleteInterceptor());
    
    

希望这会有所帮助!

【讨论】:

感谢您的帮助。一个问题:在行` i.HasTableAnnotation("SoftDeleteColumnName", Entity.GetSoftDeleteColumnName()); ` -> The name 'Entity does not exist in the current contex. 另外,我应该如何检查它是否有效。如果我直接查询数据库,我应该看到删除记录对吗?拦截器只应用在代码上? 在我的例子中,Entity 是一个由模型继承的基类 看看@tede24的回答,应该是对的!

以上是关于使用数据库拦截器的实体框架软删除实现不起作用的主要内容,如果未能解决你的问题,请参考以下文章

C# - 实体框架代码优先,延迟加载不起作用

实体框架脚手架和迁移不起作用

实体框架 4 函数导入不起作用

VS 2010 - 带有 MySql 存储过程的实体框架似乎不起作用

ASP.NET MVC Core 和实体框架中的 ToListAsync 不起作用

在实体框架 6 中不起作用的实体之间的一对一关系