我想使用 AutoMapper 的可查询扩展.ProjectTo(这是一个覆盖 ToString 并具有自定义属性的示例:
class Claim {
public int Id { get; set; }
public int TypeId { get; set; }
public ClaimType Type { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Name
=> $"{FirstName} {LastName}";
}
class ClaimType {
public int Id { get; set; }
public string Value { get; set; }
public string Abbrev { get; set; }
public override string ToString()
=> Value + (Abbrev != null ? $" ({Abbrev})" : "");
}
class ClaimViewModel {
public int Id { get; set; }
public string Type { get; set; }
public string Name { get; set; }
}
现在假设我有上述两个数据库模型和视图模型,我想在查询中将 Claim 投影到 ClaimViewModel。我知道如何做到这一点的唯一方法是这样的:
CreateMap<Claim, ClaimViewModel>()
.ForMember(c => c.ClaimType, x => x.MapFrom(c => c.ClaimType.Value + (c.ClaimType.Abbrev != null ? $" ({c.ClaimType.Abbrev})" : "")))
.ForMember(c => c.Name, x => x.MapFrom(c => $"{c.FirstName} {c.LastName}"));
显然,这复制了 Name 属性和 ClaimType.ToString 方法的代码。是否有任何既定的模式可以解决这个问题并保持代码干燥?我们的模型中有很多自定义属性和 ToString 覆盖。我发现我可以对 Name 属性执行以下操作...首先,在 Claim 类中:
public static readonly Expression<Func<Claim, string>> NameExpr = (c) => $"{c.FirstName} {c.LastName}";
public string Name => NameExpr(this);
然后在映射中这样做:
.ForMember(c => c.Name, x => x.MapFrom(Claim.NameExpr));
对于那个非常简单的情况,这似乎很好,但它不适用于具有外键引用的情况,例如 ClaimType.ToString 方法。在这种情况下,我可以将表达式放在 Claim 中并在 Claim 映射中引用它,但是当我需要将 ClaimType 投影到 ClaimTypeViewModel 时,我必须复制代码。或者,如果有另一个数据库模型引用了 ClaimType 模型,我也会遇到同样的问题。
如果对答案很重要,ORM 是 EF Core @ 2.1.3。
编辑:当我写这个问题时,我没有意识到这一点,但是上面的映射将在没有 ForMember 配置的情况下工作,但我在 SQL Profiler 中注意到查询将拉回 Claim 和 ClaimType 模型中的每一列,而不仅仅是列需要。这很好,但我真的需要 ProjectTo 的性能奖励,只需要拉动实际需要的列。