如何使用Linq和Entity Framework来加入两个连接表?
我有一个非常规范化的数据库,我试图将两个连接表连接在一起。如何使用Linq和Entity Framework来加入两个连接表?
我的目标是仅显示用户有权访问的文档。我使用实体框架,并有几个外键设置为下表:
关系(外键)
Users ---------------------------------------------
|
UserGroupMembership (UserID, GroupID)
|
|
Groups ----- -------------------------------------------------|
|
|
|---------------------------------------------------------|
|
|
XDocuments XDocumentSecurity (DocumentID, GroupID)
| |
---------------------------------------------
表定义
public partial class Users : EntityObject
{
public int UserID {get;set;} //PK
public string UserDisplayName {get;set;}
public DateTime CreateDate {get;set;}
public DateTime LoginDate {get;set;}
}
public partial class Groups : EntityObject
{
public int GroupID {get;set;} //PK
public string GroupDisplayName {get;set;}
public DateTime CreateDate {get;set;}
public DateTime LoginDate {get;set;}
}
public partial class UserGroupMembership: EntityObject // JoinTable
{
public int UserID {get;set;} //PK
public int GroupID {get;set;} //PK
// Not sure if this extra columns below causes an issue
public bool CanView {get;set;}
public bool CanDelete {get;set;}
public bool CanUpdate {get;set;}
public DateTime CreateDate {get;set;}
}
public partial class XDocumentSecurity : EntityObject // JoinTable
{
public int DocumentID {get;set;} //FK
public int GroupID {get;set;} //FK
public DateTime CreateDate {get;set;} // Not sure if this extra column causes an issue
}
public partial class XDocuments : EntityObject
{
public int DocumentID {get;set;} //PK
public string URL {get;set;}
public DateTime CreateDate {get;set;}
}
我我听说过很多关于Linq to EF如何以一种性能次优的方式改变SQL查询的故事。
这是is the .Union
operator sample,这似乎最适用于我在做什么。我可以简单地获得组列表当前用户中的一员,并发出此查询的修改版本:
public void Linq50()
{
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
var commonNumbers = numbersA.Intersect(numbersB);
Console.WriteLine("Common numbers shared by both arrays:");
foreach (var n in commonNumbers)
{
Console.WriteLine(n);
}
}
问题
- 如何查看SQL查询EF生成?
- 什么是更好的方式来解决这个问题(如果有的话)
如果您对您的所有键和外键导航属性没有Intersect
可选的查询应该是:
var query = context.XDocuments
.Where(d => d.Groups.Any(g => g.Users.Any(u => u.UserID == givenUserId)));
(“过滤器,其是在所有文档至少具有至少与钥匙一个用户= givenUserId
一个基团”)
我不知道这是否会在性能方面更好。
在EF 4.1,你可以检查生成的SQL只需:
var sql = query.ToString();
编辑
我理解你的模式将看怎么是这样的:
与相应的表三个实体:
public class User
{
public int UserID { get; set; }
public ICollection<Group> Groups { get; set; }
}
public class Group
{
public int GroupID { get; set; }
public ICollection<User> Users { get; set; }
public ICollection<XDocument> Documents { get; set; }
}
public class XDocument
{
public int DocumentID { get; set; }
public ICollection<Group> Groups { get; set; }
}
而User
和Group
之间的多对多的关系和Group
之间XDocument
以及:
modelBuilder.Entity<User>()
.HasMany(u => u.Groups)
.WithMany(g => g.Users)
.Map(c =>
{
c.MapLeftKey("UserID");
c.MapRightKey("GroupID");
c.ToTable("UserGroupMembership"); // join table name, no entity
});
modelBuilder.Entity<XDocument>()
.HasMany(d => d.Groups)
.WithMany(g => g.Documents)
.Map(c =>
{
c.MapLeftKey("DocumentID");
c.MapRightKey("GroupID");
c.ToTable("XDocumentSecurity"); // join table name, no entity
});
在此模型中,并映射上述查询应该是可能的。没有必要直接访问连接表(并且实际上不能通过LINQ to Entities访问它们,EF在内部管理这些表)。
我想我已经被洗脑,认为这是选择数据的唯一方法:'从_entities.X文件中的c'c.UserID == UserID.Value select c;'我会尝试你的技术,谢谢! – LamonteCristo
+1,酷,不知道'ToString()'会这样做:) –
嗯; XDocumentSecurity的引用在哪里?这是我真的坚持的部分 – LamonteCristo
您还可以查看由EF 4.1生成的SQL使用或者
- SQL事件探查器
- LINQPad
虽然,在这个时候,你需要使用LINQPad beta为EF 4.1。
关于你的第二个问题,我相信你的查询会翻译得很好。使用LINQPad检查SQL,下面的查询
var a1 = Addresses.Where(a => a.City.ToUpper().EndsWith("L")).Select(a => a.AddressID);
var a2 = Addresses.Where(a => a.City.ToUpper().StartsWith("B")).Select(a => a.AddressID);
var x1 = a1.Intersect(a2);
转化为
SELECT
[Intersect1].[AddressID] AS [C1]
FROM (SELECT
[Extent1].[AddressID] AS [AddressID]
FROM [Person].[Address] AS [Extent1]
WHERE UPPER([Extent1].[City]) LIKE N'%L'
INTERSECT
SELECT
[Extent2].[AddressID] AS [AddressID]
FROM [Person].[Address] AS [Extent2]
WHERE UPPER([Extent2].[City]) LIKE N'B%') AS [Intersect1]
我觉得@ Slauma的建议使用的导航proeprties是去,如果您的模型支持的方式。
不过,得到LINQPad--你不会后悔的:)
可以加载文档,而不是仅在一个查询中使用“Intersect”的ID(沿着第一个代码片段在这里:http:// stackoverflow。COM /问题/ 7821500 /什么,是最最有效的路到DO-比较,涉及到一对多,多对多relationshi/7828307#7828307)。这也可能是LINQ或导航属性友好,可以这么说。但我真的不知道关于SQL性能的更好。我也有这样的感觉,Where-Any-Any对大桌子更好,但我确定我不是。 – Slauma
呵呵,编辑之后我现在很困惑。为什么'XDocumentSecurity.GroupID'没有引用'Groups.GroupID'而不是'UserGroupMembership.GroupID'?我想知道如何在SQL中定义它,因为在关系中没有(完整的)主键或唯一的列(这是必需的 - 至少在SQL Server中)。 – Slauma
@Slauma - 你说得对,我纠正了FK图。 – LamonteCristo
什么是EntityObject?是你自己的基类还是EF的EntityObject类?我想知道你的问题是用EF 4.1标记的。如果你使用'DbContext',那么你不能从EF的'EntityObject'派生。是的,如果你想定义一个多对多的关系,连接表中的附加列是一个问题。您基本上必须为每个多对多创建两个一对多关系。最后的问题:为什么你的模型没有任何导航属性来定义实体之间的关系? – Slauma