代码改变世界

Entity framework and many to many queries unusable?

  AnyKoro  阅读(427)  评论(0编辑  收藏  举报
2 vote down star
3

I'm trying EF out and I do a lot of filtering based on many to many relationships. For instance I have persons, locations and a personlocation table to link the two. I also have a role and personrole table.

EDIT: Tables:

 

 

Person (personid, name)

 

 

Personlocation (personid, locationid)

 

 

Location (locationid, description)

 

 

Personrole (personid, roleid)

 

 

Role (roleid, description)

EF will give me persons, roles and location entities. EDIT: Since EF will NOT generate the personlocation and personrole entity types, they cannot be used in the query.

How do I create a query to give me all the persons of a given location with a given role?

In SQL the query would be

select p.*
from persons as p
join personlocations as pl on p.personid=pl.personid
join locations       as l  on pl.locationid=l.locationid
join personroles     as pr on p.personid=pr.personid
join roles           as r  on pr.roleid=r.roleid
where r.description='Student' and l.description='Amsterdam'

I've looked, but I can't seem to find a simple solution.

flag


If you want a good answer to this question, you should include the entity types. After all, that, not the tables is what the Entity SQL/LINQ to Entities is going to refer to. Otherwise, I have to guess at your property names and cardinality. – Craig Stuntz Feb 16 '09 at 17:57

4 Answers

vote up 2 vote down

In Lambda :

    var persons = Persons.Where(p=>(p.PersonLocations.Select(ps=>ps.Location)
   .Where(l=>l.Description == "Amsterdam").Count() > 0)
    && (p.PersonRoles.Select(pr=>pr.Role)
   .Where(r=>r.Description == "Student").Count() > 0));

query result:

SELECT [t0].[personId] AS [PersonId], [t0].[description] AS [Description]
FROM [Persons] AS [t0]
WHERE (((
    SELECT COUNT(*)
    FROM [personlocations] AS [t1]
    INNER JOIN [Locations] AS [t2] ON [t2].[locationid] = [t1].[locationid]
    WHERE ([t2].[description] = @p0) AND ([t1].[personid] = [t0].[personId])
    )) > @p1) AND (((
    SELECT COUNT(*)
    FROM [PersonRoles] AS [t3]
    INNER JOIN [Roles] AS [t4] ON [t4].[roleid] = [t3].[roleid]
    WHERE ([t4].[description] = @p2) AND ([t3].[personid] = [t0].[personId])
    )) > @p3)

Using Contains():

var persons = Persons
                .Where(p=>(p.Personlocations.Select(ps=>ps.Location)
                .Select(l=>l.Description).Contains("Amsterdam")) && 
                (p.PersonRoles.Select(pr=>pr.Role)
                .Select(r=>r.Description).Contains("Student")));

query result:

SELECT [t0].[personId] AS [PersonId], [t0].[description] AS [Description]
FROM [Persons] AS [t0]
WHERE (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [personlocations] AS [t1]
    INNER JOIN [Locations] AS [t2] ON [t2].[locationid] = [t1].[locationid]
    WHERE ([t2].[description] = @p0) AND ([t1].[personid] = [t0].[personId])
    )) AND (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [PersonRoles] AS [t3]
    INNER JOIN [Roles] AS [t4] ON [t4].[roleid] = [t3].[roleid]
    WHERE ([t4].[description] = @p1) AND ([t3].[personid] = [t0].[personId])
    ))

using join():

var persons = Persons
        .Join(Personlocations, p=>p.PersonId, ps=>ps.Personid,
(p,ps) => new {p,ps})
.Where(a => a.ps.Location.Description =="Amsterdam")
        .Join(PersonRoles,
pr=> pr.p.PersonId, r=>r.Personid,(pr,r) => new {pr.p,r})
.Where(a=>a.r.Role.Description=="Student")
        .Select(p=> new {p.p});

Query Result:

SELECT [t0].[personId] AS [PersonId], [t0].[description] AS [Description]
FROM [Persons] AS [t0]
INNER JOIN [personlocations] AS [t1] ON [t0].[personId] = [t1].[personid]
INNER JOIN [Locations] AS [t2] ON [t2].[locationid] = [t1].[locationid]
INNER JOIN [PersonRoles] AS [t3] ON [t0].[personId] = [t3].[personid]
INNER JOIN [Roles] AS [t4] ON [t4].[roleid] = [t3].[roleid]
WHERE ([t4].[description] = @p0) AND ([t2].[description] = @p1)

You may want test wich one is faster with large data.

Good luck.

Giuliano Lemes

link|flag





vote up 1 vote down

Note:

Since it's in EF v1, we will NOT have PersonLocation and PersonRole generated as entities like what LINQ2SQL does

Solution 1:

Persons.Include("Role").Include("Location") // Include to load Role and Location
       .Where(p => p.Role.Any(r => r.description == "Student") 
       && p.Location.Any(l => l.description == "Amsterdam")).ToList();

This looks nice and straightforward, but this generates ugly SQL script and its performance is ok.

Solution 2:

Here are breakdowns.

   // Find out all persons in the role
   // Return IQuerable<Person> 
  var students = Roles.Where(r => r.description == "Student")
                      .SelectMany(r => r.Person);

 

 

// Find out all persons in the location
  // Return IQuerable<Person>  
  var personsInAmsterdam = Locations.Where(l=> l.description == "Amsterdam")
                                    .SelectMany(l=>l.Person);

 

 

// Find out the intersection that gives us students in Admsterdam.
  // Return List<Person>
     var AdmsterdamStudents = students.Intersect(personsInAmsterdam).ToList();

Combine three steps above into one:

 //Return List<Person>
 var AdmsterdamStudents = Roles.Where(r => r.description == "Student")
                              .SelectMany(r => r.Person)
                              .Intersect
                              ( 
                                Locations
                                .Where(l=> l.description == "Amsterdam")
                                .SelectMany(l=>l.Person)
                               ).ToList();

It's sort of verbose. But this generates clean SQL query and performs well.

编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示