Как я могу запрашивать иерархии с помощью LinqToSQL?

StackOverflow https://stackoverflow.com/questions/463233

  •  19-08-2019
  •  | 
  •  

Вопрос

У меня есть иерархия, которую я хотел бы запросить с помощью LinqToSql:

Страна -> Регион -> Город -> Почтовый индекс

Каждая сущность содержит как ссылку на своего родителя (например,Регион.Страна) и коллекцию его дочерних элементов (например.Регион.Города).

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

Ситуация усложняется тем, что каждая сущность перед проецированием в модель локализуется.Таким образом, Country.Name меняется в зависимости от языка.

Вот некоторые фрагменты того, что у меня есть на данный момент:

public IQueryable<Country> ListCountries()
{
  return ProjectCountry(dataContext.GetTable<ec_Country>());
}

private IQueryable<Country> ProjectCountry(IQueryable<ec_Country> query)
{
  var result = from country in query
  join localized in dataContext.GetTable<ec_CountryLocalization>() on country.CountryID equals localized.CountryID
  let regions = GetRegions(country.CountryID)
  where localized.StatusID == 4 && localized.WebSiteID == this.webSiteID
  select new Country(country.CountryID) {
    CreatedDate = country.CreatedDate,
    IsDeleted = country.IsDeleted,
    IsoCode = country.IsoCode,
    Name = country.Name,
    Regions = new LazyList<Region>(regions),
    Text = localized.Text,
    Title = localized.Title,
    UrlKey = country.UrlKey
  };

  return result;
}

private IQueryable<Region> GetRegions(Int32 countryID)
{
  var query = from r in dataContext.GetTable<ec_Region>()
  where r.CountryID == countryID
  orderby r.Name
  select r;

  return ProjectRegion(query);
}

private IQueryable<Region> ProjectRegion(IQueryable<ec_Region> query)
{
  var result = from region in query
  join localized in dataContext.GetTable<ec_RegionLocalization>() on region.RegionID equals localized.RegionID
  join country in ListCountries() on region.CountryID equals country.CountryID
  let cities = GetCities(region.RegionID)
  select new Region(region.RegionID) {
    Cities = new LazyList<City>(cities),
    Country = country,
    CountryID = region.CountryID,
    CreatedDate = region.CreatedDate,
    IsDeleted = region.IsDeleted,
    IsoCode = region.IsoCode,
    Name = region.Name,
    Text = localized.Text,
    Title = localized.Title,
    UrlKey = region.UrlKey
  };

  return result;
}

...и т. д.

[TestMethod]
public void DataProvider_Correctly_Projects_Country_Spike()
{
  // Act
  Country country = dataProvider.GetCountry(1);

  // Assert
  Assert.IsNotNull(country);
  Assert.IsFalse(String.IsNullOrEmpty(country.Description));
  Assert.IsTrue(country.Regions.Count > 0);
}

Тест не пройден с:

Системное.NotSupportedException:Метод «System.Linq.IQueryable`1[Beeline.EducationCompass.Model.Region] GetRegions(Int32)» не поддерживает перевод в SQL.

Как бы вы порекомендовали мне это сделать?Было бы проще (или возможно), если бы каждый уровень иерархии находился в одной таблице, а не в отдельных?

Это было полезно?

Решение

Вам понадобится использовать конструктор linq для настройки связей между вашими объектами.Это избавит вас от необходимости писать соединение за объединением, создавая свойства.

  • между страной и ее регионами
  • между регионом и его городами
  • между страной и ее локализациями
  • между регионом и его локализациями

Вы захотите использовать К списку чтобы разделить те операции, которые вы собираетесь транслировать в SQL, и те операции, которые вы собираетесь выполнять в локальном коде.Если вы этого не сделаете, вы продолжите видеть исключения «невозможно перевести ваш метод в SQL».

Вы также захотите использовать Параметры загрузки данных в некоторых случаях для быстрой загрузки этих свойств.Вот мой удар по этому поводу.

DataLoadOptions dlo = new DataLoadOptions();
//bring in the Regions for each Country
dlo.LoadWith<ec_Country>(c => c.Regions);
//bring in the localizations
dlo.AssociateWith<ec_Country>(c => c.Localizations
  .Where(loc => loc.StatusID == 4 && loc.WebSiteID == this.webSiteID)
);
dlo.AssociateWith<ec_Region>(r => r.Localizations);

//set up the dataloadoptions to eagerly load the above.
dataContext.DataLoadOptions = dlo;

//Pull countries and all eagerly loaded data into memory.
List<ec_Country> queryResult = query.ToList();

//further map these data types to business types
List<Country> result = queryResult
  .Select(c => ToCountry(c))
  .ToList();

public Country ToCountry(ec_Country c)
{
  return new Country()
  {
    Name = c.Name,
    Text = c.Localizations.Single().Text,
    Regions = c.Regions().Select(r => ToRegion(r)).ToList()
  }
}

public Region ToRegion(ec_Region r)
{
  return new Region()
  {
    Name = r.Name,
    Text = r.Localizations.Single().Text,
    Cities = r.Cities.Select(city => ToCity(city)).ToLazyList();
  }
}

Другие советы

Это один клочок кода, и я бы не ответил на это из-за отсутствия соответствующих навыков, если бы кто-то еще имел, но так как у вас не было ответов ...

Я могу рассказать вам, что означает сообщение об ошибке. Это означает, что функция GetRegions не может быть преобразована в sql поставщиком linq to sql. Некоторые встроенные функции могут быть, потому что поставщик понимает их, вот список . В противном случае вы можете предоставить перевод, см. здесь .

В вашей ситуации вам нужно «встроить» логику этого запроса, чтобы логика не пересекала границу вызова функции, потому что вы имеете дело с деревом выражений, сервер sql не может перезвонить в ваш Метод GetRegions.

Что касается точного способа сделать это, вам нужно попробовать, у меня нет времени, чтобы навязать вам сейчас. (Если у кого-то еще нет времени и навыков?)

Удачи.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top