6

I am using an IDbCommandTreeInterceptor to enable soft deletes on my model.

System.Data.Entity.Infrastructure.Interception.DbInterception.Add(
     new SoftDeleteInterception());

I want to be able to disable the interceptor temporarily so that I can select a "deleted" entity for auditing purposes.

However, It seems like the DbInterception collection is assembly-wide.

Is there any way to create a new DbContext without interception on? Or even a way to add the interceptor to the DbContext every time it is created?

MrZander
  • 3,031
  • 1
  • 26
  • 50

1 Answers1

9

I have extended my db context class with additional property

[DbConfigurationType(typeof(DbConfig))] 
public partial class YourEntitiesDB 
{
    public bool IgnoreSoftDelete { get; set; }
}

Then in the TreeCreated(...) method i check this flag and if true then it just doesn't go further to the QueryVisitor

public class SoftDeleteInterceptor : IDbCommandTreeInterceptor
{
    public SoftDeleteInterceptor()
    {

    }
    public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
    {
        var db = interceptionContext.DbContexts.FirstOrDefault() as YourEntitiesDB;
        if (db!=null && db.IgnoreSoftDelete)
        {
            // Ignore soft delete interseptor (Used in archives)
            return;
        }

        if (interceptionContext.OriginalResult.DataSpace == DataSpace.CSpace)
        {
            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;
                }
            }
        }
    }
}

In order to use it i just set the flag to true when needed

YuorEntitiesDB DB = new YuorEntitiesDB();
DB.IgnoreSoftDelete = true;
DB.Records.Where(...)
jekcom
  • 2,065
  • 2
  • 24
  • 34
  • Man, I swear I tried this and it didn't work, but it looks like it solved the problem. Thanks! – MrZander Mar 20 '17 at 16:31
  • Updating answer, for this to work you need to also include AsNoTracking() – BlackTigerX Sep 06 '17 at 00:21
  • @BlackTigerX could you explain why exactly you need to use AsNoTracking() ? I pesronaly don't use it and it works fine – jekcom Sep 07 '17 at 07:21
  • 1
    I can't get this to work either - only the first time it seems to run as expected. It seems the TreeCreated method is only run on the very first query pr. entity type (and then cached?). So setting IgnoreSoftDelete to true will work on first query, but changing the property back to false won't work subsequently. This behavior is true whether recreating the Entities instance or not. Please elaborte @jekcom if you can get this to work...in which case I must be missing something. – Michael H. Pedersen Oct 11 '17 at 08:43
  • @MichaelH.Pedersen TreeCreated is visited only after you enumerate the query. E.g. if you do DB.Records.ToList() you will see it is being visited. I can't assist you much without seeing the code. So if you wish, you could open a new question with a code that does not work and we will try to help – jekcom Oct 16 '17 at 10:38
  • @MichaelH.Pedersen this is why you need to use .AsNoTracking – BlackTigerX Oct 17 '17 at 16:49
  • @jekcom for the problem that Michael is having – BlackTigerX Oct 17 '17 at 16:51
  • @jekcom Thanks. I still can't make TreeCreated run more than once regardless of how I enumerate and use .AsNoTracking() (thanks @BlackTigerX). I ended up writing my own .SoftDelete() extension instead. It's not quite as elegant...but that I can get working. – Michael H. Pedersen Oct 18 '17 at 11:05
  • @BlackTigerX i'm sorry, but your edit to my answer is misleading. It works without AsNoTracking() and I facilitate all the features of EF6 including saving changes. So unless you can explain exactly why .AsNoTracking() makes a difference, please do not make such edits and do not write any edits as if it was me. If you think your answer is better feel free to post it as new answer – jekcom Oct 18 '17 at 11:27
  • @MichaelH.Pedersen can you show how you add the interceptor to your db context? – jekcom Oct 18 '17 at 11:30
  • @jekcom should I specify that if it doesn't work for you, adding .AsNoTracking fixes it? it is definitely the case for me and Michael – BlackTigerX Oct 20 '17 at 18:04
  • @MichaelH.Pedersen look at the edits, and see if adding .AsNoTracking fixes the problem for you – BlackTigerX Oct 20 '17 at 18:06
  • @BlackTigerX I have tried every possible combo of settings, enumerations and whatnot. Still can't make it work. For the time being I'm sticking with my own implementation so I can get on the project at hand. jekcom I'll whip up a small example of what I've done and post somewhere in a few days. – Michael H. Pedersen Oct 21 '17 at 08:39
  • @jekcom here's an example of my implementation. It's not a thing of beauty, but for now it get's the job done. It's on jsfiddle...didn't know where else to put it. [link](https://jsfiddle.net/svhm9w1u/) – Michael H. Pedersen Oct 24 '17 at 16:13
  • @MichaelH.Pedersen, Ah, I see now. We are probably talking about 2 different things. The issue discussed here is related to **Interceptors** in general. And particularly using technique similar to the one that is described in [this topic](http://marisks.net/2016/02/27/entity-framework-soft-delete-and-automatic-created-modified-dates/) – jekcom Oct 26 '17 at 12:05
  • @jekcom Maybe I didn't phrase my response correctly. But I meant to say, that I just ditched the soft delete interceptor altogether and came up with a different type of solution....not a fix to making the actual interceptor work. But you probably figured this be now. My apologies. – Michael H. Pedersen Oct 26 '17 at 12:38
  • yeah, I guess I'll add it as another answer, to avoid the conflicts on this one, my apologies, I was just trying to make it more complete – BlackTigerX Aug 13 '18 at 22:32