#nhibernate #proxy-classes
#nhibernate #прокси-классы
Вопрос:
Я хочу создать прокси-объект, аналогичный тому, что ISession.Загрузка возвращается, но с инициализированными некоторыми полями. Для других свойств, при обращении к которым прокси-сервер извлекает весь объект из базы данных. Рассмотрим следующий пример:
public class User
{
protected User() {
}
public User(int id, string username, string email) {
// ...
}
// initialize the following fields from other datasources
public virtual int Id { get; set; }
public virtual string UserName { get; set; }
public virtual string Email { get; set; }
// the rest of fields when accessed will trigger a select by id in the database
public virtual string Field1 { get; set; }
public virtual string Field2 { get; set; }
public virtual DateTime Field3 { get; set; }
public virtual ISet<Comment> Comments { get; set; }
}
Идентификатор, имя пользователя, адрес электронной почты хорошо известны в моем случае, поэтому я мог бы создать прокси-объект, содержащий эти поля, а для остальных оставить поведение прокси по умолчанию. В дополнение к созданию исключения, если этот id не найден в базе данных, я мог бы создать исключение, если предварительно инициализированные поля не совпадают, или перезаписать их молча. Я использую NHibernate.Байт-код.Замок для прокси-фабрик.
Редактировать: Цель этого — иметь некоторые свойства проекции из объекта, которые могут быть запрошены в другом месте (скажем,. индекс lucene) и избегать вызовов базы данных. Тогда вместо того, чтобы переносить эти поля в пользовательский класс компонента, содержащий только это подмножество свойств, я хочу использовать прокси-объект напрямую, чтобы при необходимости я мог загружать остальные поля. В лучшем случае я бы вообще не обращался к базе данных, но в некоторых крайних случаях я бы хотел получить доступ и к другим полям. Влияние проблемы ВЫБОРА N 1 может быть значительно уменьшено с помощью пакетной обработки. Гипотетическая версия кода, которую я хочу использовать, будет:
// create User object proxy with some fields initialized
var user = Session.Load<User>(5, new { UserName = "admin", Email = "admin@company.com" });
Console.WriteLine(user.Id); // doesn't hit the database
Console.WriteLine(user.UserName); // doesn't hit the database
Console.WriteLine(user.FullName); // doesn't hit the database
if (somecondition) {
Console.WriteLine(user.Field1); // fetches all other fields
}
Ответ №1:
Вы можете указать нетерпеливую выборку внутри запроса, чтобы фактически получить необходимые ассоциации. Это может быть сделано по-разному в зависимости от того, какой стиль запроса (критерии, Hql или LINQto NH ) вы используете. Но ключ меняет режим выборки.
Ответ №2:
для свойств, не входящих в коллекцию, я бы не стал этого делать;
стоимость предварительной выборки их из базы данных при загрузке вашего объекта (обычно) настолько мала, что я бы даже не стал беспокоиться.
для свойств коллекции просто отметьте стратегию выборки коллекции как 'lazy=true'
.
Единственный случай, когда я бы подумал о том, чтобы сделать что-то подобное, — это когда у меня есть очень большое количество свойств, которые мне не нужны (в вашем примере — скажем, Field1 ..Поле 20).
В этом случае я бы либо:
1. Определил эти свойства вместе как компонент, либо
2. создал DTO для извлечения только подмножества свойств вашего объекта.
Комментарии:
1. Вы правы насчет большого количества свойств, то, что я ищу, — это небольшая проекция полей из класса entity (2-4 поля), но без использования псевдонима obeantransformer anonymous classes для проекции, потому что я мог бы запрашивать не к базе данных. И возможность загружать другие поля, если это необходимо, в некоторых крайних случаях.
2. ну, я не тестировал это, но идея компонента может сработать. вы пробовали это? дело в том, что как только вы загрузите одно из свойств компонента, все остальные тоже будут загружены.
Ответ №3:
указание lazy = «true» (или нет.Может помочь LazyLoad() для свободного NHib) в свойствах Field1
, Field2
, Field3
, Comments
сопоставлениях, хотя я не уверен в проблеме выбора N 1.
другой способ — указать lazy = «false» для UserName
, Email