2013-12-11 2 views
5

Я модель сущностей, как это:Запрос на 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; } 
} 

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

ответ

5

Как почти всегда, у 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) 
    ; 

На данный момент мы имеем к внутреннему Выбору подфункции, вложенный. давайте их использовать:

// 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» и упростил ее ... это как раз ответ, как это сделать ...

+0

Спасибо за ваш ответ. Что делать, если я не могу ожидать, что мои ответы * всегда будут иметь * RequestId *? – dhrm

+0

Честно говоря, я вижу это так: вы добавили некоторую * бизнес-логику *, которая сможет пометить последнюю запись как «IsActive» (новый столбец). Если вы не пойдете этим путем ... тогда вы не сможете этого SQL-оператора без столбца «RequestId» - точно. Итак, если этот столбец существует ... он ** должен быть отображен в вашем объекте Reponse ** ... как отношение. Тогда вы можете использовать мой способ ... никакой другой вариант, я бы сказал;) удачи в NHibernate –

+0

Radim, можете ли вы объяснить, что здесь делает ** SelectList **? Кроме того, будет ли он работать так же, как и заказывать ответы в ** maxSubquery ** по дате начала, а затем Take (1)? – Michael

0

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

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

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

+1

Это не будет работать с QueryOver. Вы можете попробовать использовать этот запрос, используя LINQ-провайдер, но, как правило, если задействована другая таблица/сущность, вам нужно делать соединения с помощью QueryOver –

+0

@ColeW спасибо за головы. – Matthew

+0

Любое решение? Будет ли проще использовать критерии? – dhrm

Смежные вопросы