MVC 3 недействительная ошибка пользовательского контекста членства - PullRequest
1 голос
/ 05 сентября 2011

У меня есть пользовательское членство, которое использует мой CustomerService для связи с базой данных, используя сначала код EF (4.1). Я использую ninject для добавления CustomerService в мой пользовательский класс членства.Но когда я пытаюсь подтвердить, я получаю контекстную ошибку.Это связано с тем, что в моем контексте репозитории и сервисы являются InRequestScope ().И поскольку я внедряю CustomerService, используя [inject] для свойства моего пользовательского членства, а в ninject - _kernel.Inject (Membership.Provider), он вводится только при запуске.Сообщения об этой проблеме, но не может найти ответ, который решает эту проблему.

У кого-нибудь есть решение этой проблемы?

1 Ответ

9 голосов
/ 23 октября 2011

Это распространенная ошибка для людей, которые начинают с контейнеров DI и IoC. Вы должны поддерживать согласованность областей. Вы не можете иметь зависимости, связанные с областью запроса, когда зависящие от них службы связаны с одноэлементной областью (или, что еще хуже, некоторые области вообще не управляются контейнером). Это просто неправильно.

У вас есть два основных варианта:

  1. Свяжите CustomerService как InSingletonScope вместе с самим поставщиком членства. Очевидно, что это имеет все обычные недостатки долгоживущих EF-сервисов.

  2. Ваш поставщик услуг не должен зависеть от экземпляра CustomerService. Вместо этого установите зависимость от CustomerServiceFactory, который может создавать CustomerService экземпляров, и обрабатывать каждый вызов поставщика членства как временный.

Для # 2 процесс создания и привязки фабрики очень прост:

public interface ICustomerServiceFactory
{
    ICustomerService GetService();
}

public class NinjectCustomerServiceFactory : ICustomerServiceFactory
{
    private readonly IKernel kernel;

    public NinjectCustomerServiceFactory(IKernel kernel)
    {
        if (kernel == null)
            throw new ArgumentNullException("kernel");
        this.kernel = kernel;
    }

    public ICustomerService GetService()
    {
        return kernel.Get<ICustomerService>();
    }
}

Тогда в вашем модуле:

Bind<ICustomerService>()
    .To<EFCustomerService>();
    .InRequestScope();
Bind<ICustomerServiceFactory>()
    .To<NinjectCustomerServiceFactory>()
    .InSingletonScope();

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

В итоге вы получите код участника, который выглядит так:

public class MyMembershipProvider : MembershipProvider
{
    public override MembershipUserCollection GetAllUsers()
    {
        var service = serviceFactory.GetService();
        var serviceUsers = service.GetAllUsers();
        return serviceUsers.Select(u => CreateMembershipUser(u));
    }

    // Other methods...

    [Inject]
    public ICustomerServiceFactory ServiceFactory { get; set; }
}

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

...