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

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

Страна -> Регион -> Город -> ZipCode

Каждый объект содержит ссылку на своего родителя (например, Region.Country) и коллекцию его дочерних элементов (например, Region.Cities).

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

Чтобы усложнить ситуацию, каждая сущность локализуется перед проецированием в модель. Итак, 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);
}

Тест не проходит с:

System.NotSupportedException: Метод 'System.Linq.IQueryable`1 [Beeline.EducationCompass.Model.Region] GetRegions (Int32)' не поддерживает преобразование в SQL.

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
471
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это один липкий фрагмент кода, и я бы не ответил на него из-за отсутствия соответствующих навыков, если бы кто-то еще имел, но поскольку у вас нет ответов ...

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

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

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

Удачи.

Ответ принят как подходящий

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

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

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

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

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();
  }
}

Спасибо! Это выглядит многообещающе. Я попробую завтра.

Doug Wilson 27.01.2009 02:30

Вариант этого сработал. Чтобы LinqToSql не выдавал по одному запросу на регион для получения городов, мне пришлось создать как ToRegion (ec_Region dto), так и ToRegionShallow (ec_Region dto). Мелкая версия создает модель, но не заполняет дочерние коллекции (Города в случае Региона).

Doug Wilson 29.01.2009 02:55

Я думаю, если бы моя база данных использовала подход Джо Селко «Вложенный набор», это можно было бы сделать в одном запросе. Теоретически для создания экземпляра иерархии потребуется только один проход по набору результатов. У кого-нибудь еще есть мысли по этому поводу?

Doug Wilson 29.01.2009 02:59

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