Entity Framework - Внутреннее соединение по внешнему ключу - PullRequest
1 голос
/ 31 января 2012

Я использую Entity Framework 4.0 (поэтому я могу использовать его с .NET 3.5) и сгенерировал DAL из существующей базы данных.

В базе данных у меня есть две таблицы со следующими столбцами (ничто не может быть пустым):

  1. tblWeapon (WeaponId PK, WeaponLabel)
  2. tblShot (ShotId PK, WeaponId)

И есть внешний ключ в tblShot's WeaponId для tblWeapon.

Тогда сгенерированные сущности выглядят примерно так:

public class Weapon {
    public int WeaponId { ... }
    public string WeaponLabel { ...}
    public EntityCollection Shots { ... }
}

public class Shot {
    public int ShotId { ... }
    public EntityReference WeaponReference { ... }
    public Weapon Weapon { ... }
}

В моем коде у меня есть классы ShotFilter и WeaponFilter, которые содержат критерии для фильтрации отдельных таблиц. Поскольку фильтр для сущностей генерируется динамически, я хотел бы распространить генерацию запросов на соответствующие классы фильтров. Каждый фильтр будет возвращать IQueryable<T>, и они будут объединяться по мере необходимости для достижения желаемых результатов.

То, что я хочу сделать, - это получить все объекты Shot, которые ссылаются на оружие, на этикетке которого содержится текст 0.5.

Проблема возникает при попытке выполнить внутреннее соединение на IQueryable<Shot> с IQueryable<Weapon>, поскольку Shot не содержит поля WeaponId (просто WeaponReference). Пройдя по сети и не найдя ничего, я нашел сообщение на форуме , где ответом было просто присоединиться к самим объектам. Поэтому я попробовал это и получил ожидаемые результаты:

var oWeaponQuery = from w in oDc.Weapons select w;
oWeaponQuery = oWeaponQuery.Where(w => w.Label.Contains("0.5"));

var oShotQuery = from s in oDc.Shots select s;
oShotQuery = oShotQuery.Join(oWeaponQuery, s => s.Weapon, w => w, (s, w) => s);

Но когда я проверял фактический SQL-запрос, запрашиваемый с помощью SQL Server Profiler, я увидел это ужасное утверждение (и его немного вырвало):

SELECT 
1 AS [C1], 
[Extent1].[ShotId] AS [ShotId], 
[Extent1].[WeaponId] AS [WeaponId]
FROM  [dbo].[tblShot] AS [Extent1]
INNER JOIN [dbo].[tblWeapon] AS [Extent2] ON  EXISTS (SELECT 
    cast(1 as bit) AS [C1]
        FROM    ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
        LEFT OUTER JOIN  (SELECT 
            [Extent3].[WeaponId] AS [WeaponId]
            FROM [dbo].[tblWeapon] AS [Extent3]
            WHERE [Extent1].[WeaponId] = [Extent3].[WeaponId] ) AS [Project1] ON 1 = 1
        LEFT OUTER JOIN  (SELECT 
            [Extent4].[WeaponId] AS [WeaponId]
            FROM [dbo].[tblWeapon] AS [Extent4]
            WHERE [Extent1].[WeaponId] = [Extent4].[WeaponId] ) AS [Project2] ON 1 = 1
        WHERE ([Project1].[WeaponId] = [Extent2].[WeaponId]) OR (([Project2].[WeaponId] IS NULL) AND ([Extent2].[WeaponId] IS NULL))
    )
WHERE (CAST(CHARINDEX(N'0.5', [Extent2].[Label]) AS int)) > 0

Итак, как бы я сделал это правильно или хотя бы эффективно? Или какие-нибудь другие предложения о том, как организовать генерацию запросов динамически и распределенно?

Спасибо!


Обновление с более подробной информацией

Часть моей проблемы при выполнении соединения связана с EF, в сгенерированном объекте для Shot нет свойства WeaponId. Есть только свойство WeaponReference, которое управляет им. Поэтому в моем соединении я ожидал, что смогу использовать:

oShotQuery = oShotQuery.Join(oWeaponQuery, s => s.WeaponId, w => w.WeaponId, (s, w) => s);

Но это не работает из-за того, что WeaponId не является свойством Shot.

Затем я попробовал это (что опять-таки кажется странным):

oShotQuery = oShotQuery.Join(oWeaponQuery, s => s.Weapon.WeaponId, w => w.WeaponId, (s, w) => s);

И это работает, и выдает довольно сжатый SQL (за исключением):

SELECT 
1 AS [C1], 
[Extent1].[ShotId] AS [ShotId], 
[Extent1].[WeaponId] AS [WeaponId]
FROM  [dbo].[tblShot] AS [Extent1]
INNER JOIN [dbo].[tblWeapon] AS [Extent2] ON ([Extent1].[WeaponId] = [Extent2].[WeaponId]) OR 
(([Extent1].[WeaponId] IS NULL) AND ([Extent2].[WeaponId] IS NULL))
WHERE (CAST(CHARINDEX(N'0.5', [Extent2].[Label]) AS int)) > 0

И это исключение таково: OR (([Extent1].[WeaponId] IS NULL) AND ([Extent2].[WeaponId] IS NULL)). Я не хочу, где они оба NULL, я хочу только там, где они равны.

1 Ответ

0 голосов
/ 01 февраля 2012

Что я хочу сделать, так это получить все объекты Shot, которые ссылаются на оружие, где на ярлыке содержится текст 0.5.

Этот запрос я бы использовал:

var oShotQuery = oDc.Shots.Where(s => s.Weapon.Label.Contains("0.5"))

Что касается сгенерированного SQL, не откладывайте, если он добавляет кучу вещей, о которых вы и не думали.Эти запросы, как правило, выполняются как минимум так же хорошо, как и любые, которые вы могли бы генерировать вручную.Главное, чтобы исходный запрос был простым.

Обновление

Я хочу иметь возможность динамически генерировать различные запросы, а затем объединять их позже.

Как насчет этого?

var oShotQuery = oShotQuery.Where(
    o => oWeaponQuery.Any(w => w.WeaponId == o.Weapon.WeaponId))
...