繁体   English   中英

属性具有相同代码时,使AutoMapper ProjectTo()保持干燥

[英]Keeping AutoMapper ProjectTo() DRY When Properties Have the Same Code

我想使用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方法的代码。 是否有解决此问题并使代码保持DRY的已建立模式? 我们的模型中有很多自定义属性和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的性能红利,只能拉出实际需要的列。

@LucianBargaoanu提供了一个不错的可行解决方案的正确钥匙。 AutoMapper.EF6提供了一些简单的包装器来完成此操作; 他们包装的实际上是完成实际工作的DelegateDecompiler软件包。 最终的解决方案如下:

首先,通过将NuGet包添加到项目中来引用DelegateDecompiler。

其次,将ComputedAttribute添加到您希望EFCore能够正确处理转入SQL的模型上的任何计算属性或函数中,例如:

class Claim {
  [Computed]
  public string Name
    => $"{FirstName} {LastName}";
}

class ClaimType {
  [Computed]
  public override string ToString()
    => Value + (Abbrev != null ? $" ({Abbrev})" : "");
}

最后,您要调用ProjectTo任何地方,还要在其ProjectTo附加Decompile() (或DecompileAsync() )。 例如

var myClaimViewModels = Db.Claims.ProjectTo<ClaimViewModel>(Mapper.ConfigurationProvider).Decompile().ToList();

我验证了上面的代码,即使使用标记为Computed的重写ToString方法也能正常工作。 我检查了SQL Server Profiler,以确保查询仅拉回必要的列,所幸的是,确实如此。

作为参考,AutoMapper.EF6库实际上只有一个源文件 ,并且可以轻松地将其复制并粘贴到自己的源中,以EF Core为目标,而不是EF 6。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM