[英]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.