LINQ объединяет запрос с реляционными сущностями - PullRequest
0 голосов
/ 07 сентября 2018

Я использую ef-core 2.1, у меня есть следующие упрощенные сущности, где один Account отображается на ноль или более Attribute объектов:

public class Account
{
    public int Id { get; set; }
    public int LongId { get; set; }
    public List<Attribute> Attributes { get; set; }
}

public class Attribute
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
    public Account Account { get; set; }
}

У меня есть исходная коллекция строк, которые напоминают Attribute.Value для данного фиксированного Attribute.Name, я хочу найти второй связанный Attribute объект от того же родителя Account и получить его Attribute.Value.

Я хочу объединить сущности ef с исходной коллекцией строк, чтобы я мог легко вывести:

  1. Независимо от того, существует ли соответствующий Account или Account без связанных объектов Attribute (оба соответствуют одному и тому же варианту использования).
  2. Если существует Account и он содержит все необходимые Attribute объекты, я хочу получить значение вторичного Attribute.

Без LINQ и ef я запускаю следующий SQL-запрос, который игнорирует родительский Account и выдает желаемый набор результатов:

CREATE TABLE #Temp
(
    id nvarchar(20) not null
);
INSERT INTO #Temp (id) VALUES ('cejawq'), ('issokq'), ('cqlpjq'), ('mbgzvi'), ('wqwlff'), ('iedifh');
SELECT t.[Id], attr2.[Value]
FROM #Temp t
LEFT OUTER JOIN [dbo].[Attributes] attr1
    ON t.[Id]=attr1.[Value]
    AND attr1.[Name]='uid'
LEFT OUTER JOIN [dbo].[Attributes] attr2
    ON attr1.[AccountId]=attr2.[AccountId]
    AND attr2.[Name]='objType';

Я получаю следующий набор результатов:

id|objType
-----------
cejawq|ext
issokq|ext
cqlpjq|int
mbgzvi|int
wqwlff|ext
iedifh|null

Я пытаюсь сопоставить это с эффективным LINQ таким образом, чтобы сгенерированный SQL генерировал набор результатов удаленно и возвращал данные, которые я могу проецировать, в эквивалентный анонимный тип. Нужно ли заботиться о родительских объектах в случае LINQ? У меня нет индекса по столбцу Attribute.Value.

Таблица Attributes содержит следующие данные:

Id|Name   |Value |AccountId
1 |uid    |cejawq|1
2 |objType|ext   |1
3 |uid    |issokq|2
4 |objType|ext   |2
5 |uid    |cqlpjq|3
6 |objType|int   |3
7 |uid    |mbgzvi|4
8 |objType|int   |4
9 |uid    |wqwlff|5
10|objType|ext   |5

1 Ответ

0 голосов
/ 07 сентября 2018

Поскольку ядро ​​EF не поддерживает объединения с последовательностями в памяти (пока), вы можете разделить запрос на две части - одну, которая принимает сторону сервера данных (объединение [Attributes до [Attributes), используя в сборе памяти как фильтр (SQL IN через метод LINQ Contains) и второй, который выполняет левое соединение в памяти с результатом запроса db:

DbContext db = ...;
var uids = new [] { "cejawq", "issokq", "cqlpjq", "mbgzvi", "wqwlff", "iedifh" };

var dbQuery =
    from attr1 in db.Set<Attribute>()
    where attr1.Name == "uid" && uids.Contains(attr1.Value)
    join attr2 in db.Set<Attribute>()
    on new { AccountId = attr1.Account.Id, Name = "objType" }
    equals new { AccountId = attr2.Account.Id, attr2.Name }
    into attr2Group from attr2 in attr2Group.DefaultIfEmpty() // left outer join
    select new { uid = attr1.Value, objType = attr2.Value };

var query =
    from uid in uids
    join dbResult in dbQuery on uid equals dbResult.uid
    into dbResultGroup from dbResult in dbResultGroup.DefaultIfEmpty() // left outer join
    select new { uid, dbResult?.objType };

var result = query.ToList();

Это переводит в один запрос БД, как это:

SELECT [attr1].[Value] AS [uid], [attr2].[Value] AS [objType]
FROM [Attributes] AS [attr1]
LEFT JOIN [Attributes] AS [attr2] ON ([attr1].[AccountId] = [attr2].[AccountId]) AND (N'objType' = [attr2].[Name])
WHERE ([attr1].[Name] = N'uid') AND [attr1].[Value] IN (N'cejawq', N'issokq', N'cqlpjq', N'mbgzvi', N'wqwlff', N'iedifh')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...