Запрос по ссылке на HasMany

У меня есть такая модель сущности:

public class Request
{
    public virtual IList<Response> Responses { get; set; }
}

public class Response
{
    public virtual DateTime Timestamp { get; set; }
    public virtual bool Success { get; set; }
}

Я пытаюсь создать запрос, который выдаст мне все запросы, где его последний ответ (относительно его метки времени) является успешным. Как это может быть сделано?

2 ответа

Решение

Как почти всегда, у NHibernate есть ответ на этот вопрос. То, чего мы здесь пытаемся достичь, - это выражение SQL, похожее на это:

// final Request selection
SELECT request.[RequestId] 
 FROM [Request] request 

   // Only requests, which are successful, and have Max(date)
   WHERE request.[RequestId] IN 
   (
     SELECT successResponse.RequestId as y0_ 
      FROM [Response] successResponse 

        // response which max date is equal to the upper response
        // and which RequestId corresponds with supper upper Request
        WHERE EXISTS
        (
          SELECT maxResponse.RequestId as y0_
           , max(maxResponse.[DateTime]) as y1_           
           FROM [Response] maxResponse 

           // do the MAX only for current Request
           WHERE maxResponse.RequestId = successResponse.RequestId 
           GROUP BY maxResponse.RequestId 

           // assure that the Response match is on the max DateTime
           HAVING max(maxResponse.[DateTime]) = successResponse.[DateTime]
        ) 
        AND successResponse.[Success] = 1
   )

Заметки:

  1. Ожидая ответа , есть RequestId
  2. выше был использован C# // комментарий вместо SQL --

А теперь магия NHibernate и QueryOver:

// This declaration will allow us, to use a reference from middle SELECT
// in the most deeper SELECT
Response response = null;

// the most INNER SELECT
var maxSubquery = QueryOver.Of<Response>()
   .SelectList(l => l
    .SelectGroup(item => item.RequestId)
    .SelectMax(item => item.DateTime)
    )
    // WHERE Clause
   .Where(item => item.RequestId == response.RequestId)
   // HAVING Clause
   .Where(Restrictions.EqProperty(
      Projections.Max<Response>(item => item.DateTime),
      Projections.Property(() => response.DateTime)
    ));

// the middle SELECT
var successSubquery = QueryOver.Of<Response>(() => response)
    // to filter the Request
    .Select(res => res.RequestId)
    .WithSubquery
    .WhereExists(maxSubquery)
    // now only these wich are successful
    .Where(success => success.Success == true)
    ;

В этот момент нам нужно внутреннее SUB SELECT, вложенное. давайте использовать их:

// the most outer SELECT
var query = session.QueryOver<Request>();
query.WithSubquery
    // our Request ID is IN(...
    .WhereProperty(r => r.ID)
    .In(successSubquery);

var list = query
    .List<Request>();

Заключительные замечания, я не обсуждаю концепцию. Не производительность. Я бы скорее использовал настройку ответа "IsActive" и упростил бы... это просто ответ, как это сделать...

Я возьму удар в этом, вот некоторые linq (используя Query вместо).

session.Query<Request>()
    .Where(request => 
        request.Responses.Count() > 0 && 
        request.Responses.OrderByDescending(response => response.Timestamp)
                         .First()
                         .Success);

Не знаю, работает ли это.

Другие вопросы по тегам