简体   繁体   English

Linq 连接不包括实体框架中子表的数据

[英]Linq join does not include data from child table in Entity Framework

I am trying to join few tables and return all data from all tables in the list.我正在尝试加入几个表并返回列表中所有表的所有数据。

Here is the query I tried这是我尝试过的查询

List<TblCrrequests> mycr = (from ep in _context.TblCrrequests
                            join e in _context.TblCrExternalBudget on ep.CrId equals e.CrId
                            join i in _context.TblCrInternalbudget on ep.CrId equals i.CrId
                            join t in _context.TblExternalBudgetTypes on e.TypeId equals t.TypeId
                            where ep.ProjectCode == pcode
                            select ep)
                            .OrderByDescending(d => d.RequestedOn)
                            .Take(8).ToList<TblCrrequests>();

But on executing in this list variable, it only contain data for the main table only.但是在这个列表变量中执行时,它只包含主表的数据。 Showing the child table array as empty.将子表数组显示为空。 How to modify the query to get all those details from the main and child tables as well into the list variable.如何修改查询以从主表和子表以及列表变量中获取所有这些详细信息。 I am very new to linq..我对 linq 很陌生。

I tried select ep,e,i,t which didn't work.我试过 select ep,e,i,t 没用。 Also if I don't need all columns from one table, is that possible?另外,如果我不需要一张表中的所有列,这可能吗?

Here is my class defenitions这是我的 class 防御

public partial class TblCrrequests
{
        public TblCrrequests()
        {
            TblCrExternalBudget = new HashSet<TblCrExternalBudget>();
        }

        public int CrId { get; set; }
        public string ProjectCode { get; set; }
       
        public virtual ICollection<TblCrExternalBudget> TblCrExternalBudget { get; set; }
}

public partial class TblCrExternalBudget
{
        public int? CrId { get; set; }
        public int? TypeId { get; set; }

        public virtual TblCrrequests Cr { get; set; }
        public virtual TblExternalBudgetTypes Type { get; set; }
}

public partial class TblExternalBudgetTypes
{
        public TblExternalBudgetTypes()
        {
            TblCrExternalBudget = new HashSet<TblCrExternalBudget>();
        }

        public int TypeId { get; set; }
        public string TypeName { get; set; }

        public virtual ICollection<TblCrExternalBudget> TblCrExternalBudget { get; set; }
}

Use Include() method on a related property you would like to load too.在您也想加载的相关属性上使用Include()方法。 Something like this:像这样的东西:

// in this case its assuming that 
var books = (from b in db.books.Include(p => p.Authors)
                    where <your filter criteria>
                    select b).ToList();

The Include() method instructs it to load book's Authors with it as well. Include()方法指示它也加载书籍的作者。

Alas, you forgot to tell us the classes that you have.唉,你忘了告诉我们你有什么课程。 You also didn't inform us about the relations between the tables.你也没有告诉我们表之间的关系。 I'll have to guess from your usage.我不得不从你的用法中猜测。 Next question you ask consider to provide this information.您提出的下一个问题考虑提供此信息。

It seems that you have a table with Requests , and a table InternalBudgets .似乎您有一个带有Requests的表格和一个表格InternalBudgets There seems to be a one-to-many relation between Requests and InternalBudgets : every Request has zero or more InternalBudgets , every InternalBudget belongs to exactly one Request , namely the one that the foreign key refers to. RequestsInternalBudgets之间似乎存在一对多的关系:每个Request都有零个或多个InternalBudgets ,每个InternalBudget恰好属于一个Request ,即外键所指的那个。

There seems to be a similar one-to-many between Requests and ExternalBudgets . RequestsExternalBudgets之间似乎存在类似的一对多。

Finally there is also a one-to-many between ExternalBudgets and ExternalBudgetTypes .最后, ExternalBudgetsExternalBudgetTypes之间也存在一对多的关系。 Personally I would expect a many-to-many relation: every ExternalBudgetType is used by zero or more ExternalBudgets .就我个人而言,我希望有一个多对多的关系:每个ExternalBudgetType都被零个或多个ExternalBudgets使用。 This doesn't change the answer drastically.这不会彻底改变答案。

