使用投影加入 QueryOver 中的集合
Join collection in QueryOver with projections
我想检索短 User
版本的列表及其 Characters
集合(也是短版本)。我将 QueryOver 与投影一起使用。
public class User
{
public virtual int Id { get; set; }
public virtual string Nickname { get; set; }
public virtual ISet<Character> Characters { get; set; }
// and 30 other properties
}
public class Character
{
public virtual int Id { get; set; }
public virtual int XP { get; set; }
public virtual int UserId { get; set; }
public virtual int ClassId { get; set; }
// and 30 other properties
}
public class UserDto // short version
{
public virtual string Nickname { get; set; }
public virtual ISet<CharacterDto> Characters { get; set; }
}
public class CharacterDto // short version
{
public virtual int XP { get; set; }
public virtual int ClassId { get; set; }
}
User userAlias = null;
UserDto userDto = null;
Character characterAlias = null;
CharacterDto characterDto = null;
var result = session.QueryOver<User>(() => userAlias)
.Where(Restrictions.Like(
Projections.SqlFunction("lower", NHibernateUtil.String, Projections.Property<User>(x => x.Nickname)),
nickname.ToLowerInvariant(), MatchMode.Start))
.JoinAlias(x => x.Characters, () => characterAlias, JoinType.LeftOuterJoin)
.Select(
Projections.Property(() => userAlias.Nickname).WithAlias(() => userDto.Nickname),
Projections.Property(() => userAlias.Characters).WithAlias(() => userDto.Characters),
Projections.Property(() => characterAlias.XP).WithAlias(() => characterDto.XP),
Projections.Property(() => characterAlias.ClassId).WithAlias(() => characterDto.ClassId)
)
.TransformUsing(Transformers.AliasToBean<UserDto>())
.Take(50)
.List<UserDto>();
当我 运行 这段代码抛出一个异常:Could not find a setter for property ClassId in class UserDto
。如果我删除转换器,则结果包含每个用户一个字符的条目(User1-Character1,User1-Character2,...)。每个条目都有正确的昵称和角色 class id 但错误的 xp(而不是用户 id)和角色集合(== null)。
如何在不使用 hql 或对字符进行单独查询的情况下获取正确的数据?
更新
我删除了转换器并注释掉了 Projections.Property(() => userAlias.Characters)...
,现在它 returns 了正确的结果,但是有没有办法像 AliasToBeanTransformer 那样将它们转换为投影 dto,并折叠每个重复的用户数据字符条目?
您正在尝试投影到两种不同的对象类型(UserDto
和 CharacterDto
)。我认为它不受支持。如果你看你的代码,你告诉 NHibernate 他应该使用 UseDto
(Transformers.AliasToBean<UserDto>()
) 但你没有告诉它 CharacterDto
.
参见示例 http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/ :
You cannot populate collections (e.g., if you had a class with ProductID and a collection of ProductReviews you could not do that in one step using AliasToBean)
你能做什么:
添加一个 class 包含所有内容 (UserCharacterDto
)
public class UserCharacterDto // short version
{
public virtual int Id { get; set; }
public virtual string Nickname { get; set; }
public virtual int? XP { get; set; }
public virtual int? ClassId { get; set; }
}
public class UserDto // short version
{
public virtual string Nickname { get; set; }
// Changed from ISet: you don't have a key!
public virtual IList<CharacterDto> Characters { get; set; }
}
public class CharacterDto // short version
{
public virtual int XP { get; set; }
public virtual int ClassId { get; set; }
}
首先你投影到 UserCharacterDto
,
UserCharacterDto uc = null;
IList<UserCharacterDto> result = ...
.Select(
Projections.Property(() => userAlias.Nickname).WithAlias(() => uc.Nickname),
Projections.Property(() => characterAlias.XP).WithAlias(() => uc.XP),
Projections.Property(() => characterAlias.ClassId).WithAlias(() => uc.ClassId)
)
.TransformUsing(Transformers.AliasToBean<UserDto>())
.Take(50)
.List<UserCharacterDto>()
然后你 GroupBy
最后你创建你的元素
IList<UserDto> result2 = (from x in result
// Here you group by User
group x by x.Id into y
let first = y.First()
// Here you build the "definitive" UserDto
select new UserDto {
Nickname = first.Nickname,
// and here you build the "definitive" CharacterDto
// note the handling of empty objects!
Characters = new List<CharacterDto>(y.Select(z => z.XP != null && z.ClassId != null ? new CharacterDto { XP = z.XP.Value, ClassId = z.ClassId.Value } : null))
}
).ToList();
// remove empty CharacterDto from left join
foreach (UserDto user in result2) {
if (user.Characters.Count == 1 && user.Characters[0] == null) {
user.Characters.Clear();
}
}
(请注意,我没有使用 LINQ 的函数式语法,因为我想使用 let
关键字)
我想检索短 User
版本的列表及其 Characters
集合(也是短版本)。我将 QueryOver 与投影一起使用。
public class User
{
public virtual int Id { get; set; }
public virtual string Nickname { get; set; }
public virtual ISet<Character> Characters { get; set; }
// and 30 other properties
}
public class Character
{
public virtual int Id { get; set; }
public virtual int XP { get; set; }
public virtual int UserId { get; set; }
public virtual int ClassId { get; set; }
// and 30 other properties
}
public class UserDto // short version
{
public virtual string Nickname { get; set; }
public virtual ISet<CharacterDto> Characters { get; set; }
}
public class CharacterDto // short version
{
public virtual int XP { get; set; }
public virtual int ClassId { get; set; }
}
User userAlias = null;
UserDto userDto = null;
Character characterAlias = null;
CharacterDto characterDto = null;
var result = session.QueryOver<User>(() => userAlias)
.Where(Restrictions.Like(
Projections.SqlFunction("lower", NHibernateUtil.String, Projections.Property<User>(x => x.Nickname)),
nickname.ToLowerInvariant(), MatchMode.Start))
.JoinAlias(x => x.Characters, () => characterAlias, JoinType.LeftOuterJoin)
.Select(
Projections.Property(() => userAlias.Nickname).WithAlias(() => userDto.Nickname),
Projections.Property(() => userAlias.Characters).WithAlias(() => userDto.Characters),
Projections.Property(() => characterAlias.XP).WithAlias(() => characterDto.XP),
Projections.Property(() => characterAlias.ClassId).WithAlias(() => characterDto.ClassId)
)
.TransformUsing(Transformers.AliasToBean<UserDto>())
.Take(50)
.List<UserDto>();
当我 运行 这段代码抛出一个异常:Could not find a setter for property ClassId in class UserDto
。如果我删除转换器,则结果包含每个用户一个字符的条目(User1-Character1,User1-Character2,...)。每个条目都有正确的昵称和角色 class id 但错误的 xp(而不是用户 id)和角色集合(== null)。
如何在不使用 hql 或对字符进行单独查询的情况下获取正确的数据?
更新
我删除了转换器并注释掉了 Projections.Property(() => userAlias.Characters)...
,现在它 returns 了正确的结果,但是有没有办法像 AliasToBeanTransformer 那样将它们转换为投影 dto,并折叠每个重复的用户数据字符条目?
您正在尝试投影到两种不同的对象类型(UserDto
和 CharacterDto
)。我认为它不受支持。如果你看你的代码,你告诉 NHibernate 他应该使用 UseDto
(Transformers.AliasToBean<UserDto>()
) 但你没有告诉它 CharacterDto
.
参见示例 http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/ :
You cannot populate collections (e.g., if you had a class with ProductID and a collection of ProductReviews you could not do that in one step using AliasToBean)
你能做什么:
添加一个 class 包含所有内容 (UserCharacterDto
)
public class UserCharacterDto // short version
{
public virtual int Id { get; set; }
public virtual string Nickname { get; set; }
public virtual int? XP { get; set; }
public virtual int? ClassId { get; set; }
}
public class UserDto // short version
{
public virtual string Nickname { get; set; }
// Changed from ISet: you don't have a key!
public virtual IList<CharacterDto> Characters { get; set; }
}
public class CharacterDto // short version
{
public virtual int XP { get; set; }
public virtual int ClassId { get; set; }
}
首先你投影到 UserCharacterDto
,
UserCharacterDto uc = null;
IList<UserCharacterDto> result = ...
.Select(
Projections.Property(() => userAlias.Nickname).WithAlias(() => uc.Nickname),
Projections.Property(() => characterAlias.XP).WithAlias(() => uc.XP),
Projections.Property(() => characterAlias.ClassId).WithAlias(() => uc.ClassId)
)
.TransformUsing(Transformers.AliasToBean<UserDto>())
.Take(50)
.List<UserCharacterDto>()
然后你 GroupBy
最后你创建你的元素
IList<UserDto> result2 = (from x in result
// Here you group by User
group x by x.Id into y
let first = y.First()
// Here you build the "definitive" UserDto
select new UserDto {
Nickname = first.Nickname,
// and here you build the "definitive" CharacterDto
// note the handling of empty objects!
Characters = new List<CharacterDto>(y.Select(z => z.XP != null && z.ClassId != null ? new CharacterDto { XP = z.XP.Value, ClassId = z.ClassId.Value } : null))
}
).ToList();
// remove empty CharacterDto from left join
foreach (UserDto user in result2) {
if (user.Characters.Count == 1 && user.Characters[0] == null) {
user.Characters.Clear();
}
}
(请注意,我没有使用 LINQ 的函数式语法,因为我想使用 let
关键字)