Вынудите ленивый объект загрузить реальный экземпляр

Попытайтесь установить core.autocrlf параметр конфигурации к true. Также взгляните на core.safecrlf опция.

На самом деле это походит core.safecrlf, мог бы уже быть установлен в Вашем репозитории, потому что (шахта акцента):

, Если дело обстоит не так для текущей установки core.autocrlf, мерзавец отклонит файл .

, Если это верно, то Вы могли бы хотеть проверить, что Ваш текстовый редактор настроен для использования окончаний строки последовательно. Вы, вероятно, столкнетесь с проблемами, если текстовый файл будет содержать смесь LF и окончаний строки CRLF.

Наконец, я чувствую, что рекомендация просто "использует то, что Вам дают" и используете нагруженные линии LF в Windows, вызовет больше проблем, чем это решает. У мерзавца есть вышеупомянутые опции попытаться обработать окончания строки разумным способом, таким образом, имеет смысл использовать их.

7
задан Sam 30 July 2009 в 03:25
поделиться

3 ответа

Для принудительной выборки прокси-сервера из базы данных можно использовать метод NHibernateUtil.Initialize (proxy) или получить доступ к методу / свойству прокси.

var foo = session.Get<Foo>(id);
NHibernateUtil.Initialize(foo.Bar);

Чтобы проверить, инициализирован ли объект или нет, вы можете использовать метод NHibernateUtil.IsInitialized (proxy) .

Обновление:

Чтобы удалить объект из кеша сеанса, используйте Метод Session.Evict (obj) .

session.Evict(myEntity);

Информацию о Evict и других методах управления кешем сеанса можно найти в главе 14.5 документации NHibernate.

12
ответ дан 6 December 2019 в 05:39
поделиться

Поскольку прокси является производным от класса сущности, вы, вероятно, можете просто проверить entity.GetType (). BaseType, чтобы получить ваш определенный тип.

-2
ответ дан 6 December 2019 в 05:39
поделиться

На мой взгляд, вместо решения этой проблемы вам следует переосмыслить свой дизайн. Вы абсолютно уверены, что вы не можете использовать полиморфизм в этой ситуации - либо напрямую назначьте объект ответственным за операцию, которую вы пытаетесь выполнить, либо используйте шаблон посетителя. Я сталкивался с этой проблемой несколько раз и всегда решал изменить дизайн - это привело к более четкому коду. Я предлагаю вам сделать то же самое, если вы не абсолютно уверены, что полагаться на тип - лучшее решение.

Проблема

Чтобы иметь пример, хоть немного напоминающий реальный мир, предположим, что у вас есть следующие сущностей:

public abstract class Operation
{
    public virtual DateTime PerformedOn { get; set; }
    public virtual double Ammount { get; set; }
}

public class OutgoingTransfer : Operation
{
    public virtual string TargetAccount { get; set; }
}

public class AtmWithdrawal : Operation
{
    public virtual string AtmAddress { get; set; }
}

Это ' Естественно, я буду маленькой частью гораздо большей модели. И теперь вы столкнулись с проблемой: для каждого конкретного типа Операции есть свой способ отображения:

private static void PrintOperation(Operation operation)
{
    Console.WriteLine("{0} - {1}", operation.PerformedOn,
                      operation.Ammount);
}

private static void PrintOperation(OutgoingTransfer operation)
{
    Console.WriteLine("{0}: {1}, target account: {2}",
                      operation.PerformedOn, operation.Ammount,
                      operation.TargetAccount);
}

private static void PrintOperation(AtmWithdrawal operation)
{
    Console.WriteLine("{0}: {1}, atm's address: {2}",
                      operation.PerformedOn, operation.Ammount,
                      operation.AtmAddress);
}

Простые перегруженные методы будут работать в простом случае:

var transfer = new OutgoingTransfer
               {
                   Ammount = -1000,
                   PerformedOn = DateTime.Now.Date,
                   TargetAccount = "123123123"
               };

var withdrawal = new AtmWithdrawal
                 {
                     Ammount = -1000,
                     PerformedOn = DateTime.Now.Date,
                     AtmAddress = "Some address"
                 };

// works as intended
PrintOperation(transfer);
PrintOperation(withdrawal);

К сожалению, перегруженные методы привязываются во время компиляции, поэтому, как только вы вводите массив / список / любые операции, будет вызываться только общая (операция операция) перегрузка.

Operation[] operations = { transfer, withdrawal };
foreach (var operation in operations)
{
    PrintOperation(operation);
}

