Entity Framework многие-ко-многим, используя VB.Net Lambda - PullRequest
4 голосов
/ 11 февраля 2010

Я использую Entity Framework в Visual Studio 2010 Beta 2 (.NET Framework 4.0 Beta 2). Я создал модель базы данных .edmx из моей базы данных, и у меня есть несколько взаимосвязей «многие ко многим».

Тривиальным примером моей схемы базы данных является

  • Роли (ID, Имя, Актив)
  • Члены (ID, DateOfBirth, DateCreated)
  • RoleMembership (RoleID, MemberID)

Сейчас я пишу пользовательский поставщик ролей (Inheriting System.Configuration.Provider.RoleProvider) и пришел написать реализацию IsUserInRole (username, roleName).

Запросы LINQ-to-Entity, которые я писал, когда SQL-Profiled, производили операторы CROSS JOIN, когда я хочу, чтобы они INNER JOIN.

        Dim query = From m In dc.Members
                    From r In dc.Roles
                    Where m.ID = 100 And r.Name = "Member"
                    Select m

Моя проблема почти точно описана здесь: Платформа сущностей и многие ко многим запросам непригодны?

Я уверен, что представленное там решение работает хорошо, но хотя я изучал Java в универе и в основном понимаю C #, я не могу понять предоставленный синтаксис Lambda, и мне нужно получить аналогичный пример в VB. Большую часть дня я просматривал в Интернете, но я не приблизился к своему ответу.

Поэтому, пожалуйста, кто-нибудь может посоветовать, как в VB я могу создать оператор LINQ, который бы делал этот эквивалент в SQL:

SELECT rm.RoleID
FROM RoleMembership rm 
  INNER JOIN Roles r ON r.ID = rm.RoleID
  INNER JOIN Members m ON m.ID = rm.MemberID
WHERE r.Name = 'Member' AND m.ID = 101

Я бы использовал этот запрос, чтобы увидеть, есть ли Участник 101 в Роли 3. (Я понимаю, что мне, вероятно, не нужно объединяться с таблицей Members в SQL, но я думаю, что в LINQ мне нужно будет ввести объект Member?)

UPDATE:

Я немного ближе, используя несколько методов:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Dim count As Integer

    Using dc As New CBLModel.CBLEntities

        Dim persons = dc.Members.Where(AddressOf myTest)

        count = persons.Count

    End Using

    System.Diagnostics.Debugger.Break()
End Sub

Function myTest(ByVal m As Member) As Boolean
    Return m.ID = "100" AndAlso m.Roles.Select(AddressOf myRoleTest).Count > 0
End Function

Function myRoleTest(ByVal r As Role) As Boolean
    Return r.Name = "Member"
End Function

SQL Profiler показывает это:

SQL: BatchStarting

SELECT 
[Extent1].[ID] AS [ID], 
... (all columns from Members snipped for brevity) ...
FROM [dbo].[Members] AS [Extent1]

RPC: завершено

exec sp_executesql N'SELECT 
[Extent2].[ID] AS [ID], 
[Extent2].[Name] AS [Name], 
[Extent2].[Active] AS [Active]
FROM  [dbo].[RoleMembership] AS [Extent1]
INNER JOIN [dbo].[Roles] AS [Extent2] ON [Extent1].[RoleID] = [Extent2].[ID]
WHERE [Extent1].[MemberID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=100

SQL: BatchCompleted

SELECT 
[Extent1].[ID] AS [ID], 
... (all columns from Members snipped for brevity) ...
FROM [dbo].[Members] AS [Extent1]

Я не уверен, почему он использует sp_execsql для внутреннего оператора соединения и почему он все еще выполняет выбор для выбора ВСЕХ членов.

Спасибо.

ОБНОВЛЕНИЕ 2

Я написал это, превратив вышеупомянутые «множественные методы» в лямбда-выражения, а затем все в один запрос, например:

    Dim allIDs As String = String.Empty

    Using dc As New CBLModel.CBLEntities

        For Each retM In dc.Members.Where(Function(m As Member) m.ID = 100 AndAlso m.Roles.Select(Function(r As Role) r.Name = "Doctor").Count > 0)
            allIDs &= retM.ID.ToString & ";"
        Next

    End Using

Но, похоже, это не работает: «Доктор» - это не роль, которая существует, я просто поместил ее для тестирования, но для «allIDs» все еще установлено значение «100;»

SQL в SQL Profiler на этот раз выглядит так:

SELECT 
[Project1].*
FROM ( SELECT 
    [Extent1].*, 
    (SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[RoleMembership] AS [Extent2]
        WHERE [Extent1].[ID] = [Extent2].[MemberID]) AS [C1]
    FROM [dbo].[Members] AS [Extent1]
)  AS [Project1]
WHERE (100 = [Project1].[ID]) AND ([Project1].[C1] > 0)

Для краткости я превратил список всех столбцов из таблицы Members в *

Как видите, он просто игнорирует запрос "Роль".

Ответы [ 2 ]

3 голосов
/ 03 июня 2010

Вот как я решил это, используя лямбда-выражения:

    Public Overrides Function IsUserInRole(ByVal username As String, ByVal roleName As String) As Boolean
        Dim retVal As Boolean

        Using db As New CBLEntities
            Dim theRole = db.Roles.Where(Function(x) x.Name = roleName)
            retVal = theRole.Any(Function(r As Role) r.Members.Where(Function(m As Member) m.ID = username).Any())
        End Using

        Return retVal
    End Function
3 голосов
/ 03 июня 2010

Если вы не используете ассоциации (сопоставления или внешние ключи) в своей схеме, вы можете использовать этот синтаксис:

Dim query = From rm As RoleMembership _
            In RoleMemberships _
            Join m As Member In Members On m.ID Equals rm.MemberID _
            Join r As Role In Roles On r.ID Equals rm.RoleID _
            Where r.Name = "Member" _
                And m.ID = 100 _
            Select rm

И если вы используете ассоциации, это так:

Dim query2 = From r As Role _
             In Roles _
             Where r.Name = "Member" _
                 And r.Members.Any(Function(m As Member) m.ID = 100) _
             Select r
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...