如何使用 LinqToSQL 查询层次结构?
我有一个层次结构,我想使用 LinqToSql 进行查询:
Country -> 地区-> 城市-> 邮政编码
每个实体都保存对其父级(例如 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: Method 'System.Linq.IQueryable`1[Beeline.EducationCompass.Model.Region] GetRegions(Int32)' has no support翻译成SQL。
你会建议我如何去做这件事? 如果层次结构的每个级别都在同一个表中而不是单独的表中,会更简单(或可能)吗?
I have a hierarchy that I'd like to query with LinqToSql:
Country -> Region -> City -> ZipCode
Each entity holds both a reference to it's parent (eg. Region.Country) and a collection of it's children (eg. Region.Cities).
I'd like to eager load each entity's parent along with Countries and Regions but lazy load cities and zip codes.
To complicate things, each entity is being localized before being projected in to the model. So Country.Name changes based on the language.
Here's some snippets of what I have so far:
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);
}
The test fails with:
System.NotSupportedException: Method 'System.Linq.IQueryable`1[Beeline.EducationCompass.Model.Region] GetRegions(Int32)' has no supported translation to SQL.
How would you recommend I go about this? Would it be simpler (or possible) if each level of the hierarchy was in the same table instead of separate ones?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您将需要使用 linq 设计器来设置对象之间的关系。 这使您不必通过创建属性来编写一个又一个的连接。
您需要使用 ToList 来分隔您打算转换为 SQL 的操作和您打算在本地代码中完成的操作。 如果您不这样做,您将不断看到那些“无法将您的方法转换为 SQL”的异常。
您还需要使用 DataLoadOptions 在某些情况下急切地加载这些属性。 这是我的尝试。
You're going to want to use the linq designer to set up relationships between your objects. This gets you out of writing join after join after join by creating properties.
You're going to want to use ToList to seperate those operations you intend to be translated into SQL, and those operations you intend to be done in local code. If you don't do this, you'll keep seeing those "cannot translate your method into SQL" exceptions.
You're also going to want to use DataLoadOptions to eagerly load these properties in some cases. Here's my stab at it.
这是一段粘性代码,如果其他人有的话,由于缺乏相关技能,我不会回答这个问题,但由于您没有回复......
我可以告诉您错误消息的含义。 这意味着函数 GetRegions 无法由 linq to sql 提供程序转换为 sql。 一些内置函数可以,因为提供者理解它们,这里有一个 列表。 否则,您可以提供翻译,请参阅此处。
在您的情况下,您需要“内联”此查询的逻辑,逻辑不会跨越函数调用的边界,因为您正在处理表达式树,sql 服务器无法回调到您的 GetRegions 方法。
至于具体的方法,你得去尝试一下,我现在没时间强迫你。 (除非其他人有时间和技能?)
祝你好运。
That's one sticky piece of code, and I wouldn't have answered this due to lack of relevant skill if anyone else had, but since you had no responses...
I can tell you what the error message means. It means the function GetRegions can't be translated into sql by the linq to sql provider. Some built-in functions can be, because the provider understands them, here is a list. Otherwise you can provide translations see here.
In your situation you need to 'inline' the logic of this query, the logic won't cross the boundary of a function call, because you are dealing with an expression tree, the sql server can't call back into your GetRegions method.
As to the exact way to do that, you'll have to have a go, I don't have the time to oblige you at the moment. (Unless someone else has time and skill?)
Good luck.