Autofac - 如何从Singleton解析Func for ISomething,其中ISomething是InstancePerHttpRequest

时间:2013-03-21 02:56:54

标签: inversion-of-control autofac fluentvalidation

我正在尝试使用Autofac将依赖项注入MVC​​ 4应用程序中的FluentValidation。我想我的策略已经解决了,但是我已经陷入了从单身人士处理我的请求ISomething的问题。

以下是该方案: 我有一个派生自FluentValidation的AbstractValidator的验证器。我已经读过FluentValidation验证器作为单例表现最好,所以我的构造函数需要一个Func并存储该Factory以供以后使用。使用验证器时,它应该向存储的工厂询问IDataStore,获取为该请求创建的实例并使用它。这就是理论。我想赞扬https://github.com/robdmoore/UnobtrusiveMVCTechniques,这有助于我解决这个问题。这是验证器...

public class SiteAdminViewModelValidator : AbstractValidator<SiteAdminViewModel> {
    private readonly Func<IDataStore> _dbFactory;

    public SiteAdminViewModelValidator(Func<IDataStore> dbFactory) {
        _dbFactory = dbFactory;

        RuleFor(model => model.SiteCode).Length(1, 40).Must(BeSpecial);
    }

    public bool BeSpecial(string siteCode) {
        var db = _dbFactory();
        List<Stuff> stuffs = db.All<Stuff>().ToList();

        return true;
    }
}

如果有人可以指出我正在努力完成的工作示例,那就太棒了,但我也想知道这一特定Autofac诡计的解决方案。

这是我的验证员注册......

public class FluentValidatorModule : Module {
    protected override void Load(ContainerBuilder builder) {
        base.Load(builder);
        builder.RegisterType<AutofacValidatorFactory>().As<IValidatorFactory>().SingleInstance();

    var validators = AssemblyScanner.FindValidatorsInAssembly(System.Reflection.Assembly.GetExecutingAssembly());
    validators.ToList().ForEach(v => builder.RegisterType(v.ValidatorType).As(v.InterfaceType).SingleInstance());
    }
}

这是我在IDataStore工厂的注册......

builder.RegisterType<SuperDB>().As<IDataStore>().InstancePerHttpRequest();
builder.Register<Func<IDataStore>>(c => {
                                       var context = c.Resolve<IComponentContext>();
                                       return context.Resolve<IDataStore>;
                                   });

这是我的验证器在线上要求IDataStore时出现的错误 - var db = _dbFactory();

  

从中可以看到没有与“AutofacWebRequest”匹配的标记的范围   请求实例的范围。这通常表明   正在请求按照HTTP请求注册的组件   一个SingleInstance()组件(或类似的场景。)在网络下   集成始终请求来自的依赖项   DependencyResolver.Current或ILifetimeScopeProvider.RequestLifetime,   从来没有从容器本身。

...这正是我在编写自己的工厂注册 - Func注册之前尝试的时候得到的。从阅读各种答案到类似的问题,看起来我上面应该工作,因为我以为我现在正在解决Func以获得当前的解析器。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:18)

我同意这应该有效 - Func<IDataStore>正在定义一个工厂,它将根据需要在每个方法中产生依赖关系。

我使用此方法的方法是使用DependencyResolver.Current,如错误消息所示。主要原因是我已经使用Autofac.Mvc4 nuget包设置了它......

DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

因此,为了实际设置方法,我有以下函数

public Func<T> PerHttpSafeResolve<T>()
{
    return () => DependencyResolver.Current.GetService<T>();
} 

构建容器时

builder.RegisterType<SuperDB>().As<IDataStore>().InstancePerHttpRequest();
builder.RegisterInstance(PerHttpSafeResolve<IDataStore>());

编辑:注册实例的第二行是 - 如果需要Func<IDataStore>,则使用传递给方法的值。 PerHttpSafeResolve<IDataStore>的结果只是一个函数(工厂),因此它可以作为单个实例存在。

答案 1 :(得分:9)

问题在于验证者的范围。 Autofac始终从应用程序容器中解析SingleInstance依赖项,这意味着验证程序的依赖项也来自应用程序容器。

Autofac从那里请求Func<IDataStore>实例,而不是请求容器。解析IComponentContext时,您将获得正在解析验证程序的容器:应用程序容器。由于Func<IDataStore>的范围是一个请求,因此Autofac无法在应用程序级别提供它,因此会出错。

正确的方法是将验证器注册为InstancePerHttpRequest。组件的生命周期取决于其最长寿命的依赖性;验证者在没有依赖关系时最适合作为单身人士。但是,在这种情况下,依赖于IDataStore的验证器被限制为最多只能存活IDataStore实例的生命周期。 (好的一面是,你可以摆脱Func。)

想想参加派对:如果你和主人搭车,你必须留在那里直到派对结束。如果你想早点离开,你不能和主人一起骑车。