Entity Framework soft delete implementation using database interceptor not working

I have implemented a database soft delete (a boolean flag that marks entries as deleted) using the following tutorial: http://www.codeguru.com/csharp/csharp/soft-deleting-entities-cleanly-using-entity-framework-6-interceptors.html

It seems to me a very good implementation because once set up soft delete is applied to a model simply by adding a [SoftDelete("IsDeleted")] annotation. Problem is so far it is not working.

The source appears to be reliable, and they even published an example of their solution: https://github.com/rakeshbabuparuchuri/EFExpensionPoints

Can you have a look at my code in case I did something wrong when applying the soft delete to my project?

This is the Model:

[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

I noticed that SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.VariableType.EdmType) does not find the soft delete attribute and returns null. But I don’t know why.

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 <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="acf9dfc9de91edc8c1c5c2ecedc8c1c5c282cfc3c1">[email protected]</a> with <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a5d5c4d6d6d2cad7c198e4c1c8cccbe5949796919093">[email protected]</a> in the Admin role        
    public static void InitializeIdentityForEF(ApplicationDbContext db)
    {
        //Initialize users and roles...
    }
}

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

There is a bug in ApplicationDbContext.cs:

protected new void OnModelCreating(DbModelBuilder modelBuilder) {...}

You are using “new” instead of “override” so OnModelCreating is never executed (try to add a breakpoint to check it). So AttributeToTableAnnotationConvention never runs and entity annotation is never added.

Changing it to

protected override void OnModelCreating(DbModelBuilder modelBuilder) {...}

will make it work

Method 2

Well, you code seems fine to me. Perhaps there is a little mistake that is breaking your app. You could try this:

  1. Remove the SoftDeleteAttribute from BC_Instance
  2. Edit the OnModelCreating method
    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());
    });
  3. Delete ApplicationDbConfiguration class
  4. Edit the context’s constructor
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
        DbInterception.Add(new SoftDeleteInterceptor());
    }

Hope this helps!


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x