
[英]EF Core: how to load releated data with only having a FK of the related data
[英]EF core load data from child object using FK
我有一個 class 的代理
public class Agent
{
public string Id { get; set; }
public string Name { get; set; }
public List<AgentUser> Users { get; set; }
}
public class User
{
public string Id { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class AgentUser
{
public string Id { get; set; }
public string AgentId { get; set; }
public Agent Agent { get; set; }
public string UserId { get; set; }
public User User { get; set; }
}
關系是一對多。 在 AgentUsers 表上,它只包含 UserId。 我想知道在調用 Agents 表時如何加載用戶詳細信息(電子郵件、名字、姓氏)。
這是我的查詢,但它只包含 UserId。
var result= await _context.Agents
.Include(au => au.Users)
.Where(x => x.Id.Equals(request.Id))
.AsNoTracking()
.FirstOrDefaultAsync();
是的,我可以進行 JOIN,但是如何僅使用 FK 以最有效的方式從 User 表中獲取詳細信息? 謝謝!
如果您想像您的示例一樣在關系中使用連接實體,那么讀取數據時的另一個選項是使用投影而不是加載整個實體圖:
var result= await _context.Agents
.Where(a => a.Id.Equals(request.Id))
.Select(a => new AgentSummaryViewModel
{
AgentId = a.Id,
// other details from agent...
Users = a.Users.Select(u => new UserSummaryViewModel
{
UserId = u.User.Id,
Email = u.User.EMail,
// ...
}).ToList()
})
.SingleOrDefaultAsync();
一旦映射器配置了如何構建代理和用戶 ViewModel,就可以使用 Automapper 及其針對 EF IQueryable
的ProjectTo()
方法進行簡化。 投影具有額外的優勢,即從實體圖中加載與您的消費者(視圖等)需求一樣多的數據,構建高效的查詢。 由於實體本身沒有被加載,因此無需急切加載相關實體或擔心跟蹤引用。
如果您想加載該代理及其用戶以進行編輯,則:
var agent = await _context.Agents
.Include(a => a.Users)
.ThenInclude(u => u.User)
.Where(a => a.Id.Equals(request.Id))
.SingleOrDefaultAsync();
或者,對於多對多關系,如果連接表可以僅用兩個 FK 表示為復合 PK,則 EF 可以按約定 map 此表,而無需將 AgentUser 聲明為實體。 如果您想要記錄和訪問有關該關系的其他詳細信息,您只需要定義 AgentUser。 因此,例如,如果您想使用帶有 IsActive 標志等的軟刪除系統,並且刪除代理和用戶之間的關聯導致該記錄變為非活動而不是刪除,您將需要一個 AgentUser 實體。
因此,如果您可以將 AgentUser 表簡化為:
AgentId [PK, FK(Agents)]
UserId [PK, FK(Users)]
...然后您的實體 model 可以簡化為:
public class Agent
{
public string Id { get; set; }
public string Name { get; set; }
public List<User> Users { get; set; }
}
實體映射需要顯式添加,例如使用 OnModelCreating 或通過注冊的 IEntityTypeConfiguration 實現,以便:
modelBuilder.Entity<Agent>()
.HasMany(x => x.Users)
.WithMany(x => Agents);
不幸的是,對於 EF Core,以這種方式映射的多對多關系需要雙方都有導航屬性。 (代理有用戶,用戶有代理)
EF 應該能夠按照慣例計算出鏈接表,但是如果您想更好地控制表名等,您可以明確告訴它要使用什么。 這是它可能會變得有點棘手的地方,但您可以通過定義一個連接實體來完成它,或者沒有一個,使用“影子”實體。 (本質上是一本字典)
modelBuilder.Entity<Agent>()
.HasMany(x => x.Users)
.WithMany(x => Agents);
.UsingEntity<Dictionary<string, object>>(
"AgentUsers",
l => l.HasOne<Agent>().WithMany().HasForeignKey("AgentId"),
r => r.HasOne<User>().WithMany().HasForeignKey("UserId"),
j =>
{
j.HasKey("AgentId", "UserId");
j.ToTable("AgentUsers"); // Or "UserAgents", or "AgentUserLinks" etc.
});
編輯:一個使用 Automapper 的簡單示例...
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Agent, AgentSummaryViewModel>();
cfg.CreateMap<User, UserSummaryViewModel>();
});
var result= await _context.Agents
.Where(a => a.Id.Equals(request.Id))
.ProjectTo<AgentSummaryViewModel>()
.SingleOrDefaultAsync();
上面的“config”可以從具有所有映射器配置的全局映射器設置中提取,根據需要臨時創建,或者我通常做的是從視圖模型本身中構建為 static 方法的映射配置表達式派生:
var config = new MapperConfiguration(AgentSummaryViewModel.BuildMapperConfig());
...然后在 AgentSummaryViewModel 中:
public static MapperConfigurationExpression BuildConfig(MapperConfigurationExpression config = null)
{
if (config == null)
config = new MapperConfigurationExpression();
config = UserSummaryViewModel.BuildMapperConfig(config);
config.CreateMap<Agent, AgentSummaryViewModel>()
.ForMember(x => x.Users, opt => opt.MapFrom(src => src.Users.Select(u => u.User));
// append any other custom mappings such as renames or flatting from related entities, etc.
return config;
}
UserSummaryViewModel 將具有類似的 BuildMapperConfig() 方法來設置將用戶轉換為 UserSummaryViewModel。 生成的映射器配置表達式可以根據需要鏈接盡可能多的相關視圖模型,位於它們各自的視圖模型下。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.