如何抑制实体框架6 IDbCommandTreeInterceptor的执行? [英] How can I suppress execution from an Entity Framework 6 IDbCommandTreeInterceptor?

查看:430
本文介绍了如何抑制实体框架6 IDbCommandTreeInterceptor的执行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 TechEd会话,但是我遇到了一个直接的问题,因为我在我的Code First模型中使用继承。第一个错误是在查询期间,因为我将IsDeleted属性放在我的超类型(基类)上,但是当我拦截了一个子类型的查询并尝试添加过滤器时,EF抱怨该类型没有这样的属性。公平的,我把属性转移到子类型,这一点工作得很好。但是当它删除时,命令树拦截器将子类型的删除更改为更新集合isdeleted = 1,但EF也为超类型(基类)生成了一个删除。这导致数据库中的外键约束错误。这是一个痛苦,但我可以通过抑制超类型的删除命令的执行来解决它。

I implemented the soft-delete pattern demonstrated by Rowan Miller during a TechEd session but I ran into an immediate problem because I am using inheritance in my Code First model. The first error was during queries because I put the IsDeleted property on my supertype (base class) but when I intercepted the query for a subtype and tried to add the filter, EF complained that there was no such property on that type. Fair enough, I moved the properties to the subtypes and that bit worked okay. But when it came to deleting, the command tree interceptor changed the delete of the subtype to an 'update set isdeleted=1' but EF also generated a delete for the supertype (base class). This caused a foreign key constraint error in the database. This is a bit of a pain but I could fix it by suppressing execution of the delete command for the supertype.

但是,我没有在拦截上下文中找到SuppressExecution方法如果我将结果设置为null我得到一个nullref异常。我想我需要一些方法来替换NullDbCommand或类似的命令。任何想法?

However, I cannot find a SuppressExecution method on the interception context and if I set the result to null I get a nullref exception. I guess I need some way to replace the command with a NullDbCommand, or similar. Any ideas?

public class CommandTreeInterceptor : IDbCommandTreeInterceptor
{
    public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
    {
        if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace) return;

        // Look for query and add 'IsDeleted = 0' filter.
        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);
        }

        // Look for delete and change it to an update instead.
        var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree;
        if (deleteCommand != null)
        {
            // !!! Need to suppress this whole command for supertypes (base class).

            var column = SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.Variable.ResultType.EdmType);
            if (column != null)
            {
                var setClause =
                    DbExpressionBuilder.SetClause(
                        deleteCommand.Target.VariableType.Variable(deleteCommand.Target.VariableName)
                            .Property(column), DbExpression.FromBoolean(true));

                var update = new DbUpdateCommandTree(deleteCommand.MetadataWorkspace,
                    deleteCommand.DataSpace,
                    deleteCommand.Target,
                    deleteCommand.Predicate,
                    new List<DbModificationClause>{ setClause }.AsReadOnly(), null);

                interceptionContext.Result = update;
            }
        }
    }
}


推荐答案

我的解决方案有点黑客,但它的工作原理。我首先尝试通过从DbCommandTree继承来创建一个NullDbCommandTree;不幸的是,后一类的大多数方法和构造函数被标记为内部,所以没有用。

My solution to this is a bit hacky but it works. I first tried to create a NullDbCommandTree by inheriting from DbCommandTree; unfortunately most of the methods and constructors in the latter class are marked as internal, so no use.

因为我不得不返回某种命令树,所以我替换了命令树与DbFunctionCommandTree。我在数据库中创建了一个不执行任何操作的存储过程,它只是被调用而不是删除。它现在可以正常工作。

Because I had to return some kind of command tree, I replaced the delete command tree with a DbFunctionCommandTree. I created a stored proc in the database that does nothing and it just gets called instead of the delete. It works okay for now.

我必须对QueryVisitor和命令树进行的其他修改是检查实体是否具有IsDeleted属性,因为在一个类层次只有一个有它。对于拥有它的那个,我们用删除替换为更新,对于那些我们不称为空函数的。所以这里是我的命令树代码:

The other modification I had to make to both the QueryVisitor and the command tree was to check whether the entity actually had the IsDeleted property, because in a class hierarchy only one has it. For the one that has it, we replace the delete with an update, and for the ones that don't we call the null function. So here is my command tree code now:

        // Look for for softdeletes delete.
        var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree;
        if (deleteCommand != null)
        {
            var columnName =
                SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.Variable.ResultType.EdmType);
            if (columnName != null)
            {
                // If the IsDeleted property is on this class, then change the delete to an update,
                // otherwise suppress the whole delete command somehow?

                var tt = (EntityType) deleteCommand.Target.Variable.ResultType.EdmType;
                if (
                    tt.DeclaredMembers.Any(
                        m => m.Name.Equals(columnName, StringComparison.InvariantCultureIgnoreCase)))
                {
                    var setClause =
                        DbExpressionBuilder.SetClause(
                            deleteCommand.Target.VariableType.Variable(deleteCommand.Target.VariableName)
                                .Property(columnName), DbExpression.FromBoolean(true));

                    var update = new DbUpdateCommandTree(deleteCommand.MetadataWorkspace,
                        deleteCommand.DataSpace,
                        deleteCommand.Target,
                        deleteCommand.Predicate,
                        new List<DbModificationClause> {setClause}.AsReadOnly(), null);

                    interceptionContext.Result = update;
                }
                else
                {
                    interceptionContext.Result = CreateNullFunction(deleteCommand.MetadataWorkspace,
                        deleteCommand.DataSpace);
                }
            }
        }
    }

    private DbFunctionCommandTree CreateNullFunction(MetadataWorkspace metadataWorkspace, DataSpace dataSpace)
    {
        var function = EdmFunction.Create("usp_SoftDeleteNullFunction", "dbo", dataSpace,
            new EdmFunctionPayload { CommandText = "usp_SoftDeleteNullFunction" }, null);
        return new DbFunctionCommandTree(metadataWorkspace, dataSpace, function,
            TypeUsage.CreateStringTypeUsage(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), false, true),
            null);
    }
}

这里是查询访问者代码:

and here is the query visitor code:

        var columnName = SoftDeleteAttribute.GetSoftDeleteColumnName((expression.Target.ElementType));

        if (columnName == null  || !expression.Target.ElementType.Members.Any(m => m.Name.Equals(columnName, StringComparison.InvariantCultureIgnoreCase)))
        {
            return base.Visit(expression);
        }

        var binding = expression.Bind();

        return binding.Filter(binding.VariableType.Variable(binding.VariableName).Property(columnName).NotEqual(DbExpression.FromBoolean(true)));

这篇关于如何抑制实体框架6 IDbCommandTreeInterceptor的执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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