Создайте экземпляр ISession для ViewModel - PullRequest
4 голосов
/ 25 июня 2010

вот моя проблема: я создаю настольное приложение со следующими инструментами:

  • Caliburn
  • Ninject
  • NHibernate

Все мои модели представлений и репозитории создаются с помощью Ninject. Все мои репозитории нуждаются в ISession в своем конструкторе.

Я бы хотел последовать совету Айенде относительно ViewModels: каждая ViewModel открывает новый сеанс.

Можно ли настроить Ninject для открытия нового сеанса при создании ViewModel и использовать этот сеанс в репозиториях, используемых этой моделью представления?

Я рассмотрел функцию InScope в Ninject, а также интерфейс ICurrentSessionContext в NHibernate, но я не знаю, как все это смоделировать, чтобы получить то, что я хочу ...

Кто-то делал что-то подобное раньше?

Заранее спасибо

Mike

Ответы [ 5 ]

0 голосов
/ 28 июня 2010

Что ж, я нашел решение благодаря группе исключений.

Решение здесь состоит в том, чтобы использовать функцию InScope при привязке ISession и просматривать переменную IContext для проверки служб. Если один сервис в иерархии запросов назначается базовому классу моих моделей представлений, я использую контекст в качестве области действия.

Таким образом, когда ISession впервые будет добавлен в конструктор моей ViewModel, используется новая область. И все последующие вызовы ISession внутри конструктора ViewModel будут разрешаться в той же области видимости. И тогда для моей ViewModel создается только одна сессия.

Вот код:

Bind<ISession>().ToMethod(ctx =>
    {
        var session = ctx.Kernel.Get<INHibernateSessionFactoryBuilder>()
            .GetSessionFactory()
            .OpenSession();

        session.FlushMode = FlushMode.Commit;

        return session;
    })
    .InScope(ctx =>
    {
        var request = ctx.Request;

        if (request.Service is IScreen)
            return request;

        while ((request = request.ParentRequest) != null)
            if (typeof(IScreen).IsAssignableFrom(request.Service))
                return request;

        return new object();
    });

И конструктор модели представления должен содержать все внедренные зависимости, которые зависят от ISession:

[Inject]
public PlayersManagementViewModel(ISession session, IPlayersRepository playersRepository)
{
}

Надеюсь, это поможет

0 голосов
0 голосов
/ 25 июня 2010

У меня очень похожий проект (за исключением того, что я не использую Caliburn), и я пытался выяснить, как это сделать. Я придумал один метод, который хорошо работает для внедрения в конструктор, используя метод InScope () от Ninject.

У меня есть статический класс, называемый IoC, который обертывает доступ к ядру Ninject. Поскольку все зависимости внедряются в конструктор, контекст имеет значение только при создании объекта. Так что не имеет значения, что предоставляется для контекста, но Guid чувствует себя как безопасный выбор. Program.OpenSession () - статический метод для открытия новой ISession.

public static class Ioc
{
    private static readonly IKernel _kernel;

    static IoC()
    {
        _kernel = new StandardKernel();
        _kernel.Load(new ContextModule());
    }

    private static object _context;

    public static T ResolveInContext<T>(object context)
    {
        _context = context;
        var result = _kernel.Get<T>();
        _context = null;
        return result;
    }

    private class ContextModule : NinjectModule
    {
        public override void Load()
        {
            Bind<ISession>().ToMethod(x => Program.OpenSession()).InScope(x => _context);
            Bind<frmCompanyViewer>().ToSelf().InScope(x => _context);
        }
    }
}

Использование:

var frm = IoC.ResolveInContext<frmCompanyViewer>(Guid.NewGuid());

Подпись конструктора формы:

public frmCompanyViewer(ISession session, ICompanyRepository companyRepository)

Я убедился, что с привязкой InScope к той же ISession, которая используется для создания frmCompanyViewer, также используется для создания companyRepository. Если я удаляю InScope, то используются две сессии ISession.

Отредактировано, чтобы добавить: Это также будет работать, см. Комментарии. Это должно быть сделано потокобезопасным для реального применения. Я изменил имя метода на ConstructInContext, чтобы уточнить, что контекст применяется только во время создания объекта.

    public static T ConstructInContext<T>()
    {
        _context = Guid.NewGuid();
        var result = _kernel.Get<T>();
        _context = null;
        return result;
    }
0 голосов
/ 26 июня 2010

У нас это с АОП, в хаддинах. Называется "Разговор на одну деловую транзакцию".

поиск в Google

0 голосов
/ 25 июня 2010

Я решил аналогичный сценарий, используя жизненный цикл ViewModel: я создал интерфейс ISessionAware (с методом SetSession), который будет реализован в репозиториях, затем я инициализировал репозитории через ISessionAware в методе OnInitialize ViewModel (который обеспечивается Caliburn когда виртуальная машина управляется ScreenConductor).

Используя отражение для проверки свойств, содержащих репозитории, я мог бы поместить всю инфраструктуру в класс BaseDataVM.

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

...