实体框架拦截器设置心室实体的Id字段

时间:2016-04-08 22:10:30

标签: entity-framework entity-framework-6

在我当前的项目中,我正在使用具有非常奇怪的表结构的数据库(大多数表中的所有Id字段都标记为不可为空且主要是没有为它们启用自动增量增量的那些Id字段需要是唯一的以及)。

遗憾的是,我无法修改数据库,因此我找到了另一个解决问题的原因。

我在查询数据时没有问题,但在插入过程中我想做的是, 从要插入实体的表中获取最大Id并将其递增1或者甚至更好地在插入期间使用SSELECT max(id)模式。

我希望在EF中使用Interceptor来实现这一目标,但对我来说现在看起来太难了,我设法做的就是确定这是否是插入命令。

有人可以帮我解决这个问题吗?如何通过选择最大ID或使用SELECT max(id)

来实现此目的并在插入期间设置ID
    public void TreeCreated(DbCommandTreeInterceptionContext context)
    {
        if (context.OriginalResult.CommandTreeKind != DbCommandTreeKind.Insert && context.OriginalResult.DataSpace != DataSpace.CSSpace) return;
        {
            var insertCommand = context.Result as DbInsertCommandTree;

            var property = insertCommand?.Target.VariableType.EdmType.MetadataProperties.FirstOrDefault(x => x.Name == "TableName");
            if (property == null) return;
                var tbaleName = property?.Value as ReadOnlyCollection<EdmMember>;

            var variableReference = insertCommand.Target.VariableType.Variable(insertCommand.Target.VariableName);

            var tenantProperty = variableReference.Property("ID");
            var tenantSetClause = DbExpressionBuilder.SetClause(tenantProperty, DbExpression.FromString("(SELECT MAX(ID) FROM SOMEHOWGETTABLENAME)"));
            var filteredSetClauses = insertCommand.SetClauses.Cast<DbSetClause>().Where(sc => ((DbPropertyExpression)sc.Property).Property.Name != "ID");
            var finalSetClauses = new ReadOnlyCollection<DbModificationClause>(new List<DbModificationClause>(filteredSetClauses) { tenantSetClause });

            var newInsertCommand = new DbInsertCommandTree(
                   insertCommand.MetadataWorkspace,
                   insertCommand.DataSpace,
                   insertCommand.Target,
                   finalSetClauses,
                   insertCommand.Returning);

            context.Result = newInsertCommand;
        }
    }

不幸的是,拦截器的概念对我来说有点新鲜,我完全不理解它。

更新

我设法动态构建该表达式,以便ID字段现在包含在insert语句中,但这里的问题是我不能在其中使用SQL查询。每当我尝试使用它时,它总会导致一些错误的SQL查询,所以无论如何我调整插入语句,以便在插入期间执行此SELECT MAX(ID)FROM TABLE_NAME?

1 个答案:

答案 0 :(得分:1)

从上下文中获取下一个id,然后相应地设置insert命令的参数。

    void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        var context = interceptionContext.DbContexts.First() as WhateverYourEntityContainerNameIs;

        // get the next id from the database using the context

        var theNextId = (from foo in context...)

        // update the parameter on the command

        command.Parameters["YourIdField"].Value = theNextId;
    }

请记住,这不是非常安全的线程;如果两个用户在同一时间更新同一个表,理论上它们可以获得相同的id。如果您在应用程序而不是数据库中管理密钥,则无论使用何种更新方法,这都将成为一个问题。但看起来这个决定已不在您手中。

如果这是一个问题,你可能需要做一些更激烈的事情,例如改变command.CommandText以用子查询替换values子句中的值,例如更改

insert into ... values (@YourIdField, ...)

insert into ... values ((select max(id) from...), ...)

相关问题