If you've followed the entity framework conventions , you'll have classes similar to the following:如果您遵循实体框架约定,您将拥有类似于以下的类:

class Request
{
    public int Id {get; set;}
    ...

    // Every Request has zero or more InternalBudgets (one-to-many)
    public virtual ICollection<InternalBudget> InternalBudgets {get; set;}

    // Every Request has zero or more ExternalBudgets (one-to-many)
    public virtual ICollection<ExternalBudged> ExternalBudgets {get; set;}
}

The Internal and External budgets:内部和外部预算:

class InternalBudget
{
    public int Id {get; set;}
    ...

    // Every InternalBudget belongs to exactly one Request, using foreign key
    public int RequestId {get; set;}
    public virtual Request Request {get; set;}
}

class ExternalBudget
{
    public int Id {get; set;}
    ...

    // Every ExternalBudget belongs to exactly one Request, using foreign key
    public int RequestId {get; set;}
    public virtual Request Request {get; set;}

    // Every ExternalBudget has zero or more ExternalBudgetTypess (one-to-many)
    public virtual ICollection<ExternalBudgetType> ExternalBudgetTypes {get; set;}

}

Finally the ExternalBudgetTypes :最后是ExternalBudgetTypes

class ExternalBudgetType
{
    public int Id {get; set;}
    ...

    // Every ExternalBudgetType belongs to exactly one ExternalBudget, using foreign key
    public int ExternalBudgetId{get; set;}
    public virtual ExternalBudget ExternalBudget {get; set;}
}

Because you stuck to the conventions, this is all that entity framework needs to detect your tables, the relations between the tables and the primary and foreign keys因为您遵守约定,所以这就是实体框架检测您的表、表之间的关系以及主键和外键所需的全部内容

In entity framework the columns of the tables are represented by the non-virtual properties.在实体框架中,表的列由非虚拟属性表示。 The virtual properties represent the relations between the tables (one-to-many, many-to-many)虚拟属性表示表之间的关系(一对多、多对多)

The foreign key is a column in the table.外键是表中的一列。 Hence it is non-virtual.因此它是非虚拟的。 The object that the foreign key refers to is not part of the table, hence if is virtual.外键引用的 object 不是表的一部分,因此 if 是虚拟的。

The "Many" side in xxx-to-many relations should be implemented with ICollection<...> , not with IList<...> . xxx 对多关系中的“多”端应使用ICollection<...>实现,而不是IList<...> Ilist provide functionality that is undefined in databases: Ilist 提供了数据库中未定义的功能:

Requests fetchedRequest = ...
InternalBudget internalBudget = fetchedRequest[3];

Can you say which internalBudget has list index [3]?你能说哪个 internalBudget 有列表索引 [3] 吗? Better not provide functionality that is not defined.最好不要提供未定义的功能。 ICollection<...> give you the possibility to Add / Delete / Count , all functionalities that have a defined meaning in a database. ICollection<...>使您可以Add / Delete / Count ,所有功能在数据库中都有定义的含义。

One final tip: in your identifiers use plural nouns to refer to sequences;最后一个提示:在你的标识符中使用复数名词来指代序列; use singular nouns to refer to elements in these sequences.使用单数名词来指代这些序列中的元素。 That makes LINQ statements easier to understand.这使得 LINQ 语句更易于理解。

Back to your question回到你的问题

Requirement Given a projectCode, give me the requests that have this projectCode, with several properties of their Internal and External Budgets要求给定一个项目代码,给我具有此项目代码的请求,以及它们的内部和外部预算的几个属性

Easy method: use the virtual ICollections简单方法:使用虚拟 ICollections

var projectCode = ...
var result = dbContext.Requests

    // Keep only the Requests that have projectCode
    .Where (request => request.ProjectCode == projectCode)

    // order the remaining Requests by descending RequestedOn date
    .OrderByDescending(request => request.RequestedOn)

    // Select the properties that you want:
    .Select(request => new
    {
        // Select only the Request properties that you plan to use
        Id = request.Id,
        Name = request.Name,
        ...

        // Internal budgets of this Request
        InternalBudgets = request.InternalBudgets.Select(budget => new
        {
             // Again, only the properties that you plan to use
             Id = budget.Id,
             ...

             // not Needed, you know the value
             // RequestId = budget.RequestId,
        })
        .ToList(),

        // External budgets of this Request
        ExternalBudgets = request.ExternalBudgets.Select(budget => new
        {
             ...

             ExternalBudgetTypes = budget.ExternalBudgetTypes
                 .Select(budgetType => ...)
                 .ToList(),
        })
        .ToList(),
    });

