C # лямбда-соединение с несколькими условиями в левом внешнем соединении SQL - PullRequest
0 голосов
/ 11 января 2019

У меня есть следующий SQL:

 select * 
  from  [dbo].[CustomField] cf
  left outer join [dbo].[CustomFieldDataItem] cd on cd.CustomFieldId = cf.Id and cd.OutsideId = 180
  where cf.AnotherId = 1

И я хочу написать это на C # Лямбда, НО мой код показывает только элементы данных для настраиваемых полей, которые имеют элементы данных - я хочу увидеть все настраиваемые поля и есть ли у них элементы данных - пока у меня есть это:

var myQuery = _db.CustomFields
                        .Where(c => c.AnotherId == 1)
                        .Join(_db.CustomFieldDataItems, cf => cf.Id, cd => cd.CustomFieldId, (cf, cd) => new { cf, cd })
                        .Where(f => f.cd.OutsideId == 180)
                        .Select(z => new CustomFieldModel
                        {
                            CustomFieldId = z.cf.Id,
                            Name = z.cf.Name,
                            DataValue = z.cd.DataValue
                        }).ToList()  

Я не знаю, куда поместить предложение OutsideId where, которое действительно должно быть частью объединения

Ответы [ 4 ]

0 голосов
/ 14 января 2019

В ответ на все работы @Cetin Basoz мне удалось получить решение с использованием LinqPad в Lambda - вместо Join мне нужно было использовать SelecctMany () в этом примере:

var myQuery = _db.CustomFields.SelectMany(
                      cf => cf.DataItems.Where(d => d.OutsideId == x.Id).DefaultIfEmpty(),
                      (cf, cd) => new {cf, cd })
                   .Where(s => s.cf.AnotherId == 1)
                   .Select(
                      m =>
                         new MyModel
                         {
                             CustomFieldId = m.cf.Id,
                             Name = m.cf.Name,
                             DataType = m.cf.DataType,
                             StringValue = m.cd.StringValue,
                             IntValue = m.cd.IntValue,
                             BoolValue = m.cd.BoolValue,
                             DateTimeValue = m.cd.DateTimeValue,
                             DecimalValue = m.cd.DecimalValue
                         }
                   ).ToList()
0 голосов
/ 11 января 2019

В Linq вам действительно нужно присоединиться очень редко:

var myQuery = _db.CustomFields
                 .Where(c => c.AnotherId == 1)
                 .Select(cf => new CustomFieldModel
                 {
                     CustomFieldId = cf.Id,
                     Name = cf.Name,
                     DataValue = cf.cd.Any(cd => cd.OutSideId == 180) 
                              ? cf.cd.First(cd => cd.OutSideId == 180).DataValue
                              : (<type?>)null;
                 }).ToList();

В LinqToSQL это сгенерирует SQL вроде:

DECLARE @p0 int = 1;
DECLARE @p1 Int = 180;
DECLARE @p2 Int = 180;

SELECT [t0].[CustomFieldId], [t0].[Name], 
    (CASE 
        WHEN EXISTS(
            SELECT NULL AS [EMPTY]
            FROM [CustomFieldDataItems] AS [t1]
            WHERE ([t1].[OutsideID] = @p1) AND ([t1].[CustomFieldID] = [t0].[ID])
            ) THEN (
            SELECT [t3].[DataValue]
            FROM (
                SELECT TOP (1) [t2].[DataValue]
                FROM [CustomFieldDataItems] AS [t2]
                WHERE ([t2].[OutsideID] = @p2) AND ([t2].[CustomFieldID] = [t0].[ID])
                ) AS [t3]
            )
        ELSE NULL
     END) AS [DataValue]
FROM [CustomFields] AS [t0]
WHERE [t0].[AnotherId] = @p0

Это на самом деле не дает результата с вашим исходным SQL (я использовал First, чтобы получить DataValue, что означало бы, что вы не получите результат как 1-Many).

Тогда мы могли бы переписать его так, как в этой оптимизированной версии, которая также дает исходный результат, который вы изначально (не уверены, хотите ли вы повторять строки из пользовательских полей - ваш SQL делает):

