代码之家  ›  专栏  ›  技术社区  ›  materight

自定义EF Core addor用复合键更新

  •  0
  • materight  · 技术社区  · 6 年前

    Microsoft.EntityFrameworkCore 实现 AddOrUpdateMethod . 它工作正常,但是对于具有复合主键的实体 AnyAsync

    方法如下:

    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());
    

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

    0 回复  |  直到 6 年前
        1
  •  3
  •   Ivan Stoev    6 年前

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

    key 带的表达式 entity 将参数替换为 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;
        }
    }