简体   繁体   中英

LINQ to NHibernate join - Query latest N per group - NotSupportedException

I'm trying to get the last message sent to each customer. But I'm getting a NotSupportedException without any additional details. I'm unfamiliar with the join method of LINQ to NHibernate since this is the first time using it. Can someone please explain what is wrong with my query and why I'm getting this error? Here is my query and the error:

var messages = _session.Query<Communication>();     

return messages.Join(
                _session.Query<Communication>().GroupBy(m => m.Customer),
                x => new { x.Customer, x.Message.CreatedOn },
                g => new { Customer= g.Key, CreatedOn = g.Max(p => p.Message.CreatedOn) },
                (x, g) => x)
                .ToList();

System.NotSupportedException: query ( query ( select_from ( from ( range App.Core.Customer m ) ) ( select m ) ) )

Entities:

public class Communication
{
    public Message Message { get; set; }
    public Customer Customer { get; set; }
    ...
}

public class Message
{
    public DateTime CreatedOn { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
    ...
}

Can someone please explain what is wrong with my query and why I'm getting this error?

NHibernate doesn't support joins on sub-query. That gives you NotSupportedException .

It also has some issues with group by sub-queries (see for details How to query the first entry in each group in NHibernate ). But using last technique described in this answer you can rewrite your query to something like:

var results = session.Query<Communication>()
        .Where(c => c == session.Query<Communication>()
                            .Where(cs => cs.Customer == c.Customer)
                            .OrderByDescending(cs => cs.Message.CreatedOn)
                            .First()
        ).ToList();

I'm assuming a Communication is like a thread of multiple messages, and CustomerId exists at the Communication level, and then messages have a CommunicationId and a CreatedOn. I don't claim to be able to provide the nHibernate/LINQ syntax but you could perhaps do it with raw SQL:

var m = _session.CreateSQLQuery(@"

  SELECT m.Id, m.CommunicationId, m.CreatedOn, m.<put a list of all other columns in messages here>
  FROM
    (SELECT *, ROW_NUMBER() OVER(PARTITION BY CommunicationId ORDER BY CreatedOn DESC) rown FROM Messages) m 
  WHERE m.rown = 1")
.AddEntity(typeof(Message))
.List<Message>();

You'll need to fill in the <... > with all the other columns in Message. I think (I've only looked at posts on the web about how to run raw SQL in nH) that this will select all the latest Messages per Communication.. And maybe then crawling round the object tree would be like m.First().Communication.Customer.Name etc

ie Hopefully from these you can join/navigate onto the Communications, get the Customer etc. I don't know enough about raw querying nH to know how to get it to form an object graph that includes the Communication in one hit, but you could use this query:

  SELECT d.*
  FROM
    (SELECT *, ROW_NUMBER() OVER(PARTITION BY c.CustomerId ORDER BY CreatedOn DESC) rown FROM Messages m INNER JOIN Communication c ON m.CommunicationId = c.Id) d 
  WHERE d.rown = 1

To get all the latest messages per customer (subtly different than latest message per comm, if one customer has two comms concurrent), and combine it with the advice here to make the results a dynamic

Maybe this is what you want to do?

return _session
          .Query<Communication>()
          .GroupBy(e => e.Customer)
          .Select(g => new {Customer = g.Key, MaxDate = g.Max(r => r.Message.CreatedOn)})
          .ToList()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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