var myQuery = (from cf in CustomFields
              from cd in cf.CustomFieldDataItems.Where(d => d.OutsideId==180).DefaultIfEmpty()
              where cf.AnotherId == 1
              select new CustomFieldModel
             {
                 CustomFieldId = cf.Id,
                 Name = cf.Name,
                 DataValue = cd == null?(int?)null:cd.DataValue,
                 AnotherValue = cd == null?(<typeName?>)null:cd.AnotherValue,
             }).ToList();

Сгенерированный SQL будет выглядеть так:

DECLARE @p0 Int = 1;
DECLARE @p1 Int = 180;

SELECT [t0].[CustomFieldId], [t0].[Name], 
    (CASE 
        WHEN [t2].[test] IS NULL THEN NULL
        ELSE [t2].[DataValue]
     END) AS [DataValue], 
    (CASE 
        WHEN [t2].[test] IS NULL THEN NULL
        ELSE [t2].[AnotherValue]
     END) AS [AnotherValue]
FROM [CustomFields] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[DataValue], [t1].[CustomFieldID], [t1].[AnotherValue], [t1].[OutsideId]
    FROM [CustomFieldDataItems] AS [t1]
    ) AS [t2] ON ([t2].[OutsideId] = @p0) AND ([t2].[CustomFieldID] = [t0].[ID])
WHERE [t0].[AnotherId] = @p1;

РЕДАКТИРОВАТЬ: Если подумать, это можно было бы упростить до:

var myQuery = (from cf in CustomFields
              from cd in cf.CustomFieldDataItems.Where(d => d.OutsideId==180).DefaultIfEmpty()
              where cf.AnotherId == 1
              select new CustomFieldModel
             {
                 CustomFieldId = cf.Id,
                 Name = cf.Name,
                 DataValue = (int?)cd.DataValue,
                 AnotherValue = (<typeName?>)cd.AnotherValue,
             }).ToList();

Это сгенерирует SQL как:

DECLARE @p0 Int = 180;
DECLARE @p1 Int = 1;

SELECT [t0].[CustomFieldId], [t0].[Name], 
       [t1].[DataValue], [t1].[AnotherValue]
FROM [CustomFields] AS [t0]
LEFT OUTER JOIN [CustomFieldDataItems] AS [t1] 
  ON ([t1].[OutSideId] = @p0) AND ([t1].[CustomFieldID] = [t0].[ID])
WHERE [t0].[AnotherId] = @p1;

Почти такой же, как ваш.

Только для завершения как "лямбда":

var myQuery = _db.CustomFields
   .Where (cf => cf.AnotherId == 1)
   .SelectMany (
      cf => cf.CustomFieldDataItems.Where (cd => (cd.OutsideId == (Int32?)180)).DefaultIfEmpty(), 
      (cf, cd) => new CustomFieldModel
             {
                 CustomFieldId = cf.Id,
                 Name = cf.Name,
                 DataValue = (int?)cd.DataValue,
                 AnotherValue = (<typeName?>)cd.AnotherValue,
             }
   ).ToList();
0 голосов
/ 11 января 2019

Если у вас есть свойство навигации, вы можете пойти так:

var myQuery = _db.CustomFields.Include(x => x.CustomFieldDataItems && x.CustomFieldDataItems.OutsideId == 180)
.Where(c => c.AnotherId == 1).Select(z => new CustomFieldModel
                        {
                            CustomFieldId = z.Id,
                            Name = z.Name,
                            DataValue = z.CustomFieldDataItems.DataValue
                        }).ToList() 

«Включить» - это «ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ» в базе данных t

РЕДАКТИРОВАТЬ: Изменено включить лямбда

0 голосов
/ 11 января 2019

попробуй с .DefaultIfEmpty()

var myQuery = _db.CustomFields                   
    .Where(c => c.AnotherId == 1)
    .Join(_db.CustomFieldDataItems.DefaultIfEmpty(), cf => cf.Id, cd => cd.CustomFieldId, (cf, cd) => new { cf, cd })
    .Where(f => f.cd.OutsideId == 180)
    .Select(z => new CustomFieldModel
    {
        CustomFieldId = z.cf.Id,
        Name = z.cf.Name,
        DataValue = z.cd.DataValue
    }).ToList()  
...