繁体   English   中英

使用投影将QueryOver中的集合加入

[英]Join collection in QueryOver with projections

我想使用其Characters集合(也是简短版本)来检索简短User版本的列表。 我将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,...)。 每个条目都具有正确的昵称和字符类ID,但错误的xp(代替用户ID)和字符集合(== null)。

如何在不使用hql或不对字符进行单独查询的情况下获取正确的数据?

更新

我删除了转换器并注释掉Projections.Property(() => userAlias.Characters)... ,现在它返回正确的结果,但是有一种方法可以像AliasToBeanTransformer一样将它们转换为投影的dto,并折叠重复的用户数据每个字符项?

您试图投影到两种不同的对象类型( UserDtoCharacterDto )。 我认为不支持。 如果您看一下代码,便告诉NHibernate他应该使用UseDtoTransformers.AliasToBean<UserDto>() ),但您不会告诉我有关CharacterDto

参见例如http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/

您无法填充集合(例如,如果您有一个带有ProductID的类和一个ProductReviews的集合,则无法使用AliasToBean一步完成此操作)

你可以做什么:

添加一个包含所有内容的类( 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();
    }
}

(请注意,由于要使用let关键字,因此我不使用LINQ的功能语法)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM