为复合 DTO 编写表达式树
Composing expression trees for composite DTO
假设我有以下 3 个 DTO
public class Mailing
{
public long Id { get; set; }
//...
public long IdSender { get; set; }
public Sender Sender { get; set; }
public long IdTemplate { get; set; }
public Template Template { get; set; }
}
public class Sender
{
public long Id { get; set; }
public string Email { get; set; }
//...
}
public class Template
{
public long Id { get; set; }
public string Name { get; set; }
//...
}
我有 3 个表达式树来管理 DAO 到 DTO 的转换:
private static readonly Expression<Func<DaoMailing, Mailing>> ToMailingShort =
input => new Mailing
{
Id = input.Id,
IdSender = input.IdSender,
IdTemplate = input.IdTemplate,
// ...
};
private static readonly Expression<Func<DaoTemplate, Template>> ToTemplate =
input => new Template
{
Id = input.Id,
Name = input.Name,
// ...
};
private static readonly Expression<Func<DaoSender, Sender>> ToSender =
input => new Sender
{
Id = input.Id,
Email = input.Email,
// ...
};
如何从上面的 3 个中构建给定的表达式?
private static readonly Expression<Func<DaoMailing, DaoTemplate, DaoSender, MailingFull>> ToMailingFull =
(input, template, sender) => new Mailing
{
Id = input.Id,
IdSender = input.IdSender,
IdTemplate = input.IdTemplate,
// ...
Template = new Template
{
Id = template.Id,
Name = template.Name,
// ...
},
new Sender
{
Id = sender.Id,
Email = sender.Emai;,
// ...
}
};
显然,目标是避免重写复合转换中的每个单独转换
简短的回答是使用AutoMapper,或者将编译后的表达式转换成函数。在 C# 中编写函数很容易,编写表达式要难得多。
较长的答案是这是可能的,但并不容易。您的代码使用 'friendly' 表达式语法,但要真正混合和匹配表达式,您需要使用不友好的版本,该版本更加丑陋且难以维护:
private static readonly Expression<Func<DaoMailing, DaoTemplate, DaoSender, Mailing>> ToMailingFull =
(Expression<Func<DaoMailing, DaoTemplate, DaoSender, Mailing>>)Expression.Lambda(
Expression.MemberInit(
Expression.New(typeof(Mailing).GetConstructor(Type.EmptyTypes)),
(ToMailingShort.Body as MemberInitExpression).Bindings
.Concat(new List<MemberBinding>{
Expression.MemberBind(typeof(Mailing).GetProperty("Sender"), (ToSender.Body as MemberInitExpression).Bindings),
Expression.MemberBind(typeof(Mailing).GetProperty("Template"), (ToTemplate.Body as MemberInitExpression).Bindings)
})
),
ToMailingShort.Parameters[0],
ToTemplate.Parameters[0],
ToSender.Parameters[0]
);
假设我有以下 3 个 DTO
public class Mailing
{
public long Id { get; set; }
//...
public long IdSender { get; set; }
public Sender Sender { get; set; }
public long IdTemplate { get; set; }
public Template Template { get; set; }
}
public class Sender
{
public long Id { get; set; }
public string Email { get; set; }
//...
}
public class Template
{
public long Id { get; set; }
public string Name { get; set; }
//...
}
我有 3 个表达式树来管理 DAO 到 DTO 的转换:
private static readonly Expression<Func<DaoMailing, Mailing>> ToMailingShort =
input => new Mailing
{
Id = input.Id,
IdSender = input.IdSender,
IdTemplate = input.IdTemplate,
// ...
};
private static readonly Expression<Func<DaoTemplate, Template>> ToTemplate =
input => new Template
{
Id = input.Id,
Name = input.Name,
// ...
};
private static readonly Expression<Func<DaoSender, Sender>> ToSender =
input => new Sender
{
Id = input.Id,
Email = input.Email,
// ...
};
如何从上面的 3 个中构建给定的表达式?
private static readonly Expression<Func<DaoMailing, DaoTemplate, DaoSender, MailingFull>> ToMailingFull =
(input, template, sender) => new Mailing
{
Id = input.Id,
IdSender = input.IdSender,
IdTemplate = input.IdTemplate,
// ...
Template = new Template
{
Id = template.Id,
Name = template.Name,
// ...
},
new Sender
{
Id = sender.Id,
Email = sender.Emai;,
// ...
}
};
显然,目标是避免重写复合转换中的每个单独转换
简短的回答是使用AutoMapper,或者将编译后的表达式转换成函数。在 C# 中编写函数很容易,编写表达式要难得多。
较长的答案是这是可能的,但并不容易。您的代码使用 'friendly' 表达式语法,但要真正混合和匹配表达式,您需要使用不友好的版本,该版本更加丑陋且难以维护:
private static readonly Expression<Func<DaoMailing, DaoTemplate, DaoSender, Mailing>> ToMailingFull =
(Expression<Func<DaoMailing, DaoTemplate, DaoSender, Mailing>>)Expression.Lambda(
Expression.MemberInit(
Expression.New(typeof(Mailing).GetConstructor(Type.EmptyTypes)),
(ToMailingShort.Body as MemberInitExpression).Bindings
.Concat(new List<MemberBinding>{
Expression.MemberBind(typeof(Mailing).GetProperty("Sender"), (ToSender.Body as MemberInitExpression).Bindings),
Expression.MemberBind(typeof(Mailing).GetProperty("Template"), (ToTemplate.Body as MemberInitExpression).Bindings)
})
),
ToMailingShort.Parameters[0],
ToTemplate.Parameters[0],
ToSender.Parameters[0]
);