SimpleInjector针对每个Web请求和生命周期范围混合生活方式

时间:2013-02-04 13:10:28

标签: c# .net dependency-injection simple-injector

我使用Simple Injector作为我的IoC容器,并使用以下技术为每个Web请求或每个线程注册某些对象的“混合”生活方式。

interface IUnitOfWork { }
interface IWebUnitOfWork : IUnitOfWork { }
interface IThreadUnitOfWork : IUnitOfWork { }
class UnitOfWork : IWebUnitOfWork, IThreadUnitOfWork { }

container.RegisterPerWebRequest<IWebUnitOfWork, UnitOfWork>();
container.RegisterLifetimeScope<IThreadUnitOfWork, UnitOfWork>();
container.Register<IUnitOfWork>(() => container.GetInstance<UnitOfWork>());

// Register as hybrid PerWebRequest / PerLifetimeScope.
container.Register<UnitOfWork>(() =>
{
    if (HttpContext.Current != null)
        return container.GetInstance<IWebUnitOfWork>() as UnitOfWork;
    else
        return container.GetInstance<IThreadUnitOfWork>() as UnitOfWork;
});

我对这个解决方案并不完全满意,因为每个要求我必须定义额外的空接口以使其工作并确保它们被我的具体类引用。

有什么理由我不应该使用以下扩展方法而不是定义额外的接口?如果这些方法存在一些问题,还是有其他方法可以完全放心地建立我当前的容器实例在IIS中运行吗?

public static void RegisterHybridLifestyle<TService, TImplementation>(
    this Container container)
    where TService : class
    where TImplementation : class, TService
{
    if (System.Web.Hosting.HostingEnvironment.ApplicationHost != null)
        container.RegisterPerWebRequest<TService, TImplementation>();
    else
        container.RegisterLifetimeScope<TService, TImplementation>();
}

public static void RegisterForLifestyle<TConcrete>(
    this Container container)
    where TConcrete : class
{
    if (HostingEnvironment.ApplicationHost != null)
        container.RegisterPerWebRequest<TConcrete>();
    else
        container.RegisterLifetimeScope<TConcrete>();
}

更新

上述问题and this follow on question基于对SimpleInjector和混合注册的误解。上面和SO上其他地方描述的技术适用于容器何时可以处理对Web请求和未在Web请求的上下文中运行的后台进程的请求。我一直在努力实现的是变量注册,以满足适用于Web Request OR 线程请求的容器配置。即我需要配置我的容器在IIS中工作,并在Windows服务中工作。我不需要动态注册,可以同时满足。

结果是以下扩展方法,我从我的解决方案中删除了“额外”接口: - )

public static void RegisterForScope<TService, TImplementation>(this Container container)
    where TService : class
    where TImplementation : class, TService
{
    if (System.Web.Hosting.HostingEnvironment.ApplicationHost != null)
        container.RegisterPerWebRequest<TService, TImplementation>();
    else
        container.RegisterLifetimeScope<TService, TImplementation>();
}

public static void RegisterForScope<TConcrete>(this Container container)
    where TConcrete : class
{
    if (System.Web.Hosting.HostingEnvironment.ApplicationHost != null)
        container.RegisterPerWebRequest<TConcrete>();
    else
        container.RegisterLifetimeScope<TConcrete>();
}

1 个答案:

答案 0 :(得分:3)

  

我对此解决方案并不完全满意

是的,我同意这一点。说实话,不得不做这样的事情实际上很糟糕IMO。这就是Simple Injector 2.0修复的原因。它包含生活方式的明确概念,它将包含Lifestyle.CreateHybrid方法,这使得注册混合生活方式变得更加容易。

然而,你似乎并不需要混合生活方式。混合生活方式是一种可以动态切换的生活方式(每次呼叫GetInstance<T>和每次注射),而您似乎只需要在启动期间切换。我认为使用RegisterHybridLifestyle扩展方法并没有什么害处,但请记住,这不是一种混合生活方式(因此名称有点误导),而只是配置/部署 - 切换。

简单的注射器2及以上使这更容易,它可以让你做这样的事情:

// Define a lifestyle once based on the deployment.
Container.Options.DefaultScopedLifestyle =
    Lifestyle.CreateHybrid(
        lifestyleSelector: HostingEnvironment.ApplicationHost != null,
        trueLifestyle: new WebRequestLifestyle(),
        falseLifestyle: new LifetimeScopeLifestyle());

// And use it for registering the unit of work
// (no extra interfaces needed anymore).
container.Register<IUnitOfWork, UnitOfWork>(Lifestyle.Scoped);

// After setting DefaultScopedLifestyle, you can usr Lifestyle.Scoped
container.RegisterCollection(
    typeof(ISubscriber<>),
    new [] { typeof(ISubscriber<>).Assembly },
    Lifestyle.Scoped);