在将Linq用于实体时,有关使用Include()和Load()来获取相关表信息的问题很多。我对这个问题有不同的看法。
我的情况:
我有一个表,其中包含系统中每个用户的许多时间记录,并且我使用存储库模式和泛型进行开发,因此我的所有实体都有一个用于标准方法调用的接口。我关闭了延迟加载,我自己加载所有数据。因此,存储库中用于从具有相关表的表中加载所有记录的代码如下所示:
public class Repository<T> : IRepository<T> where T : class
{
protected readonly ApplicationDbContext Context;
public Repository(IConnectionHelper connection)
{
Context = connection.Context;
}
public virtual DbSet<T> ObjectSet
{
get { return Context.Set<T>(); }
}
public List<T> GetAll(String[] include, Expression<Func<T, bool>> predicate)
{
DbQuery<T> outQuery = null;
foreach (String s in include)
{
outQuery = ObjectSet.Include(s);
outQuery.Load();
}
return outQuery.Where(predicate).ToList();
}
}
对方法的调用如下:
string[] includes = { "User.UserProfile", "CampaignTimeClocks.CampaignRole.Campaign", "Site", "Type" };
DateTime uTcCurrent = GetUtc();
DateTime MinClockinDate = uTcCurrent.AddHours(-10);
List<TimeClock> tcPending = _timeClock.GetAll(includes, x => (x.PendingReview || x.ClockInDate < MinClockinDate && x.ClockOutDate == null) && (x.Site.Id == currentUser.SiteId));
当此方法运行并加载第一个User.Profile表时,它会加载所有时钟记录并将它们与所有用户相关联,这需要花费一分钟,这太长了,因为结束记录count只有185条记录,但是查询的初始加载运行了27,000 * 560个用户,或者1500万条记录,而且随着时间的推移,这只会变得更糟。
问题是我如何在没有这种负载开销的情况下做到这一点,我知道我可以链接包含,但由于包含的数量将根据我所做的数据和所调用的数据而改变,我不能简单地硬编码包含链。
我也尝试过:
List<TimeClock> testLst = _timeClock.GetAll(x => x.PendingReview ||
(x.ClockInDate < MinClockinDate && x.ClockOutDate == null))
.Select(x => new TimeClock{Id = x.Id,
ClockInDate = x.ClockInDate,
ClockOutDate = x.ClockOutDate,
TotalClockTime = x.TotalClockTime,
Notes = x.Notes,
PendingReview = x.PendingReview,
Type = x.Type,
User = x.User,
CampaignTimeClocks = x.CampaignTimeClocks,
TimeClockAdjustments = x.TimeClockAdjustments,
Site = x.User.Site}).ToList();
这将为我提供User.Profile信息,但Site和Type属性为null。
所以我对如何加载我需要的数据感到有点迷茫。
非常感谢所有帮助。
答案 0 :(得分:0)
你能先获得初始名单
吗?List<TimeClock> testLst = _timeClock.Where(x => x.PendingReview || (x.ClockInDate < MinClockinDate && x.ClockOutDate == null)).ToList();
然后调用一个以T为参数的修改后的GetAll()
?
答案 1 :(得分:0)
您执行的每个包含都将最终在数据库中执行连接。 假设您的左表是非常大的1024字节的记录大小,并且您有许多细节,比如1000,并且详细记录大小只有100。 这将导致左表的信息重复1000次,这些信息将由db放在线上,EF必须过滤掉重复的内容以创建左实例。
最好不要使用include并进行显式加载。基本上在同一个上下文中执行2个查询。
我有一个这样的例子,与你的不同,但我希望你能得到这个想法。它可以比依赖包含快10倍。 (数据库只能有效地处理有限数量的连接)
var adressen = adresRepository
.Query(r => r.RelatieId == relatieId)
.Include(i => i.AdresType)
.Select().ToList();
var adresids = (from a in adressen select a.AdresId).ToList();
IRepositoryAsync<Comm> commRepository = unitOfWork.RepositoryAsync<Comm>();
var comms = commRepository
.Query(c => adresids.Contains(c.AdresId))
.Include(i => i.CommType)
.Select();
对于commType和adresType,我使用include,因为存在1对1的关系,我避免了太多的连接,因此我的多个查询将比使用include的单个查询更快。我没有在第一个查询中包含Comms来尝试避免第二个查询,重点是在这种情况下2个查询比单个查询更快。
请注意,我的代码是使用我自己的存储库构建的,因此这段代码对您不起作用,但您可以了解这一点。
答案 2 :(得分:0)
我发现在不使用Load()语句的情况下更有效地执行此操作的方法是将DBQuery更改为IQueryable并链接包含,并返回执行的查询结果,并一起删除DBQuery.Load() 。这将查询的执行时间从秒改为毫秒。
public List<T> GetAll(String[] include)
{
IQueryable<T> outQuery = ObjectSet;
foreach (String s in include)
{
outQuery = outQuery.Include(s);
}
return outQuery.ToList();
}