Есть два решения этой проблемы, и у обоих есть свои недостатки. Вы можете ввести абстрактный / виртуальный метод в Operation для печати информации в выбранном потоке. Но это приведет к смешению проблем пользовательского интерфейса с вашей моделью, так что это неприемлемо для вас (я покажу вам, как вы можете улучшить это решение, чтобы оно соответствовало вашим ожиданиям).

Вы также можете создать множество if в форме :

if(operation is (ConcreteType))
   PrintOperation((ConcreteType)operation);

Это некрасивое решение, подверженное ошибкам. Каждый раз, когда вы добавляете / изменяете / удаляете тип операции, вам нужно пройти через все места, где вы использовали этот взлом, и изменить его. И если вы пропустите одно место, вы, вероятно, сможете поймать только это время выполнения - никаких строгих проверок времени компиляции на наличие некоторых ошибок (например, пропущенного одного подтипа).

Кроме того, это решение выйдет из строя, как только вы введете любой тип прокси.

Как работает прокси

Приведенный ниже код является ОЧЕНЬ простым прокси (в этой реализации он такой же, как и шаблон декоратора, но в целом эти шаблоны не совпадают. Для различения потребуется некоторый дополнительный код эти два шаблона).

public class OperationProxy : Operation
{
    private readonly Operation m_innerOperation;

    public OperationProxy(Operation innerOperation)
    {
        if (innerOperation == null)
            throw new ArgumentNullException("innerOperation");
        m_innerOperation = innerOperation;
    }


    public override double Ammount
    {
        get { return m_innerOperation.Ammount; }
        set { m_innerOperation.Ammount = value; }
    }

    public override DateTime PerformedOn
    {
        get { return m_innerOperation.PerformedOn; }
        set { m_innerOperation.PerformedOn = value; }
    }
}

Как видите, существует только один прокси-класс для всей иерархии. Почему? Потому что вы должны писать свой код так, чтобы он не зависел от конкретного типа - только от предоставленной абстракции. Этот прокси может отложить загрузку объекта вовремя - может быть, вы вообще не будете его использовать? Может быть, вы будете использовать всего 2 сущности из 1000? Зачем тогда загружать их все?

Итак, NHibernate использует прокси, как указано выше (хотя и гораздо более сложный), чтобы отложить загрузку сущностей. Он может создать по одному прокси для каждого подтипа, но это разрушит всю цель ленивой загрузки. Если вы внимательно посмотрите, как NHibernate хранит подклассы, вы увидите, что для определения сущности типа вам необходимо загрузить ее. Таким образом, невозможно иметь конкретные прокси - у вас может быть только самый абстрактный, OperationProxy.

Несмотря на то, что решение с если это уродливо, это было решение. Теперь, когда вы ввели в свою проблему прокси - уже не работает. Остается только полиморфный метод, что неприемлемо из-за смешивания ответственности пользовательского интерфейса с вашей моделью. Давайте исправим это.

Инверсия зависимостей и шаблон посетителя

Во-первых, давайте посмотрим, как будет выглядеть решение с виртуальными методами (только что добавленный код):

public abstract class Operation
{
    public abstract void PrintInformation();
}

public class OutgoingTransfer : Operation
{
    public override void PrintInformation()
    {
        Console.WriteLine("{0}: {1}, target account: {2}",
                      PerformedOn, Ammount, TargetAccount);
    }
}

public class AtmWithdrawal : Operation
{
    public override void PrintInformation()
    {
        Console.WriteLine("{0}: {1}, atm's address: {2}",
                          PerformedOn, Ammount, AtmAddress);
    }
}

public class OperationProxy : Operation
{
    public override void PrintInformation()
    {
        m_innerOperation.PrintInformation();
    }
}

А теперь, когда вы вызываете:

Operation[] operations = { transfer, withdrawal, proxy };
foreach (var operation in operations)
{
    operation.PrintInformation();
}

все работает как шарм.

Чтобы удалить эту зависимость пользовательского интерфейса в модели, давайте создадим интерфейс:

public interface IOperationVisitor
{
    void Visit(AtmWithdrawal operation);
    void Visit(OutgoingTransfer operation);
}

Давайте изменим модель, чтобы она зависела от этого интерфейса:

А теперь создадим реализацию - ConsoleOutputOperationVisitor (я удалил методы PrintInformation):

public abstract class Operation
{
    public abstract void Accept(IOperationVisitor visitor);
}

public class OutgoingTransfer : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class AtmWithdrawal : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class OperationProxy : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        m_innerOperation.Accept(visitor);
    }
}

Что здесь происходит? Когда вы вызываете Accept в операции и передаете посетителя, будет вызываться реализация accept, где будет вызвана соответствующая перегрузка метода Visit (компилятор может определить тип «this»). Итак, вы сочетаете "мощь" виртуальных методов и перегрузок, чтобы вызвать соответствующий метод. Как видите, здесь ссылка на пользовательский интерфейс, модель зависит только от интерфейса, который может быть включен в уровень модели.

