Могу ли я запросить тип пользователя с несколькими свойствами, сопоставленными одному столбцу?

#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 будет работать, но я надеялся, что смогу использовать некоторые из новых функций расширяемости, чтобы сделать это за меня.