我正在玩Subsonic 3.0 SimpleRepository并尝试使用一个linq查询获取菜单和菜单项,但是菜单项总是空的
菜单
public class Menu
{
public Menu()
{
MenuId = 0;
MenuName = "";
MenuItems = null;
}
public int MenuId { get; set; }
public string MenuName { get; set; }
public MenuItem MenuItems { get; set; }
}
菜单项
public class MenuItem
{
public MenuItem()
{
MenuItemId = 0;
MenuId = 0;
MenuItemName = "";
}
public int MenuItemId { get; set; }
public int MenuId { get; set; }
public string MenuItemName { get; set; }
}
Linq查询
var menus = from m in _repo.All<Menu>()
from mi in _repo.All<MenuItem>()
where m.MenuItems.MenuItemId == mi.MenuItemId
select new Menu
{
MenuId = m.MenuId,
MenuName = m.MenuName,
MenuItems = {
MenuItemId = mi.MenuItemId,
MenuItemName = mi.MenuItemName
}
};
有人可以告诉我这里我做错了什么吗?
答案 0 :(得分:2)
我想我找到了这个问题的实际答案。我一直在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较慢的投影,一切都会有效。
答案 1 :(得分:1)
我认为你在这里做错了什么。这似乎是Subsonic 3.0的一个问题。我现在有一个问题,我没有得到here的答案。我最近也尝试过更简单的东西。但这也不起作用。
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,
Id = r.Id,
ProductId = r.ProductId,
ReleaseNumber = r.ReleaseNumber,
RevisionNumber = r.RevisionNumber,
ReleaseDate = r.ReleaseDate,
ReleasedBy = r.ReleasedBy
};
安装数量属性未填充,但如果我映射到匿名类型,一切正常:
var result = from r in Release.All()
let i = Install.All().Count(x => x.ReleaseId == r.Id)
where r.ProductId == productId
select new
{
NumberOfInstalls = i,
Id = r.Id,
ProductId = r.ProductId,
ReleaseNumber = r.ReleaseNumber,
RevisionNumber = r.RevisionNumber,
ReleaseDate = r.ReleaseDate,
ReleasedBy = r.ReleasedBy
};
如果您将代码更改为以下代码,则可能会有效:
var menus = from m in _repo.All<Menu>()
from mi in _repo.All<MenuItem>()
where m.MenuItems.MenuItemId == mi.MenuItemId
select new
{
MenuId = m.MenuId,
MenuName = m.MenuName,
MenuItems = new {
MenuItemId = mi.MenuItemId,
MenuItemName = mi.MenuItemName
}
};
由于您希望映射回预定义的对象类型,因此这种方法失败了。也许我们可以从Rob那里得到答案? :)