Фильтрация дочерней коллекции одного объекта - PullRequest
0 голосов
/ 16 февраля 2019

У меня есть следующие объекты:

public class Product {
  public Int32 ProductId { get; set; }
  public Double Price { get; set; }
  public virtual ProductType ProductType { get; set; }
}

public class ProductType {
  public Int32 ProductTypeId { get; set; }
  public virtual ICollection<ProductTypeLocalization> ProductTypeLocalizations { get; set; }
}

public class ProductTypeLocalization {
  public Int32 ProductTypeId { get; set; }
  public String Language { get; set; }
  public String Name { get; set; }
  public String Description { get; set; }
  public virtual ProductType { get; set; }
}

Тогда у меня есть запрос следующим образом:

var models = await products.Select(product => new {
  Id = product.Id,
  Price = product.Price,
  ProductType = new {
    Id = product.ProductType.ProductTypeId,
    Name = ???,
    Description = ???
  }
}).ToListAsync()

На мой запрос, где он показывает

Name = ???,
Description ???

Мне нужно получить Name и Description от ProductTypeLocalization с Language == "en".

Я мог бы использовать FirstOrDefault для каждого, но я думаю, что это неэффективный способ.

Каков наилучший способ сделать это?

1 Ответ

0 голосов
/ 16 февраля 2019

LEFT OUTER JOIN перевод кажется наилучшим для такого сценария.

Теоретически, переводчик запросов EF Core должен иметь возможность объединять общие выражения FirstOrDefault() в один LEFT OUTER JOIN, как это делается для необязательной ссылки.Навигационные свойства.

На практике (по состоянию на последнее время в EF Core 2.2) это не выполняется, и для каждого выбранного поля создается отдельный коррелированный подзапрос.

Предполагая, что каждый тип продукта имеет 0 или 1 локализацию для конкретного языка, желаемый перевод может быть достигнут с помощью SelectMany, например:

var models = await products.SelectMany(
    product => product.ProductType.ProductTypeLocalizations
        .DefaultIfEmpty()
        .Where(ptl => ptl == null || ptl.Language == "en"),
    (product, ptl) => new
    {
        Id = product.ProductId,
        Price = product.Price,
        ProductType = new
        {
            Id = product.ProductType.ProductTypeId,
            Name = ptl.Name,
            Description = ptl.Description
        }
    })
    .ToListAsync();

или эквивалентной и более удобочитаемой версии.используя синтаксис запроса LINQ:

var models = await (
    from product in products
    let pt = product.ProductType
    from ptl in pt.ProductTypeLocalizations.DefaultIfEmpty()
    where ptl == null || ptl.Language == "en"
    select new
    {
        Id = product.ProductId,
        Price = product.Price,
        ProductType = new
        {
            Id = pt.ProductTypeId,
            Name = ptl.Name,
            Description = ptl.Description
        }
    }).ToListAsync();
...