渴望加载包括无包含表的无尽自我联接表

时间:2019-05-30 09:34:31

标签: c# entity-framework linq eager-loading self-join

当我尝试选择某些项目时,尽管我没有将它们的对象包含在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; }
}

我希望结果包含本能,但我的方法返回本市和负责任的本能。并且它以递归方式继续。

1 个答案:

答案 0 :(得分:1)

人们倾向于使用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的自由:您的用户不会知道其中的区别:如果您要编写单元测试,那就太好了。

尽管您选择了分离用于保留数据的方法,但是您选择了公开数据库布局,包括对外部世界的外键。这使得将来更改数据库更加困难:用户也必须更改。

考虑为Institution和City编写存储库类,它们仅公开持久性用户真正需要的那些属性。如果人们仅查询“具有所在城市的某些属性的机构的某些属性”,或者反过来询问“具有位于这些城市中的机构的若干属性的城市的某些属性”,那么他们将不需要外键。

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

例如:假设您添加了删除机构的可能性,但是您不想立即删除有关该机构的所有信息,例如因为这样可以在有人不小心删除该机构的情况下进行恢复,因此您可以添加一个可为空的属性ObsoleteDate

查询机构的最讨厌的人,不想过时的机构。如果您有一个中间存储库机构类,则省略了ObsoleteDate,并且所有查询都删除了所有具有非零Institutions的{​​{1}},对于您的用户来说,就像一个过时的机构将从数据库中删除。

只有一个用户需要访问ObsoleteData:这是一项更清洁的任务,该任务会不时地删除相当长一段时间内所有过时的ObsoleteDate

中间存储库类的第三项改进是您可以通过不同的界面为不同的用户提供对同一数据的访问权限:有些用户只能查询有关机构的信息,有些用户还可以更改某些数据,而另一些则可以。允许更改其他数据。如果您给他们提供一个界面,他们可以通过将其放回原始机构来破坏此功能。

使用单独的存储库类,您可以为每个用户提供自己的数据,仅此数据即可。

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