Linq2Sql - 如何延迟加载嵌套列表?
如何在不实际执行查询的情况下延迟加载嵌套列表? 使用一个非常基本的示例,假设我有:
class CityBlock {
IList<Building> BuildingsOnBlock;
Person BlockOwner;
}
class Building {
IList<Floor> FloorsInBuilding;
}
class Floor {
IList<Cubicle> EmployeeCubicles;
}
Class Cubicle {
System.Guid CubicleID;
Person CubicleOccupant;
}
然后在我的存储库层中,我有以下方法:
GetCityBlocks()
然后在服务层中,我将有 GetCityBlocksByOwner,其中我利用扩展方法来获取特定的城市街区假设我们只想要 Guido 的块:
GetCityBlocks().ForOwner("Guido")
如果我们在存储库中执行 .ToList() ,它将执行查询 - 这将是荒谬的,因为我们不知道我们在该级别获得的块是谁。 那么问题是,我们如何有效地做到这一点?
假设有 50000 个街区所有者,以及大约 1000000 个城市街区,加载所有街区并不是一种选择。 由于嵌套,使用 IQueryables 不起作用(没有极端的黑客攻击,至少我知道)。 另外,如果我尝试使用 Rob Conery 的 LazyList 之类的东西,那么我们基本上会从 DAL 泄漏到我们的域模型中,这在将来可能会非常糟糕。
那么我该如何正确地做到这一点呢?
- 这是一个确定问题 正确的上下文? 如果是这样,我们会这样做吗 这在存储库层中,或者 服务层?
- 我是否将服务层和存储库层一半融合在一起以获得 非常具体的服务方式?
- 或者我完全错过了一些东西? (对于 Linq2Sql 来说仍然相对较新 正在被淘汰的东西 无论如何......)
编辑: 在存储库模式中,我们当前正在映射到我们的域对象,因此它看起来像这样:
public IQueryable<CityBlock> GetCityBlocks(){
var results = from o in db.city_blocks
let buildings = GetBuildingsOnBlock(o.block_id)
select new CityBlock {
BuildingsOnBlock = buildings,
BlockOwner = o.block_owner
};
return results;
}
为了使其工作,我们必须使建筑物获得 .ToList(),除非我们在中创建实际字段CityBlock 对象是一个 IQueryable - 这似乎不对,因为似乎会向访问 CityBlock.BuildingsOnBlock 字段的任何人授予太多权力。 这种到领域对象的映射是我们应该在服务层中完成的吗?
How do you lazy load nested lists without actually executing the query? Using a very basic example, say I have:
class CityBlock {
IList<Building> BuildingsOnBlock;
Person BlockOwner;
}
class Building {
IList<Floor> FloorsInBuilding;
}
class Floor {
IList<Cubicle> EmployeeCubicles;
}
Class Cubicle {
System.Guid CubicleID;
Person CubicleOccupant;
}
And then in my repository layer, I have the following method:
GetCityBlocks()
And then in the Service layer I'll have GetCityBlocksByOwner, where I utilize an extension method to get the city blocks owned by a specific person, say we just want Guido's blocks:
GetCityBlocks().ForOwner("Guido")
If we do a .ToList() in the repository it's going to execute the queries - that's going to be ridiculous since we don't know who's blocks we're getting at that level. So the question is, how do we do this efficiently?
Let's assume there are 50000 block owners, and some 1000000 city blocks, loading all of them is not an option. Using IQueryables won't work due to the nesting (without extreme hacking, at least that I'm aware of). Also, if I try to use something like Rob Conery's LazyList, then we essentially have a leak from our DAL into our domain models, which could be very very bad in the future.
So how do I go about doing this correctly?
- Is it a matter of determining the
correct context? If so, would we do
this in the Repository Layer, or the
Service layer? - Do I half meld together the Service layer and my Repository layer to get
very specific service methods? - Or am I missing something entirely? (still relatively new to the Linq2Sql
thing, which is being phased out
anyways so...)
Edit:
In the repository pattern, we're currently mapping to our domain objects, so it would look something like this:
public IQueryable<CityBlock> GetCityBlocks(){
var results = from o in db.city_blocks
let buildings = GetBuildingsOnBlock(o.block_id)
select new CityBlock {
BuildingsOnBlock = buildings,
BlockOwner = o.block_owner
};
return results;
}
For this to work, we'd have to make the buildings get a .ToList(), unless we make that actual field in the CityBlock object an IQueryable - which doesn't seem right, because it seems as if too much power would be granted to anyone who accesses the CityBlock.BuildingsOnBlock field. Is this mapping to our domain objects something we should maybe do up in the service layer?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以通过返回 IQueryables 而不是 IList 来完成此操作。
ToList() 导致查询立即执行,因为必须执行从 IQueryable 到 IList 的转换。
只要您返回 IQueryables,延迟加载就应该推迟执行,直到实际需要数据时,即调用 ToList() 时。
我目前找不到参考资料,但据我了解,如果这样做,linq to sql就有机会优化它发送到服务器的SQL。 换句话说,它最终会读取这些记录:
而不是这些记录:
You do it by returning IQueryables instead of ILists.
ToList() causes the queries to execute immediately, because a conversion must be performed from IQueryable to IList.
As long as you are returning IQueryables lazy loading should defer execution until the data is actually needed, i.e. when ToList() is called.
I can't find a reference at the moment, but it is my understanding that, if you do it this way, linq to sql has the opportunity to optimize the SQL it sends to the server. In other words, it will ultimately read these records:
rather than these records:
您可以尝试使用不同的方法来映射域对象以使其正常工作。 问题是,无论您做什么(除非您将域对象中的列表更改为 IQueryables),您最终都会在映射时进行 ToList() 操作。
另一种方法是让 linq2Sql 通过创建自定义数据上下文来映射到 POCO,而不是使用映射设计器:),这样可以保持域模型干净,并让 linq2Sql 在正确的时间填充依赖项。 请注意,进入这条路线有其自身的问题,但它是可以完成的。
这是一个让您开始这条路线的链接
在 Linq to SQL 中实现 POCO
You could try a different approach in mapping your domain objects to make it work. The problem is that no matter what you do (unless you change your lists to IQueryables in your domain objects) you will end up ToList()ing while you're mapping.
The other way it to let linq2Sql do the mapping to your POCOs by creating a custom datacontext and not using the designer for the mapping :), that way you keep your domain model clean and let linq2Sql populate the dependencies at the correct time. Note that going into this route has its own problems, but it can be done.
Here's a link to get you started on this route
Achieving POCO s in Linq to SQL