首页 > 解决方案 > 急切加载包括没有包含表的无限自连接表

问题描述

当我尝试选择某些项目时,尽管我没有将它们的对象包含在 linq 中,但它们仍带有它们的包含项

public List<Institution> GetListWithCities(Expression<Func<Institution,bool>> filter = null)
{

   using (var context = new DbContext())
   {
    return filter == null 
           ? context.Set<Institution>()
                    .Include(x => x.City)
                    .ToList() 
           : context.Set<Institution>()
                    .Include(x => x.City)
                    .Where(filter)
                    .ToList();
  }
}

[Table("Institution")]
 public class Institution{
    public int ID;
    public string Name;
    public int CITY_ID;
    public int RESPONSIBLE_INSTUTION_ID;
    public virtual City City{ get; set; }
    public virtual Institution ResponsibleInstution{ get; set; }
}

我希望结果包括 instution 城市,但我的方法返回城市和负责任的 instution。并且它递归地继续。

标签: c#entity-frameworklinqeager-loadingself-join

解决方案


人们倾向于使用Include而不是Select在他们不打算使用Include提供的功能时使用,但仍然在浪费Include使用的处理能力。

在实体框架中总是使用 Select 来获取一些数据。如果您计划更新包含的项目,则仅用户包含。

数据库查询中较慢的部分之一是将获取的数据从数据库管理系统传输到本地进程。因此,明智的做法是仅选择您真正计划使用的那些属性。

显然你Institution的正好是一个City,即City外键(CityId?)所指的那个。如果Institution[10] 位于City[15] 中,则Institution.CityId值为 15,等于City.Id。所以你要转移这个值两次。

using (var dbContext = new MyDbContext())
{
    IQueryable<Institution> filteredInstitutions = (filter == null) ?
        dbContext.Institutions :
        dbContext.Institutions.Where(filter);
    return filteredInstitutions.Select(institution => new Institution
    {
        // Select only the Institution properties that you actually plan to use:
        Id = institution.Id,
        Name = institution.Name,

        City = new City
        {
            Id = institution.City.Id,
            Name = institution.City.Name,
            ...
        }

        // not needed: you already know the value:
        // CityId = institution.City.Id,
});

可能的改进 显然你选择在实体框架和你的函数的用户之间添加一个层:虽然他们使用你的函数,但他们并不需要知道你使用实体框架来访问数据库。这使您可以自由地使用 SQL 而不是实体框架。该死,它甚至让您可以自由地摆脱数据库并使用 XML 文件而不是 DBMS:您的用户不会知道其中的区别:如果您想编写单元测试,那就太好了。

尽管您选择分离用于持久化数据的方法,但您选择向外界公开您的数据库布局,包括外键。这使得将来更改数据库变得更加困难:您的用户也必须更改。

考虑为机构和城市编写存储库类,只公开持久性用户真正需要的那些属性。如果人们只查询“一些机构的属性和他们所在城市的一些属性”,或者反过来“几个城市的属性和位于这些城市的机构的几个属性”,那么他们就不需要外键。

中间存储库类使您可以更自由地更改数据库。除此之外,它还可以让您自由地为某些用户隐藏某些属性。

例如:假设您添加了删除机构的可能性,但您不想立即删除有关该机构的所有信息,例如因为这允许您在有人意外删除该机构时恢复,您可以添加一个可为空的属性ObsoleteDate

大多数查询机构的人不想要过时的机构。如果您有一个中间存储库机构类,其中您省略了ObsoleteDate,并且所有查询都删除了所有Institutions非零的ObsoleteData,那么对于您的用户来说,就好像一个过时的机构将从数据库中删除。

只有一个用户需要访问ObsoleteDate: 一项更清洁的任务,该任务时不时地删除所有Institutions在相当长一段时间内已经过时的内容。

中间存储库类的第三个改进是您可以让不同的用户访问相同的数据,使用不同的界面:一些用户只能查询有关机构的信息,一些用户还可以更改某些数据,而其他用户可以更改其他数据。如果你给他们一个接口,他们可以通过将他们放回原来的机构来打破这一点。

使用单独的存储库类,您将有可能为每个用户提供他们自己的数据,仅此数据。

存储库模式的缺点是您必须考虑不同的用户,并创建不同的查询功能。优点是存储库更容易更改和测试,因此更容易在未来更改后保持所有错误。


推荐阅读