Большинство общих советов, данных Радимом Кёлером, верны, но что касается исходной проблемы формулирования формулы - NH (по крайней мере >=5, я не знаю, как обстоят дела с более старыми) должен быть в состоянии чтобы покрыть это дело.
Прежде всего, ваша попытка потерпит неудачу, потому что в формулах любые имена столбцов, ссылающиеся на что-либо, кроме текущего основного объекта, должны иметь префикс псевдонимов. В противном случае NH будет считать, что имя столбца относится к текущему основному объекту.
Например, ваша первоначальная попытка была:
Map(x => x.CityName).Formula("select Name from City c where c.street = STREET");
^ fault ^CEE ^CEE ^fault
Обратите внимание, как вы добавили префикс c
к street
, чтобы устранить неоднозначность, какой street
из города, а какой нет. NHibernate обнаружит такие префиксы и будет считать, что все столбцы с префиксами привязаны к таблицам, указанным в тексте sql. Таким образом, c.street
будет считаться неважным. Однако STREET
и Name
не имеют префиксов. NH будет исходить из того, что они исходят от основного объекта, Человека, для которого вы определяете сопоставление. Иногда это может давать забавные результаты, но, скорее всего, в таблице Person нет столбцов Name
или Street
, и вы получите сообщение об ошибке от rdbms.
Чтобы исправить это, у вас должно быть что-то вроде:
Map(x => x.CityName).Formula("select c.Name from City c where c.street = a.STREET");
^CEE ^AYE
Конечно, теперь другая ошибка будет говорить, что префикс a
не известен. Конечно, у нас он нигде не определен, поскольку у нас есть PERSON и CITY, а промежуточный ADDRESS полностью опущен в запросе sql.
Теперь взгляните на это:
Map(x => x.CityName)
.Formula(@"
select c.Name
from City c
where c.street =
( select a.Street
from Address a
where a.address_id = address_id
)"); ^
^ no prefix!
Поскольку внутренний address_id
не имеет префикса, NH будет считать, что он принадлежит Person. Здорово. Других столбцов без префикса нет, поэтому все остальные столбцы игнорируются и предполагаются привязанными к областям, определенным в самом тексте SQL.
Таким образом, внутренний подзапрос выберет адрес на основе Person.AddressID и выберет из него улицу. Надеюсь, это всего одна улица, так как мы сопоставляем адреса по идентификатору. После нахождения улицы внешний подзапрос будет использовать ее для сопоставления с городом.
Мы также могли бы записать формулу как
select c.Name
from Address a
inner join City c no c.street = a.street
where a.address_id = address_id
с тем же эффектом и, вероятно, с другой производительностью.
Следует отметить одну вещь: когда вы пишете формулу подзапроса или когда вы пишете where foo = (select x from...)
в подзапросе, вы ДОЛЖНЫ убедиться, что подзапрос всегда возвращает ноль или один результат. Не два или больше. Многие СУБД воспринимают это как ошибку. В случае этого запроса одно и то же имя street
может встречаться в нескольких городах, поэтому у вас высока вероятность ошибки только потому, что вы сопоставляете адрес с городом по улице.
person
quetzalcoatl
schedule
21.09.2018