Fluent NHibernate: как я могу записать этот запрос в качестве критерия?

#sql #fluent-nhibernate

#sql #fluent-nhibernate

Вопрос:

Структура данных следующая: в доме много комнат. В каждой комнате много людей.

Что я хочу сделать, так это получить всех людей для дома. В обычном SQL я бы написал следующее:

 SELECT * FROM Person WHERE Room_id
IN
(SELECT Id FROM Room WHERE House_id = 1)
  

Как я могу написать это в свободном коде NHibernate?

Для этого примера мы можем предположить, что сущности и сопоставления выглядят следующим образом:

Объект House

 public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IEnumerable<Room> Rooms { get; set; }
  

Сопоставление домов

 Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Rooms);
  

Объект Room

 public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual House House { get; set; }
public virtual IEnumerable<Person> Persons { get; set; }
  

Сопоставление комнат

 Id(x => x.Id);
Map(x => x.Name);
References(x => x.House);
HasMany(x => x.Persons);
  

Person сущность

 public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Room Room { get; set; }
  

Сопоставление персон

 Id(x => x.Id);
Map(x => x.Name);
References(x => x.Room);
  

Комментарии:

1. Это не проблема fluent — fluent предназначен только для указания сопоставлений и конфигурации. В зависимости от используемой вами версии NHibernate у вас есть выбор HQL, Criteria API или (если NH3) Linq.

2. Пожалуйста, не могли бы вы показать свои сущности и отображение?

3. @Jakub: Готово. @UpTheCreek: Какое решение будет выглядеть лучше всего? Можете ли вы привести мне пример?

4. @Awesome — какую версию NHibernate вы используете? Linq, безусловно, самый понятный метод для этого.

5. @Awesome — тогда я бы лично выбрал Linq, когда у вас есть стандартный запрос, который вам нужно написать, и вас не волнует, как именно будет выглядеть SQL. Приведенный ниже ответ Якуба на ICriteria все же хорош.

Ответ №1:

Чтобы приблизить SQL-запрос к вашему, вы можете использовать эти критерии:

 var subCriteria = DetachedCriteria.For<Room>(); // subquery
subCriteria.Add(Expression.Eq("House", house)); // where clause in subquery
subCriteria.SetProjection(Projections.Id()); // DetachedCriteria needs to have a projection, id of Room is projected here

var criteria = session.CreateCriteria<Person>();
criteria.Add(Subqueries.PropertyIn("Room", subCriteria)); // in operator to search in detached criteria
var result = criteria.List<Person>();
  

Это приводит к чему-то вроде этого:

 SELECT this_.Id as Id4_0_, this_.Name as Name4_0_, this_.RoomId as RoomId4_0_
FROM [Person] this_
WHERE this_.RoomId in (SELECT this_0_.Id as y0_ FROM [Room] this_0_ WHERE this_0_.HouseId = @p0)',N'@p0 int',@p0=1
  

Я тестировал его в FNH1.2 и NH3.1, но он должен хорошо работать и в NH2.1.

РЕДАКТИРОВАТЬ: UpTheCreek правильный. Linq более понятен, чем Criterions API. Например:

 var query = session.Query<Person>().Where(x => x.Room.House == house);
var linqResult = query.ToList<Person>();
  

который выдает другой SQL-запрос, но результирующий набор тот же:

 select person0_.Id as Id4_, person0_.Name as Name4_, person0_.Room_id as Room3_4_
from [Person] person0_, [Room] room1_
where person0_.Room_id=room1_.Id and room1_.House_id=2
  

Комментарии:

1. Я рад, что это помогает. Но UpTheCreek прав. Linq более понятен, чем criterions API, который я лично ненавижу :). С другой стороны, я осторожно отношусь к NH Linq по историческим причинам — реализация Linq в NH2.1 была не очень удобной. Но судите сами (отредактированный пост).