Итак, теперь, чтобы заставить это работать, реализация интерфейса:

 public class ConsoleOutputOperationVisitor : IOperationVisitor
 {
    #region IOperationVisitor Members
    public void Visit(AtmWithdrawal operation)
    {
        Console.WriteLine("{0}: {1}, atm's address: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.AtmAddress);
    }

    public void Visit(OutgoingTransfer operation)
    {
        Console.WriteLine("{0}: {1}, target account: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.TargetAccount);
    }

    #endregion
}

И код:

Operation[] operations = { transfer, withdrawal, proxy };
foreach (var operation in operations)
{
    operation.Accept(visitor);
}

Я прекрасно понимаю, что это не идеальное решение. По мере добавления новых типов вам все равно придется изменять интерфейс и посетителей. Но вы получаете проверку времени компиляции и никогда ничего не пропустите. Одна вещь, которую было бы действительно трудно достичь с помощью этого метода, - получить подключаемые подтипы, но я в любом случае не уверен, что это допустимый сценарий. Вам также придется изменить этот шаблон, чтобы он соответствовал вашим потребностям в конкретном сценарии, но я оставлю это вам.

который может быть включен в уровень модели.

Итак, теперь, чтобы заставить это работать, реализация интерфейса:

 public class ConsoleOutputOperationVisitor : IOperationVisitor
 {
    #region IOperationVisitor Members
    public void Visit(AtmWithdrawal operation)
    {
        Console.WriteLine("{0}: {1}, atm's address: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.AtmAddress);
    }

    public void Visit(OutgoingTransfer operation)
    {
        Console.WriteLine("{0}: {1}, target account: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.TargetAccount);
    }

    #endregion
}

И код:

Operation[] operations = { transfer, withdrawal, proxy };
foreach (var operation in operations)
{
    operation.Accept(visitor);
}

Я хорошо понимаю, что это не идеальное решение. Вам все равно придется изменять интерфейс и посетителей по мере добавления новых типов. Но вы получаете проверку времени компиляции и никогда ничего не пропустите. Одна вещь, которую было бы действительно сложно достичь с помощью этого метода, - это получить подключаемые подтипы, но я в любом случае не уверен, что это допустимый сценарий. Вам также придется изменить этот шаблон, чтобы он соответствовал вашим потребностям в конкретном сценарии, но я оставлю это вам.

который может быть включен в уровень модели.

Итак, теперь, чтобы заставить это работать, реализация интерфейса:

 public class ConsoleOutputOperationVisitor : IOperationVisitor
 {
    #region IOperationVisitor Members
    public void Visit(AtmWithdrawal operation)
    {
        Console.WriteLine("{0}: {1}, atm's address: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.AtmAddress);
    }

    public void Visit(OutgoingTransfer operation)
    {
        Console.WriteLine("{0}: {1}, target account: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.TargetAccount);
    }

    #endregion
}

И код:

Operation[] operations = { transfer, withdrawal, proxy };
foreach (var operation in operations)
{
    operation.Accept(visitor);
}

Я хорошо понимаю, что это не идеальное решение. Вам все равно придется изменять интерфейс и посетителей по мере добавления новых типов. Но вы получаете проверку времени компиляции и никогда ничего не пропустите. Одна вещь, которую было бы действительно трудно достичь с помощью этого метода, - получить подключаемые подтипы, но я в любом случае не уверен, что это допустимый сценарий. Вам также придется изменить этот шаблон, чтобы он соответствовал вашим потребностям в конкретном сценарии, но я оставлю это вам.

Мне все равно придется изменять интерфейс и посетителей по мере добавления новых типов. Но вы получаете проверку времени компиляции и никогда ничего не пропустите. Одна вещь, которую было бы действительно сложно достичь с помощью этого метода, - это получить подключаемые подтипы, но я в любом случае не уверен, что это допустимый сценарий. Вам также придется изменить этот шаблон, чтобы он соответствовал вашим потребностям в конкретном сценарии, но я оставлю это вам.

Мне все равно придется изменять интерфейс и посетителей по мере добавления новых типов. Но вы получаете проверку времени компиляции и никогда ничего не пропустите. Одна вещь, которую было бы действительно трудно достичь с помощью этого метода, - получить подключаемые подтипы, но я в любом случае не уверен, что это допустимый сценарий. Вам также придется изменить этот шаблон, чтобы он соответствовал вашим потребностям в конкретном сценарии, но я оставлю это вам.

21
ответ дан 6 December 2019 в 05:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: