在LINQ2DB上更新/创建项目(从另一个对象集体设置值)?



在我的应用程序中使用LINQ2DB时,我尝试使用使用Expression<Func<Entity, DTO>>的实体- dto映射,反之亦然,如下所述:https://github.com/linq2db/linq2db/issues/1283#issuecomment-413509043

这适用于使用选择投影,但是当我需要更新/插入新记录时,我该怎么办?我已经浏览了更新和设置扩展方法,但找不到任何内容。

我试图实现的基本上是实体类和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();
}
}
}

最新更新