问题描述
var locations = await _context.Locations
.Include(x => x.LocationsOfTheUsers)
.Include(x => x.Address)
.ThenInclude(x => x.County)
.Where(CalculateFilters(searchObj))
.ToListAsync(cancellationToken);
每次LocationsOfTheUsers
为空时,我决定.Include(x => x.LocationsOfTheUsers)
并按预期收到结果,但是由于这样定义,我不确定为什么必须包含此集合:
public class Location
{
public string Title { get; set; }
public long? RegionId { get; set; }
public Region Region { get; set; }
public long? AddressId { get; set; }
public Address Address { get; set; }
public long? CountyId { get; set; }
public County County { get; set; }
public ICollection<LocationsOfTheUsers> LocationsOfTheUsers { get; set; }
}
我认为它会自动包含在内,因为它在ICollection
类中以Location
的形式存在。
那么为什么这里需要.Include()
上的LocationsOfTheUsers
?
谢谢大家
欢呼
解决方法
在实体框架中,非虚拟属性表示表的列,虚拟属性表示表之间的关系(一对多,多对多,...)
因此,您的财产应定义为:
public virtual ICollection<LocationsOfTheUsers> LocationsOfTheUsers { get; set; }
数据库查询的最慢部分之一是将所选数据从数据库管理系统传输到本地进程。因此,将所选数据限制为您实际打算使用的值是明智的。
如果您在学校与学生之间存在一对多的关系,并且您要求学校[10],那么您并不想自动获取其2000名学生。
即使您想让“学校[10]及其所有学生”都使用Include
来获取学生也不是很有效。每个学生将拥有一个值为[10]的外键SchoolId
。如果您使用Include
,则此外键将转移2000次。真是浪费!
在使用实体框架时,请始终使用
Select
来获取数据并仅选择您实际计划使用的属性。如果您打算更改包含的项目,请仅使用Include
。
这样,您可以将数据库表结构与实际查询分开。如果您的数据库结构发生了变化,则只有查询发生了变化,查询的用户不会注意到内部的变化。
除了具有更好的性能和更强的抵御更改能力外,您的代码阅读器还可以更轻松地查看其查询中的值。
当然不要使用Include
来节省您的键入时间。与以后键入include
而不是Select
最后:在流程的早期限制数据,因此将Where
放在前面。
因此您的查询应为:
var predicate = CalculateFilters(searchObj)
var queryLocations = dbContext.Locations
.Where(predicate)
.Select(location => new
{
// Select only the location properties that you plan to use
Id = location.Id,Name = location.Name,// Locations Of the users:
UserLocations = location.LocationsOfTheUsers
.Select(userLocation => new
{
// again: only the properties that you plan to use
Id = userLocation.Id,...
// Not needed,you already know the value
// LocationId = userLocation.LocationId
})
.ToList(),Address = new
{
Street = location.Address.Street,PostCode = location.Addrress.PostCode,...
County = location.Address.County.Name // if you only want one property
// or if you want more properties:
County = new
{
Name = location.Address.County.Name,Abbr = location.Address.Count.Abbr,...
}),},});
,
我认为这将被自动包括在内,因为它作为ICollection存在于Location类中。
好吧,由于性能原因,它不会自动包含在内,因为相关实体及其递归子实体的图形可能很深。
这就是为什么您使用eager loading使用Include
方法显式包括想要的相关实体的原因。
另一种选择是使用延迟加载,这意味着只要满足一些先决条件并且在此情况发生时上下文仍然存在,就可以在访问代码中的导航属性后立即加载相关实体。 >
有关更多信息,请参阅docs。
,我相信您正在使用 EntityFrameworkCore 。在EntityFramework(EF6)中,默认情况下启用了延迟加载,但是,在EntityFrameworkCore中,与延迟加载相关的实体由单独的程序包处理 Microsoft.EntityFrameworkCore.Proxies
。
要启用您要查找的行为,请安装上述软件包并添加以下代码
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
}
此后,无需进行Include
调用即可加载相关实体。