繁体   English   中英

实体框架6:虚拟集合懒加载甚至在查询中显式加载

[英]Entity Framework 6: virtual collections lazy loaded even explicitly loaded on a query

尝试优化查询时,EF6出现问题。 考虑此类与一个集合:

public class Client
{
    ... a lot of properties
    public virtual List<Country> Countries { get; set; }
}

如您所知,在EF尝试获取每个客户的所有国家/地区时,使用延迟加载会遇到n + 1个问题。

我尝试使用Linq投影; 例如:

        return _dbContext.Clients
            .Select(client => new
            {
                client,
                client.Countries
            }).ToList().Select(data =>
            {
                data.client.Countries = data.Countries; // Here is the problem
                return data.client;
            }).ToList();

在这里,我使用了两个选择:第一个用于Linq投影,以便EF可以创建SQL,第二个将结果映射到Client类。 这样做的原因是因为我使用的是存储库接口,该接口返回List<Client>

尽管查询是在其中包含国家/地区的情况下生成的,但是当我尝试呈现整个信息时,EF仍在使用延迟加载(相同的n + 1问题)。 避免这种情况的唯一方法是删除虚拟访问器:

public class Client
{
    ... a lot of properties
    public List<Country> Countries { get; set; } 
}

该解决方案的问题在于,我们仍然希望将此属性设置为虚拟。 此优化仅对于应用程序的特定部分是必需的,而在其他部分,我们希望具有此延迟加载功能。

我不知道如何将此属性“通知” EF,该属性已通过此Linq投影进行了延迟加载。 那可能吗? 如果没有,我们还有其他选择吗? n + 1问题使应用程序花费几秒钟来加载1000行。

编辑

感谢您的答复。 我知道我可以使用Include()扩展名来获取集合,但是我的问题是我需要添加一些其他优化功能(对不起,我没有发布完整的示例,我认为使用集合问题就足够了):

public class Client
{
    ... a lot of properties
    public virtual List<Country> Countries { get; set; }
    public virtual List<Action> Actions { get; set; }
    public virtual List<Investment> Investments { get; set; }
    public User LastUpdatedBy {
        get {
          if(Actions != null) {
              return Actions.Last();
          }       
        }
    }
}

如果需要渲染客户端,则需要使用Include()来获取有关最近更新和投资数量( Count() )的信息,实际上我需要从数据库中获取所有信息。 但是,如果我使用像

        return _dbContext.Clients
              .Select(client => new
              {
                    client,
                    client.Countries,
                    NumberOfInvestments = client.Investments.Count() // this is translated to an SQL query
                    LastUpdatedBy = client.Audits.OrderByDescending(m => m.Id).FirstOrDefault(),
              }).ToList().Select(data =>
              {
                    // here I map back the data
                    return data.client;
              }).ToList();

我可以减少查询,只获取所需的信息( 在LastUpdatedBy的情况下,我需要将属性更改为getter / setter,这不是什么大问题,因为它仅用于应用程序的此特定部分 )。

如果我将Select()与这种方法(投影然后是映射)一起使用,则EF不考虑Include()部分。

不知道要做什么,您使用的是lambda表达式而不是linq,而第二个选择是不必要的。

data.clientclientdata.Countriesclient.Countries ,所以data.client.Countries = data.Countries送花儿给人真实的。

如果您不希望延迟加载Countries ,请使用_dbContext.Clients.Include("Countries").Where() or select ()

为了强制渴望加载虚拟属性,您应该使用Include扩展方法。 这是MSDN https://msdn.microsoft.com/zh-cn/library/jj574232(v=vs.113).aspx的链接。

所以这样的事情应该工作:

 return _dbContext.Clients.Include(c=>c.Countries).ToList();

如果我理解正确,可以尝试一下

_dbContext.LazyLoading = false;

var clientWithCountres =  _dbContext.Clients
                                    .Include(c=>c.Countries)
                                    .ToList();

这将获取Client ,只包括它Countries 如果禁用延迟加载,则不会从查询中加载其他集合。 除非您指定包含或投影。

仅供参考:Projection和Include()不能一起使用,请参见以下答案:如果您正在投影,它将绕过include。 https://stackoverflow.com/a/7168225/1876572

我不是100%肯定,但是我认为您的问题是,您仍将维护内部查询的可查询性,直到查询结束。

这个可查询的是惰性的(因为在模型中它是惰性的),并且您没有做任何解释来说明情况并非如此,您只是将相同的惰性可查询项投影到结果集中。

我无法直截了当地告诉您什么是正确的答案,但是我会尝试以下方法:

1也使用内部可投影的投影,例如

return _dbContext.Clients
            .Select(client => new
            {
                client,
                Countries = client.Countries.Select(c=>c)// or a new Country
            })

2将include放在查询的末尾(我很确定include应用于结果而不是输入。如果将其放在投影之前,它肯定不起作用)例如:

_dbContext.Clients
            .Select(client => new
            {
                client,
                client.Countries
            }.Include(c=>c.Countries)`

3尝试在投影内部指定枚举,例如:

_dbContext.Clients
            .Select(client => new
            {
                client,
                Countries = client.Countries.AsEnumerable() //perhaps tolist if it works
            }`

我想通过说我还没有尝试以上任何方法来避免这种情况,但是我认为这将使您走上正确的道路。

关于延迟加载的注意事项

IMO很少有延迟加载的好用例。 除非您的用户直接在模型上遵循惰性路径,否则它几乎总是会导致生成太多查询。 仅在极其谨慎的情况下使用它,而在请求响应(例如Web)应用程序中则完全不使用IMO。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM