使用 LINQ 和 SubSonic 进行对象映射
我正在使用 SubSonic 3.0.0.3 ActiveRecord 构建一个小项目,但遇到了一个似乎无法解决的问题。
下面是 LINQ 查询:
var result = from r in Release.All()
let i = Install.All().Count(x => x.ReleaseId == r.Id)
where r.ProductId == productId
select new ReleaseInfo
{
NumberOfInstalls = i,
Release = new Release
{
Id = r.Id,
ProductId = r.ProductId,
ReleaseNumber = r.ReleaseNumber,
RevisionNumber = r.RevisionNumber,
ReleaseDate = r.ReleaseDate,
ReleasedBy = r.ReleasedBy
}
};
ReleaseInfo 对象是一个自定义类,如下所示:
public class ReleaseInfo
{
public Release Release { get; set; }
public int NumberOfInstalls { get; set; }
}
Release 和 Install 是由 SubSonic 生成的类。
当我对结果进行监视时,Release 属性为 null。
如果我使它成为一个更简单的查询并观察结果,则该值不为空。
var result = from r in Release.All()
let i = Install.All().Count(x => x.ReleaseId == r.Id)
where r.ProductId == productId
select new Release
{
Id = r.Id,
ProductId = r.ProductId,
ReleaseNumber = r.ReleaseNumber,
RevisionNumber = r.RevisionNumber,
ReleaseDate = r.ReleaseDate,
ReleasedBy = r.ReleasedBy
};
这是我的 LINQ 查询的问题还是 SubSonic 的限制?
I'm building a small project with SubSonic 3.0.0.3 ActiveRecord and I'm running into an issue I can't seem to get past.
Here is the LINQ query:
var result = from r in Release.All()
let i = Install.All().Count(x => x.ReleaseId == r.Id)
where r.ProductId == productId
select new ReleaseInfo
{
NumberOfInstalls = i,
Release = new Release
{
Id = r.Id,
ProductId = r.ProductId,
ReleaseNumber = r.ReleaseNumber,
RevisionNumber = r.RevisionNumber,
ReleaseDate = r.ReleaseDate,
ReleasedBy = r.ReleasedBy
}
};
The ReleaseInfo object is a custom class and looks like this:
public class ReleaseInfo
{
public Release Release { get; set; }
public int NumberOfInstalls { get; set; }
}
Release and Install are classes generated by SubSonic.
When I do a watch on result, the Release property is null.
If I make this a simpler query and watch result, the value is not null.
var result = from r in Release.All()
let i = Install.All().Count(x => x.ReleaseId == r.Id)
where r.ProductId == productId
select new Release
{
Id = r.Id,
ProductId = r.ProductId,
ReleaseNumber = r.ReleaseNumber,
RevisionNumber = r.RevisionNumber,
ReleaseDate = r.ReleaseDate,
ReleasedBy = r.ReleasedBy
};
Is this an issue with my LINQ query or a limitation of SubSonic?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我认为问题可能在于您本质上是在重复 ORM 的功能。要理解的关键是这一行:
该行返回数据库中每个项目的完全填充发布记录列表。永远不需要在查询中的其他任何地方更新版本 - 只需返回 SubSonic 已为您填充的版本即可!
使用此逻辑,您应该能够执行以下操作:
也就是说,您应该查看 Install.All() 调用,因为这可能效率极低。它将执行的操作是从数据库中提取每个安装,将这些安装合并到对象中,然后比较 .NET 中每个记录的 ID 以检查该记录是否满足该条件。您可以使用 SubSonic 中的 .Find 方法仅返回数据库层的某些记录,这将显着提高性能。即使如此,膨胀对象可能仍然很昂贵,您可能需要在此处考虑视图或存储过程。但作为简单的第一步,以下内容应该有效:
I think the issue might be that you're essentially duplicating the functionality of the ORM. The key thing to understand is this line:
This line returns a list of fully-populated Release records for every item in your database. There should never be a need to new up a release anywhere else in your query - just return the ones that SubSonic has already populated for you!
Using this logic, you should be able to do the following:
That being said, you should look at the Install.All() call, because that's likely to be tremendously inefficient. What that will do is pull every install from the database, hydrate those installs into objects, and then compare the id of every record in .NET to check if the record satisfies that condition. You can use the .Find method in SubSonic to only return certain records at the database tier, which should help performance significantly. Even still, inflating objects may still be expensive and you might want to consider a view or stored procedure here. But as a simple first step, the following should work:
我想我已经找到了这个问题的实际答案。我一直在翻阅 SubSonic 源代码,发现将数据读取器映射到对象时使用两种类型的对象投影:一种用于匿名类型和分组,另一种用于其他所有内容:
这是一个片段:第 269 行 - SubSonic.Linq.Structure.DbQueryProvider 的 298
事实证明,SubSonic ToEnumerable 尝试将数据读取器中的列名称与您尝试投影到的对象中的属性相匹配。我的 Linq 中的 SQL 查询如下所示:
请注意,[t0].[c0] 与我的属性名称 NumberOfInstalls 不同。所以 c0 的值永远不会被投影到我的对象中。
修复:
您可以简单地取出 if 语句并使用慢 10 倍的投影,一切都会正常。
I think I've found the actual answer to this problem. I've been rummaging around in the SubSonic source and found that there are two types of object projection that are used when mapping the datareader to objects: one for anonymous types and groupings and one for everything else:
Here is a snippet: Line 269 - 298 of SubSonic.Linq.Structure.DbQueryProvider
Turns out that the SubSonic ToEnumerable tries to match the column names in the datareader to the properties in the object you're trying to project to. The SQL Query from my Linq looks like this:
Notice the [t0].[c0] is not the same as my property name NumberOfInstalls. So the value of c0 never gets projected into my object.
THE FIX:
You can simply take out the if statement and use the 10x slower projection and everything will work.
我们有一个预测错误,在某些情况下会出错 - 我认为它已经被修复,但我需要对其进行更多测试。我邀请您尝试最新的部分 - 我想我们已经修复了它...很抱歉这么含糊,但在 3.0.0.1 和 3.0.0.3 之间有一个错误,但我一直无法找到它。
We have a bug with projections that trips on certain occassions - I think it's been patched but I need to test it more. I invite you to try the latest bits - I think we've fixed it... sorry to be so vague but a bug worked it's way in between 3.0.0.1 and 3.0.0.3 and I haven't been able to find it.
这个问题在 3.0.0.4 中已经修复了吗?我很生气找到这个帖子。经过 2 天的尝试找出为什么我的预测不起作用 - 除非属性名称与查询完全匹配 - 我最终来到了这里。
我是如此依赖 SS SimpleRepository,现在回头已经太晚了。像这样的错误是严重的。有机会解决吗?
我现在走了 10 倍慢的路线,所以我至少可以发布给我的客户。更喜欢更快的方法来正确工作:)
Has this been fixed in 3.0.0.4? I was so peeved to find this post. After 2 days of trying to figure out why my projections were not working - except when the property names matched the query exactly - I ended up here.
I am so dependant on SS SimpleRepository that it is too late to turn back now. A bug like this is crippling. Any chance it is sorted out?
I went the 10x slower route for now so I can at least release to my client. Would much prefer the faster method to work correctly :)