2

我有一個非常規範化的數據庫,我試圖將兩個連接表連接在一起。如何使用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生成?
  • 什麼是更好的方式來解決這個問題(如果有的話)
+0

呵呵,編輯之後我現在很困惑。爲什麼'XDocumentSecurity.GroupID'沒有引用'Groups.GroupID'而不是'UserGroupMembership.GroupID'?我想知道如何在SQL中定義它,因爲在關係中沒有(完整的)主鍵或唯一的列(這是必需的 - 至少在SQL Server中)。 – Slauma

+0

@Slauma - 你說得對,我糾正了FK圖。 – LamonteCristo

+0

什麼是EntityObject?是你自己的基類還是EF的EntityObject類?我想知道你的問題是用EF 4.1標記的。如果你使用'DbContext',那麼你不能從EF的'EntityObject'派生。是的,如果你想定義一個多對多的關係,連接表中的附加列是一個問題。您基本上必須爲每個多對多創建兩個一對多關係。最後的問題:爲什麼你的模型沒有任何導航屬性來定義實體之間的關係? – Slauma

回答

3

如果您對您的所有鍵和外鍵導航屬性沒有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; } 
} 

UserGroup之間的多對多的關係和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在內部管理這些表)。

+0

我想我已經被洗腦,認爲這是選擇數據的唯一方法:'從_entities.X文件中的c'c.UserID == UserID.Value select c;'我會嘗試你的技術,謝謝! – LamonteCristo

+0

+1,酷,不知道'ToString()'會這樣做:) –

+0

嗯; XDocumentSecurity的引用在哪裏?這是我真的堅持的部分 – LamonteCristo

3

您還可以查看由EF 4.1生成的SQL使用或者

雖然,在這個時候,你需要使用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--你不會後悔的:)

+0

可以加載文檔,而不是僅在一個查詢中使用「Intersect」的ID(沿着第一個代碼片段在這裏:http:// stackoverflow。COM /問題/ 7821500 /什麼,是最最有效的路到DO-比較,涉及到一對多,多對多relationshi/7828307#7828307)。這也可能是LINQ或導航屬性友好,可以這麼說。但我真的不知道關於SQL性能的更好。我也有這樣的感覺,Where-Any-Any對大桌子更好,但我確定我不是。 – Slauma