In words: from all Requests, keep only those Request that have a value of property ProjectCode equal to projectCode.换句话说:从所有请求中,仅保留属性 ProjectCode 的值等于 projectCode 的那些请求。 Order the remaining Requests by descending value of property RequestedOn.按属性 RequestedOn 的降序排列剩余的请求。 Finally Select several properties of the Request, its internal budgets and its external budgets.最后 Select Request 的几个属性,它的内部预算和它的外部预算。

Entity framework knows the relations between the tables, and knows how to translate the use of your virtual properties in the correct (Group-)join.实体框架知道表之间的关系,并且知道如何在正确的 (Group-)join 中转换使用您的虚拟属性。

Note: the result differs slightly from your solution.注意:结果与您的解决方案略有不同。 Your solution would give:您的解决方案将给出:

Request InternalBudget (for simplicity: leave out Externalbudget / type
  01         10
  02         12
  04         11
  01         14
  01         15
  02         13

My Solution gives:我的解决方案给出:

  • Request 01 with its InternalBudgets {10, 14, 15}请求 01 及其 InternalBudgets {10, 14, 15}
  • Request 02 with its InternalBudgets {12, 13}请求 02 及其 InternalBudgets {12, 13}
  • Request 05 without any InternalBudgets没有任何 InternalBudgets 的请求 05
  • Request 04 with its InternalBudget {11}请求 04 及其 InternalBudget {11}

IMHO this is much more intuitive and more efficient: the properties of each Request items are transferred only once.恕我直言,这更加直观和高效:每个请求项目的属性仅传输一次。

Note that you will also get the Requests that have no Internal/External budgets or types.请注意,您还将获得没有内部/外部预算或类型的请求。 If you don't want them, use a Where(... =>...Count != 0) to filter them out.如果您不想要它们,请使用Where(... =>...Count != 0)将它们过滤掉。

If you don't want "Items with their Sub-items" (which is in fact a GroupJoin), but a standard join, use SelectMany to flatten the result如果您不想要“项目及其子项目”(实际上是 GroupJoin),而是标准联接,请使用SelectMany展平结果

Do the Join yourself自己加入

Some people don't like to use the virtual ICollection<...> .有些人不喜欢使用virtual ICollection<...> They prefer to do the (Group-)Joins themselves.他们更喜欢自己做(Group-)Join。 In method syntax Joins with more than two tables look horrible, luckily you don't have to do a Join.在方法语法中,多于两个表的联接看起来很糟糕,幸运的是您不必进行联接。

var result = dbContext.Requests
    .Where (request => request.ProjectCode == projectCode)
    .OrderByDescending(request => request.RequestedOn)
    .Select(request => new
    {
        // Request properties:
        Id = request.Id,
        Name = request.Name,
        ...

        // Internal budgets:
        InternalBudgets = dbContext.InternalBudgets
            .Where(budget => budget.RequestId == request.Id)
            .Select(budget => new
            {
                 Id = budget.Id,
                 ...
            })
            .ToList(),

        // External budgets:
        ExternalBudgets = dbContext.ExternalBudgets
            .Where(budget => budget.RequestId == request.Id)
            .Select(budget => new
            {
                 Id = budget.Id,
                 ...

                 BudgetTypes = dbContext.BudgetTypes
                     .Where(budgetType => budgetType.ExternalBudgetId == budget.Id)
                     .Select(budgetType => ...)
                     .ToList(),
            })
            .ToList(),
      });

I'm not sure if you can convince your project leader that this method is better readable, more reusable, easier to test and change than the other one with the virtual ICollections.我不确定您是否可以说服您的项目负责人相信这种方法比使用虚拟 ICollections 的另一种方法更易读、更可重用、更容易测试和更改。

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

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