[英]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,並折疊重復的用戶數據每個字符項?
您試圖投影到兩種不同的對象類型( UserDto
和CharacterDto
)。 我認為不支持。 如果您看一下代碼,便告訴NHibernate他應該使用UseDto
( Transformers.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.