簡體   English   中英

在 LINQ2DB 中更新/創建項目(從另一個 object 集中設置值)?

[英]Project on update/create (set values from another object en masse) in LINQ2DB?

在我的應用程序中使用 LINQ2DB 時,我嘗試使用Expression<Func<Entity, DTO>>使用實體-DTO 映射,反之亦然,如下所述: https://github.com/linq2db/linq2db/issues/1283#issuecomment-413509043

這非常適合使用 select 進行投影,但是當我需要更新/插入新記錄時我該怎么辦? 我瀏覽了 Update 和 Set 擴展方法,但找不到任何東西。

我想要實現的基本上是實體 class 和 DTO 之間基於表達式的雙向映射,有點像 AutoMapper 對 EF 的投影,但按 DTO 手動編寫,以兩個表達式的形式進行雙向轉換。

遺憾的是,我不是表達式樹和 LINQ 到 SQL 翻譯方面的專家,所以如果有人提出這樣的建議,我將不勝感激:

Expression<Func<SomeDTO, SomeEntityTable>> projectExpr =
    x => new SomeEntity
    {
        ID = x.ID,
        Name = x.Name,
        // ...
    }; // this is just so that I can write two mapping expressions per DTO and don't ever repeat them, for stuff like CRUD

// ...

using var db = ConnectionFactory.Instance.GetMainDB();
await db.SomeEntityTable
    .Where(e => e.ID == dto.ID)
    .Set(dto, projectExpr) // dto is of SomeDTO type here; this will set ONLY the values that are written in the expression
    .Set(e => e.LastEditedAt, DateTime.Now()) // able to append some more stuff after 
    .UpdateAsync();


// similar for insert operation, using the same expression

這些擴展方法應該提供所需的映射:

using var db = ConnectionFactory.Instance.GetMainDB();

await db.SomeEntityTable
    .Where(e => e.ID == dto.ID)
    .AsUpdatable()
    .Set(dto, projectExpr) // new extension method
    .Set(e => e.LastEditedAt, DateTime.Now())
    .UpdateAsync();

await db.SomeEntityTable
    .AsValueInsertable()
    .Values(dto, projectExpr) // new extension method
    .Value(e => e.LastEditedAt, DateTime.Now())
    .InsertAsync();

和實施:

public static class InsertUpdateExtensions
{
    private static MethodInfo _withIUpdatable       = Methods.LinqToDB.Update.SetUpdatableExpression;
    private static MethodInfo _withIValueInsertable = Methods.LinqToDB.Insert.VI.ValueExpression;

    public static IUpdatable<TEntity> Set<TEntity, TDto>(
        this IUpdatable<TEntity>        updatable, 
        TDto                            obj,
        Expression<Func<TDto, TEntity>> projection)
    {
        var body = projection.GetBody(Expression.Constant(obj));

        var entityParam = Expression.Parameter(typeof(TEntity), "e");

        var pairs = EnumeratePairs(body, entityParam);

        foreach (var pair in pairs)
        {
            updatable = (IUpdatable<TEntity>)_withIUpdatable.MakeGenericMethod(typeof(TEntity), pair.Item1.Type)
                .Invoke(null,
                    new object?[]
                    {
                        updatable, 
                        Expression.Lambda(pair.Item1, entityParam), 
                        Expression.Lambda(pair.Item2)
                    })!;
        }

        return updatable;
    }

    public static IValueInsertable<TEntity> Values<TEntity, TDto>(
        this IValueInsertable<TEntity>  insertable, 
        TDto                            obj,
        Expression<Func<TDto, TEntity>> projection)
    {
        var body = projection.GetBody(Expression.Constant(obj));

        var entityParam = Expression.Parameter(typeof(TEntity), "e");

        var pairs = EnumeratePairs(body, entityParam);

        foreach (var pair in pairs)
        {
            insertable = (IValueInsertable<TEntity>)_withIValueInsertable.MakeGenericMethod(typeof(TEntity), pair.Item1.Type)
                .Invoke(null,
                    new object?[]
                    {
                        insertable, 
                        Expression.Lambda(pair.Item1, entityParam), 
                        Expression.Lambda(pair.Item2)
                    })!;
        }

        return insertable;
    }

    private static IEnumerable<Tuple<Expression, Expression>> EnumeratePairs(Expression projection, Expression entityPath)
    {
        switch (projection.NodeType)
        {
            case ExpressionType.MemberInit:
            {
                var mi = (MemberInitExpression)projection;
                foreach (var b in mi.Bindings)
                {
                    if (b.BindingType == MemberBindingType.Assignment)
                    {
                        var assignment = (MemberAssignment)b;
                        foreach (var p in EnumeratePairs(Expression.MakeMemberAccess(entityPath, assignment.Member),
                                        assignment.Expression))
                        {
                            yield return p;
                        }
                    }
                }
                break;
            }

            case ExpressionType.New:
            {
                var ne = (NewExpression)projection;
                if (ne.Members != null)
                {
                    for (var index = 0; index < ne.Arguments.Count; index++)
                    {
                        var expr   = ne.Arguments[index];
                        var member = ne.Members[index];

                        foreach (var p in EnumeratePairs(Expression.MakeMemberAccess(entityPath, member), expr))
                        {
                            yield return p;
                        }
                    }
                }
                break;
            }

            case ExpressionType.MemberAccess:
            {
                yield return Tuple.Create(projection, entityPath);
                break;

            }
            default:
                throw new NotImplementedException();
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM