问题
Hello I have one Class named Notifications which is a child class for the User.
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
public ICollection<UserNotification> UserNotifications { get; set; }
}
public class Notification
{
public int Id { get; set; }
public ICollection<UserNotification> UserNotifications { get; set; }
public string Title { get; set; }
public string Message { get; set; }
public bool IsRead { get; set; }
public DateTime CreatedDate { get; set; }
}
public class UserNotification
{
public User User { get; set; }
public Notification Notification { get; set; }
}
Now I want to get the User By ID which will bring all the notifications for the current user.
var user = NhSession.Get<User>(userId);
But I don't want to get all the notifications. I just want to get the user with unread notifications and just want to get top 5 (Latest) notifications
for the user.
I tried to achieve that by joinQueryOver but I was not able to do that. Can anyone please suggest to get this working.
回答1:
Based on the latest update and new Entity(ies) structure, we can now profit from Pairing object, and quickly select Users which has unread Notificaitons like this
Find users who have not read notifications
var session = NHSession.GetCurrent();
Notification notification = null;
UserNotification pair = null;
User user = null;
var subquery = QueryOver.Of<UserNotification>(() => pair)
// this will give us access to notification
// see we can filter only these which are NOT read
.JoinQueryOver(() => pair.Notification, () => notification)
// here is the filter
.Where(() => !notification.IsRead)
// now the trick to take only related to our user
.Where(() => pair.User.Id == user.Id)
// and get the user Id
.Select(x => pair.User.Id);
var listOfUsers = session.QueryOver<User>(() => user)
.WithSubquery
.WhereProperty(() => user.Id)
.In(subquery)
// paging
.Take(10)
.Skip(10)
.List<User>();
Find 5 unread notifications per userId
var userId = 1;
var subqueryByUser = QueryOver.Of<UserNotification>(() => pair)
// now we do not need any kind of a join
// just have to filter these pairs related to user
.Where(() => pair.User.Id == userId)
// and get the notification Id
.Select(x => pair.Notification.Id);
var notificationsPerUser = session.QueryOver<Notification>(() => notification)
.WithSubquery
.WhereProperty(() => notification.Id)
.In(subqueryByUser)
.Where(() => !notification.IsRead)
// if needed we can order
// .OrderBy(...
.Take(5)
.List<Notification>()
回答2:
The session.Get<TEntity>(entityId)
is there for us to load the Entity AS IS mapped. That's the contract.
If we want to get filtered results, we have to use another contract to recive the data: Session.CreateCriteria()
(or any other querying API i.e. QueryOver()
)
So in our case, we should build the query to find user with unread notifications:
Occupation Notification= null;
User user = null;
var subquery = QueryOver.Of<Notification>(() => notification)
.Where(() => !notification.IsRead )
// just related to the user, from outer query
.Where(() => notification.User.ID == user.ID)
.Select(x => notification.User.ID);
var list = session.QueryOver<User>(() => user)
.WithSubquery
.WhereProperty(() => user.ID)
.In(subquery)
// paging
.Take(10)
.Skip(10)
.List<User>();
What we can see here, is expectations (well in fact a MUST) that notification has back reference to its parent, to user:
public class Notification
{
...
public User User {get;set;}
}
But that should not be an issue, it is just a mapping, not change in DB
Similar query (on top of Notification) we can use to get only first 5 of them:
var notifications = session.QueryOver<Notification>(() => notification)
// here is a userId for a specific user.
// we can use IN() to load for more of them
.Where(() => notification.User.ID != userId)
.Take(5)
.List<Notification>()
;
来源:https://stackoverflow.com/questions/27478172/how-to-do-a-queryover-in-nhibernate-on-child-collection