我正在使用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; }
}
发布和安装是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的限制?
答案 0 :(得分:1)
我认为问题可能是您基本上复制了ORM的功能。要理解的关键是这一行:
from r in Release.All()
此行返回数据库中每个项目的完全填充版本记录的列表。永远不需要在查询中的任何其他位置新建一个版本 - 只需返回SubSonic已为您填充的版本!
使用此逻辑,您应该能够执行以下操作:
var result = from r in Release.All()
select new ReleaseInfo {
Release = r,
NumberOfInstalls = Install.All().Count(x => x.ReleaseId == r.Id)
};
话虽如此,你应该看一下Install.All()调用,因为这可能会非常低效。这样做是从数据库中提取每个安装,将这些安装水合成对象,然后比较.NET中每个记录的id以检查记录是否满足该条件。您可以使用SubSonic中的.Find方法仅返回数据库层的某些记录,这应该有助于显着提高性能。即便如此,膨胀对象可能仍然很昂贵,您可能需要在此处考虑视图或存储过程。但作为简单的第一步,以下内容应该有效:
var result = from r in Release.All()
select new ReleaseInfo {
Release = r,
NumberOfInstalls = Install.Find(x => x.ReleaseId == r.Id).Count()
};
答案 1 :(得分:1)
我想我找到了这个问题的实际答案。我一直在SubSonic源代码中翻找,发现在将datareader映射到对象时有两种类型的对象投影:一种用于匿名类型和分组,另一种用于其他一切:
这是一个片段:SubSonic.Linq.Structure.DbQueryProvider的第269 - 298行
IEnumerable<T> result;
Type type = typeof (T);
//this is so hacky - the issue is that the Projector below uses Expression.Convert, which is a bottleneck
//it's about 10x slower than our ToEnumerable. Our ToEnumerable, however, stumbles on Anon types and groupings
//since it doesn't know how to instantiate them (I tried - not smart enough). So we do some trickery here.
if (type.Name.Contains("AnonymousType") || type.Name.StartsWith("Grouping`") || type.FullName.StartsWith("System.")) {
var reader = _provider.ExecuteReader(cmd);
result = Project(reader, query.Projector);
} else
{
using (var reader = _provider.ExecuteReader(cmd))
{
//use our reader stuff
//thanks to Pascal LaCroix for the help here...
var resultType = typeof (T);
if (resultType.IsValueType)
{
result = reader.ToEnumerableValueType<T>();
}
else
{
result = reader.ToEnumerable<T>();
}
}
}
return result;
事实证明,SubSonic ToEnumerable会尝试将datareader中的列名与您尝试投影到的对象中的属性相匹配。我的Linq的SQL查询如下所示:
SELECT [t0].[Id], [t0].[ProductId], [t0].[ReleaseDate], [t0].[ReleasedBy], [t0].[ReleaseNumber], [t0].[RevisionNumber], [t0].[c0]
FROM (
SELECT [t1].[Id], [t1].[ProductId], [t1].[ReleaseDate], [t1].[ReleasedBy], [t1].[ReleaseNumber], [t1].[RevisionNumber], (
SELECT COUNT(*)
FROM [dbo].[Install] AS t2
WHERE ([t2].[ReleaseId] = [t1].[Id])
) AS c0
FROM [dbo].[Release] AS t1
) AS t0
WHERE ([t0].[ProductId] = 2)
注意[t0]。[c0]与我的属性名称NumberOfInstalls不同。所以c0的值永远不会被投射到我的对象中。
固定: 您可以简单地取出if语句并使用10x较慢的投影,一切都会有效。
答案 2 :(得分:0)
我们有一个错误的预测,在某些场合绊倒 - 我认为它已被修补,但我需要更多测试。我邀请你尝试最新的一些 - 我认为我们已经修好了...抱歉这么模糊,但是在3.0.0.1和3.0.0.3之间有一个错误,我无法找到它。
答案 3 :(得分:-1)
这是否已在3.0.0.4中修复?我非常恼火地发现这篇文章。经过2天试图弄清楚为什么我的预测不起作用 - 除非属性名称与查询完全匹配 - 我最终在这里。 我如此依赖SS SimpleRepository,现在回头已经太晚了。像这样的bug是致命的。有没有机会整理出来?
我现在走了10倍慢的路线所以我至少可以释放给我的客户。更喜欢更快的方法才能正常工作:)