如何在Singleton

时间:2017-06-07 22:57:43

标签: asp.net-mvc autofac

我是Autofac的新手,我非常喜欢它的终身范围功能,但我遇到了问题。

我需要在InstancePerRequest的基础上使用一个对象的实例,该对象来自一个对.Net MVC Web没有反抗意见的程序集中的单例

我尝试了this帖子的建议,但它没有用。我得到了这个例外:

  

没有标记匹配的范围' AutofacWebRequest'是可见的   请求实例的范围。

在Singleton中获取/使用InstancePerRequest对象的正确方法是什么?

更新 我想澄清一下,考虑到特拉维斯的回答,我们不想在单身人士中持有captive dependency InstancePerRequest项目。我们想在每次使用时请求该项目。我们进行了分析,将单例设置为InstancePerRequest比将每次使用InstancePerRequest项请求容器更昂贵。 这被认为是一种不好的做法吗?在请求级别拥有您需要访问的某些内容并让Singleton使用它们是相当普遍的。

3 个答案:

答案 0 :(得分:3)

不要在单件中获取每个请求项。这会产生captive dependency,这是个坏消息。将单例切换为每个请求。

答案 1 :(得分:0)

鉴于Dzyann的更新

对我来说,这听起来像每个请求实例需要一个实例工厂。这样,Factory Func可以是单例,并且可以避免Captive Dependency。 代码看起来像这样。

public class MySingleton
{
    private readonly Func<IMyInstancePerRequestType> _instanceFactory;
    public MySingleton(Func<IMyInstancePerRequestType> instanceFactory)
    {
        _instanceFactory = instanceFactory;
    }

    public void DoStuff()
    {
       var myService = _instanceFactory();
       myService.DoStuff();
    }
} 

Autofac会隐式为您创建工厂方法。 因此不需要额外的注册。

请务必查看http://docs.autofac.org/en/latest/resolve/relationships.html

关于'AutofacWebRequest'错误,您需要使用AutofacMvc或AutoFacWebAPI nuget包并在启动期间使用Owin扩展方法(给定您使用owin)注册这将创建'AutofacWebRequest'生命受害者。

在startup.cs中它会像

   app.UseAutofacMiddleware( iocContainer );
   app.UseAutofacWebApi( configuration );

你指的是一篇文章。在那里我们有

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

.InstancePerHttpRequest()可以在那里添加。

答案 2 :(得分:0)

感谢大家的回答和评论,事实证明解决方案非常简单。 这是我最终拥有的文件结构:

非网络汇编

public IPerRequestDependencyResolver
{
    T Get<T>() where T : class;
}

public interface IPerRequestObject
{
    void DoSomething();
}

public class PerRequestObject: IPerRequestObject
{
    public void DoSomething()
    {}
}

public interface IMySingletonPerRequestWrapper 
{
    DoSomethingOnPerRequestBasis();
}

public class MySingletonPerRequestWrapper: IMySingletonPerRequestWrapper 
{
    private IPerRequestDependencyResolver perRequestDependencyResolver;
    public PerRequestWrapper(IPerRequestDependencyResolver perRequestDependencyResolver)
    {
        this.perRequestDependencyResolver = perRequestDependencyResolver;
    }

    public void DoSomethingOnPerRequestBasis()
    {
        this.perRequestDependencyResolver.Get<IPerRequestObject>().DoSomething();
    }
}

public interface IMySingletonBussinesObject
{
    void DoSomething();
}

public class MySingletonBussinessObject: IMySingletonBussinesObject
{
    private IMySingletonPerRequestWrapper mysingletonPerRequestWrapper;
    public MySingletonBussinessObject(IMySingletonPerRequestWrapper mysingletonPerRequestWrapper)
    {
        this.mysingletonPerRequestWrapper = mysingletonPerRequestWrapper;
    }

    public void DoSomething()
    {
        this.mysingletonPerRequestWrapper.DoSomethingOnPerRequestBasis();
    }
}

MVC Web程序集

public class PerRequestDependencyResolver : IPerRequestDependencyResolver
{
    public T Get<T>() where T : class
    {
        return DependencyResolver.Current.GetService<T>();
    }
}

MySingletonPerRequestWrapperMySingletonBussinessObjectPerRequestDependencyResolver Autofac 中配置为.SingleInstance(),而PerRequestObject配置为`InstancePerRequest()

我创建了MySingletonPerRequestWrapper,而不是直接使用PerRequestDependencyResolver中的MySingletonBussinessObject,因为我想确保使用PerRequestObject的任何人都能正确使用并且没有人工制作的实例是错误的。 我宁愿使用 Autofac 来保留我的所有实例,而不是将我的实例保留在HttpRequest并从那里使用它(使用包装器),我觉得这更灵活。

更新网络广告

这是使用此技术与WebApi的更新。我没有自己完成实施,我们在我的小组讨论过它,而且一位朋友实施了它。我将解释他采取的方法。

为了清晰起见,我简化了逻辑和类,你可以改变你需要的任何东西。 在我们的系统中,我们配置了Autofac,因此您可以在静态类中注入实例。你可以把它改成更适合你的东西。

首先是一些帮助,扩展方法来简化。

public static class Extensions
{
    private const string dependencyScopeKey = "Resolver.Unique.key";
    public static IExecutionContext ExecutionContext {  get; set; }
    public static IDependencyScope Current(this IDependencyResolver dependencyResolver)
    {
        return ExecutionContext.GetObject<IDependencyScope>(dependencyScopeKey);
    }

    public static void SetCurrentDependencyScope(this HttpRequestMessage request)
    {
        ExecutionContext.SetObject(dependencyScopeKey, request.GetDependencyScope());
    }
}


public class WebExecutionContext : IExecutionContext
{

    public T GetObject <T>(string key)
    {
        var result = HttpContext.Current.Items[key];
        return result != null ? (T) result : default(T);
    }

    public void SetObject(string key, object val)
    {
        if (HttpContext.Current.Items.Contains(key))
            HttpContext.Current.Items.Remove(key);
        HttpContext.Current.Items.Add(key, val);
    }
}

然后是IPerRequestDependencyResolver的WebApi实现,我们使用了GlobalConfiguration

public class PerRequestWebAPIDependencyResolver : IPerRequestDependencyResolver
{
    public T Get <T>() where T : class
    {
        var config = GlobalConfiguration.Configuration;
        return (T)config.DependencyResolver.Current().GetService(typeof(T));
    }
}

然后使用动作过滤器来帮助我们添加所需的DependencyResolver:

public class IoCScopeAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext filterContext)
    {
        //ioc context set
        filterContext.Request.SetCurrentDependencyScope();
    }
}

最后在您的控制器或基本控制器中     [AuthorizationFilter]     [IoCScope()]     public class YourController:ApiController     {

}

我希望这有帮助!