我有一個 Entity Core (.Net 6) 用戶組態檔,如下所示:
public class driver {
[Key]
public int Id { get; set;}
public List<CarRace>? RacesParticipatedIn { get; set; }
public List<CarRace>? RacesWon { get; set; }
public List<CarRace>? RacesLost { get; set; }
}
所以我有 3 個不同型別的 CarRace 串列。如何設定物體關系?這是一對多還是多對多?
我打算有一個司機表和一個單獨的表,其中包含 50 個 CarRaces 的串列。如果他們參加了比賽,我只想將比賽詳細資訊復制到車手名單中。
public class CarRace {
[Key]
public int Id {get; set;}
public string Title {get; set;}
public string Distance {get; set; }
public int PrizeMoney {get; set}
}
uj5u.com熱心網友回復:
考慮資料庫方面的問題,因為物體反映資料記錄。車手參加比賽,在某些比賽中他們可能會贏,而在其他比賽中他們不會。
從模式的角度來看,我可以設計類似的東西:
[Driver]
Id (PK)
Name
// Other driver related fields.
[Race]
Id (PK)
// Other race related fields.
[DriverRace]
DriverId (PK)
RaceId (PK)
IsWinner
然后從物體的角度:
public class Driver
{
[Key]
public int Id {get; set; }
public virtual ICollection<DriverRace> DriverRaces { get; set; } = new List<DriverRace>();
}
public class Race
{
[Key]
public int Id { get; set; }
public virtual ICollection<DriverRace> DriverRaces { get; set; } = new List<DriverRace>();
}
public class DriverRace
{
[Key, ForeignKey(nameof(Driver)), Column(Order=0)]
public int DriverId { get; set; }
[Key, ForeignKey(nameof(Race)), Column(Order=1)]
public int RaceId { get; set; }
public bool IsWinner { get; set; }
public virtual Driver Driver { get; set; }
public virtual Race Race { get; set; }
}
像這樣與比賽、贏家和輸家的關系在模式方面帶有警告。使用上面的模型,沒有辦法真正強制給定比賽的車手沒有超過一個 IsWinner 集,或者有任何 IsWinner 集。如果我們改為在 Race 表中選擇不可為空的 WinnerDriverId,或者這樣,就無法強制 WinnerDriverId 是該 Race 的 DriverRaces 表中的 DriverId。強制資料完整性意味著要么在資料更新時進行檢查(這可能導致先有雞和先有蛋的問題),或者定期運行健康檢查維護查詢以檢測無效資料情況并提醒您。
現在是識別贏家和輸家的話題。有幾個選項需要考慮。我們可以利用物體??中未映射的屬性,也可以利用投影。
未映射的屬性:
public class Driver
{
[Key]
public int Id {get; set; }
public virtual ICollection<DriverRace> DriverRaces { get; set; } = new List<DriverRace>();
[NotMapped]
public virtual ICollection<Race> WonRaces => DriverRaces.Where(dr => dr.IsWinner).Select(dr => dr.Race).ToList();
[NotMapped]
public virtual ICollection<Race> LostRaces => DriverRaces.Where(dr => !dr.IsWinner).Select(dr => dr.Race).ToList();
}
這種方法有一些非常重要的警告。
- 如果在加載驅動程式時沒有預先加載這些關系,那么當第一次訪問其中一個屬性時,這將觸發 DriverRaces 和 Race 的延遲加載。如果延遲加載被禁用,它們將無法作業。
- 您不能在最終轉換為 SQL 的 EF Linq 查詢中使用未映射的屬性。這樣做會導致 EF 無法決議 WonRaces 或 LostRaces 的錯誤。
例如,如果您想獲得在特定賽道上贏得比賽的車手。(假設 Race 有一個 TrackId 參考)這不合法:
var winningDrivers = Context.Drivers
.Where(d => d.WonRaces.Any(r => r.TrackId == trackId))
.ToList();
EF 將在使用 WonRaces 時引發例外,修復它的典型“hack”是:
var winningDrivers = Context.Drivers
.ToList();
.Where(d => d.WonRaces.Any(r => r.TrackId == trackId))
.ToList();
這在技術上可行,但在不斷增長的系統中會導致絕對糟糕的性能,因為這將加載所有驅動程式,然后在觸摸 WonRaces 時延遲加載它們的每個 DriverRaces 和 Races。
相反,您需要通過映射的 DriverRaces 參考:
var winningDrivers = Context.Drivers
.Where(d => d.DriverRaces.Any(dr => dr.Race.TrackId == trackId))
.ToList();
投影:更好的選擇是將物體視為更純粹的資料表示,并將轉換為更多基于視圖的事實進行投影。這可以導致更好的查詢,并減少使用上述方法出現的性能缺陷和錯誤。
如果我們有一個 Driver 的 ViewModel 和他們隨后的 Race 細節,我們可能會有類似的東西:
[Serializable]
public class DriverViewModel
{
public int Id { get; set; }
public ICollection<RaceViewModel> WonRaces { get; set; } = new List<RaceViewModel>();
public ICollection<RaceViewModel> LostRaces { get; set; } = new List<RaceViewModel>();
}
[Serializable]
public class RaceViewModel
{
public int Id { get; set; }
// Other details.
}
現在,當我們選擇要傳遞給視圖的驅動程式詳細資訊時:
var drivers = _context.Drivers
// can use .Where() clause...
.Select(d => new DriverViewModel
{
Id = d.Id,
WonRaces = d.DriverRaces
.Where(dr => dr.IsWinner)
.Select(dr => new RaceViewModel
{
Id = dr.Race.Id
}).ToList(),
LostRaces = d.DriverRaces
.Where(dr => !dr.IsWinner)
.Select(dr => new RaceViewModel
{
Id = dr.Race.Id
}).ToList()
).ToList();
使用 Automapper 執行投影可以大大簡化這一點,使用MapperConfiguration設定知道如何投影 Driver -> DriverViewModel 和 Race -> RaceViewModel。
// Mapper configuration can be centralized or created on demand.
var mapperConfig = new MapperConfiguration( cfg => {
cfg.CreateMap<Driver, DriverViewModel>()
.ForMember(x => x.WonRaces, opt => opt.MapFrom(src => src.DriverRaces
.Where(dr => dr.IsWinner)
.Select(dr => dr.Race)))
.ForMember(x => x.LostRaces, opt => opt.MapFrom(src => src.DriverRaces
.Where(dr => !dr.IsWinner)
.Select(dr => dr.Race)));
cfg.CreateMap<Race, RaceViewModel>();
});
var drivers = _context.Drivers
// can use .Where() clause...
.ProjectTo<DriverViewModel>(mapperConfig)
.ToList();
如果物體和視圖模型之間的屬性命名約定是一致的,Automapper 可以確定如何映射大多數屬性。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/507011.html
