2

我有一个层次结构,我想用 LinqToSql 查询:

国家 -> 地区 -> 城市 -> 邮政编码

每个实体都包含对其父级的引用(例如 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;
}

... ETC。

[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 的转换。

你会建议我怎么做?如果层次结构的每个级别都在同一个表中而不是单独的表中,会更简单(或可能)吗?

4

2 回答 2

2

您将要使用 linq 设计器来设置对象之间的关系。通过创建属性,这使您无需在加入后加入后编写加入。

  • 一个国家和它的地区之间
  • 一个地区和它的城市之间
  • 一个国家和它的本地化之间
  • 区域及其本地化之间

您将要使用ToList来分隔那些您打算转换为 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();
  }
}
于 2009-01-21T18:33:49.637 回答
1

那是一段粘性代码,如果其他人有相关技能,我不会回答这个问题,但既然你没有回应......

我可以告诉你错误信息的含义。这意味着函数 GetRegions 不能被 linq to sql 提供程序转换为 sql。一些内置函数可以,因为提供者理解它们,这里有一个列表。否则,您可以提供翻译,请参见此处

在您的情况下,您需要“内联”此查询的逻辑,该逻辑不会跨越函数调用的边界,因为您正在处理表达式树,sql server 无法回调您的 GetRegions 方法。

至于具体的做法,你得去试一试,我现在没时间答应你。(除非别人有时间和技能?)

祝你好运。

于 2009-01-21T00:31:47.823 回答