#c# #nhibernate #usertype
#c# #nhibernate #usertype
Вопрос:
У меня есть следующая модель домена:
public class Name
{
private readonly string fullName;
public Name(string fullName) { this.fullName = fullName }
public string FullName { get { return fullName; } }
public string FirstName { get { /* ... */ } }
public string MiddleNames { get { /* ... */ } }
public string LastName { get { /* ... */ } }
public static implicit operator Name(string name) { /* ... */ }
}
public class Person
{
public Name BirthName { get; set; }
public Name Pseudonym { get; set; }
}
Я реализовал IUserType
, чтобы я мог сопоставить каждое имя с одним столбцом базы данных с полным именем.
Запросы, подобные этому, работают:
var people = session.QueryOver<Person>()
.Where(p => p.Name == "John Doe")
.List();
Но я не могу запросить таким образом:
var people = session.QueryOver<Person>()
.Where(p => p.Name.LastName == "Doe")
.List();
Могу ли я заставить NHibernate работать с этим?
Ответ №1:
Я не очень разбираюсь в nhibernate, но, анализируя всю имеющуюся у нас здесь информацию о сценарии, у меня есть сильное ощущение, что ответ таков: вы не можете.
Вы сопоставляете значение, содержащееся в этом классе, с единственным значением в базе данных.
Чтобы разрешить выполнение запросов к его частям, он должен понимать, как все эти вложенные фрагменты информации в классе Name связаны с полным значением.
Это означало бы понимание того, что делает пользовательский код c #, который у вас есть.
обновление 1:
Приведенный выше ответ касается автоматического создания всего этого для вас и использования точного запроса, который вы упомянули там.
Вы определенно можете обойти проблему, как предложено @cs. То есть определить пользовательскую функцию, которая выполняет то, что вы хотите, и отобразить это в NHibernate.
По крайней мере, это позволило бы вам сделать что-то вроде:
session.QueryOver<Person>()
.Where(p => session.PersonLastName(p) == "Doe")
.List();
Другим способом было бы определить метод расширения, который преобразуется в то, что вы хотите, реализуя его, как в этой статье:http://fabiomaulo.blogspot.com/2010/07/nhibernate-linq-provider-extension.html
Использование тогда было бы похоже:
session.QueryOver<Person>()
.Where(p=> p.Name.LastNameExt() == "Doe")// Ext just to avoid name collision
.List();
Наконец, я не уверен, возможно ли сопоставить вложенные свойства с пользовательскими функциями для каждого, чтобы ваш запрос мог оставаться неизменным, например:
session.QueryOver<Person>()
.Where(p => p.Name.LastName == "Doe")
.List();
Не во всех случаях вы переходите от объединения данных к анализу данных. Вы единственный, кто знает, действительно ли это вам что-то дает, по сравнению с сохранением свойств по отдельности.
Комментарии:
1. В реальной системе отдельные столбцы, вероятно, были бы лучше, но это всего лишь эксперимент, поэтому я не возражаю. В любом случае, спасибо за отличный ответ. Из статьи, на которую вы ссылались, кажется, что я могу сохранить запрос без изменений, но мне придется подтвердить это.
Ответ №2:
Я считаю, что это возможно, но потребует некоторой работы со стороны SQL. Первое, что я, вероятно, попробовал бы, это создать какой-нибудь UDF, который разделит имя для вас и выполнит необходимое сравнение. Затем вы можете расширить NHibernate для сопоставления с этим UDF, и вы должны иметь возможность вызывать эту функцию из своего запроса, будь то на основе SQL, HQL или ICriteria.
Комментарии:
1. 1 в качестве обходного пути @ cs смотрите Мой обновленный ответ, вы имеете в виду, что возможен последний сценарий? или точно так же, как предыдущие?
2. ДА. То, что вы сделали с первыми двумя, — это то, что я имел в виду. Спасибо, что обновили свой ответ всей этой информацией.
Ответ №3:
Вы сможете запрашивать только полное имя, поскольку это всего лишь одно поле базы данных.
Если вы сопоставите его по-другому, так что FirstName и Lastname будут отдельными столбцами, вы сможете выполнять запросы к каждому из них, но тогда у вас могут возникнуть трудности с запросом к свойству с именем FullName.
Ответ №4:
Вам нужно создать вычисляемые столбцы. Они будут доступны только для чтения, но вы можете выполнить запрос к ним. Смотрите здесь.
Ответ №5:
Что вы могли бы сделать, так это реализовать свою собственную оболочку репозитория для этих объектов и реализовать такие методы, как GetUsersByLastName(), которые могли бы использовать HQL для выполнения этих типов запросов «поиск по частичному значению».
Что-то вроде следующего должно позволить вам выполнять поиск по любому частичному значению при условии, что вы передаете правильную строку поиска:
var hql = "select p from People p where lower(p.BirthName) like :searchText";
Редактировать:
Удалено LastName из запроса. Способ, которым это будет работать, заключается в том, что вы должны использовать привязанный текст поиска либо к началу, либо к концу поля BirthName. Например, если вы хотите выполнить поиск по всем пользователям с фамилией Смит, вы могли бы сделать:
var hql = "select p from People p where lower(p.BirthName) like '% smith'";
И в случае людей с именем Джо:
var hql = "select p from People p where lower(p.BirthName) like 'joe %'";
Комментарии:
1. И как это будет работать? NHibernate не знает, как сделать
.LastName
.2. Видите, это именно тот SQL, к которому я ожидал, что NHibernate сопоставит эти свойства. Конечно, выполнение этого вручную с помощью HQL будет работать, но я надеялся, что смогу использовать некоторые из новых функций расширяемости, чтобы сделать это за меня.