Как использовать Linq и Entity Framework для объединения двух таблиц? - PullRequest
2 голосов
/ 05 ноября 2011

У меня очень нормализованная база данных, и я пытаюсь объединить две таблицы соединения.

Моя цель - показывать только те документы, на которые у пользователя есть разрешение. Я использую 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 способом, который является неоптимальным для производительности.

Вот - пример оператора .Union , который наиболее применим к тому, что я делаю. Я мог бы просто получить список групп, членом которых является текущий пользователь, и выдать измененную версию этого запроса:

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?
  • Как лучше подойти к этой проблеме (если есть)

Ответы [ 2 ]

3 голосов
/ 05 ноября 2011

Вы также можете просмотреть SQL, сгенерированный EF 4.1, используя

Хотя,в настоящее время вам нужно использовать LINQPad бета для 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 использовать свойства навигации - это тот способ, если ваша модельподдерживает это.

Тем не менее, получите LINQPad - вы не пожалеете об этом:)

3 голосов
/ 05 ноября 2011

Если у вас есть свойства навигации для всех ваших ключей и внешних ключей, альтернативный запрос без Intersect будет выглядеть так:

var query = context.XDocuments
    .Where(d => d.Groups.Any(g => g.Users.Any(u => u.UserID == givenUserId)));

("Фильтровать все документы, которые находятся в как минимум * 1006"* одна группа, в которой есть как минимум один пользователь с ключом = 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 управляет этими таблицами внутри).

...