【问题标题】:Custom EF Core AddOrUpdate with composite keys使用复合键自定义 EF Core AddOrUpdate
【发布时间】:2019-03-17 20:20:27
【问题描述】:

我为Microsoft.EntityFrameworkCore 构建了一个扩展,它实现了AddOrUpdateMethod。它工作正常,但对于具有复合主键的实体,AnyAsync 方法总是返回 false,即使存在具有相同键的对象。

这是方法:

public static async Task AddOrUpdateAsync<TEntity>(this DbSet<TEntity> table, Expression<Func<TEntity, object>> key, Expression<Func<TEntity, bool>> deleteExpression, params TEntity[] entities) where TEntity : class
{
    var getKeyFunction = key.Compile();
    var getShouldDeleteFunction = deleteExpression.Compile();
    var context = GetDbContext(table);
    foreach (var entity in entities)
    {
        var primaryKey = getKeyFunction(entity);
        var body = Expression.Equal(Expression.Convert(key.Body, primaryKey.GetType()), Expression.Constant(primaryKey));
        Expression<Func<TEntity, bool>> query = Expression.Lambda<Func<TEntity, bool>>(body, key.Parameters);
        var exist = await table.AnyAsync(query);
        context.Entry(entity).State = exist
            ? getShouldDeleteFunction(entity) ? EntityState.Deleted : EntityState.Modified
            : getShouldDeleteFunction(entity) ? EntityState.Detached : EntityState.Added;
    }
}

private static DbContext GetDbContext<T>(this DbSet<T> table) where T : class
{
    var infrastructure = table as IInfrastructure<IServiceProvider>;
    var serviceProvider = infrastructure.Instance;
    var currentDbContext = serviceProvider.GetService(typeof(ICurrentDbContext)) as ICurrentDbContext;
    return currentDbContext.Context;
}

我是这样使用它的:

await db.Reports.AddOrUpdateAsync(r => new { r.Number, r.Year }, r => r.Active == false, response.Reports.ToArray());

我认为这是因为我使用匿名类型作为密钥,但我不知道如何解决这个问题。

【问题讨论】:

    标签: entity-framework entity-framework-core composite-primary-key any


    【解决方案1】:

    问题似乎是匿名类型常量表达式的使用,目前导致client evaluation,而C#运算符==通过引用比较匿名类型,因此总是返回false

    获得所需服务器转换的技巧是用entity“调用”key 表达式,方法是将参数替换为Expression.Constant(entity)Expression.Invoke 在这种情况下不起作用)

    因此,删除不再需要的 var getKeyFunction = key.Compile(); 行,并使用以下内容:

    foreach (var entity in entities)
    {
        var parameter = key.Parameters[0];
        var body = Expression.Equal(
            key.Body,
            key.Body.ReplaceParameter(parameter, Expression.Constant(entity))
        );
        var query = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
        var exist = await table.AnyAsync(query);
        // ...
    }
    

    ReplaceParameter 是常用的表达式辅助方法:

    public static partial class ExpressionUtils
    {
        public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
            => new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    
        class ParameterReplacer : ExpressionVisitor
        {
            public ParameterExpression Source;
            public Expression Target;
            protected override Expression VisitParameter(ParameterExpression node)
                => node == Source ? Target : node;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多