Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Autofac解决单例问题会造成瓶颈_C#_Asp.net Mvc_Autofac - Fatal编程技术网

C# Autofac解决单例问题会造成瓶颈

C# Autofac解决单例问题会造成瓶颈,c#,asp.net-mvc,autofac,C#,Asp.net Mvc,Autofac,我在asp.net MVC应用程序中使用Autofac时遇到了锁定问题。任何时候,只要一个服务依赖于一个单例,就会从根生存期范围解析该服务。这是因为Autofac: 从根作用域解析单例组件 解析根作用域中具有必须从根解析的依赖项的所有组件 此外,当从任何作用域解析时,Autofac会锁定该作用域。我认为这些都是很好的设计决策。我的问题是行为不端的类依赖于单例,并且的构造函数很慢。这为其他需要解决单例问题的人造成了瓶颈 因为这是在MVC应用程序中,所以每个请求都被映射到某个被注入构造函数的MV

我在asp.net MVC应用程序中使用Autofac时遇到了锁定问题。任何时候,只要一个服务依赖于一个单例,就会从根生存期范围解析该服务。这是因为Autofac:

  • 从根作用域解析单例组件
  • 解析根作用域中具有必须从根解析的依赖项的所有组件
此外,当从任何作用域解析时,Autofac会锁定该作用域。我认为这些都是很好的设计决策。我的问题是行为不端的类依赖于单例,并且的构造函数很慢。这为其他需要解决单例问题的人造成了瓶颈

因为这是在MVC应用程序中,所以每个请求都被映射到某个被注入构造函数的MVC控制器。此外,我的大多数控制器依赖于各种单例服务(日志记录、缓存等)

对于具有快速构造函数的对象,这不是问题。但一旦请求一个写得不好的类,我的吞吐量就会下降,因为每个新请求都会被那个行为不端的构造函数阻塞。例如:

考虑到这三个类别

//registered as SingleInstance()
class MySingleton {}

class BadTransient
{
    public BadTransient(MySingleton s)
    { Thread.Sleep(5000); }
}  

class FriendlyTransient {}

using(var scope = container.BeginLifetimeScope("nested scope"))
{

    //resolves from child scope
    var myFriend = scope.Resolve<FriendlyTransient>(); 

    //resolves from root because it's a singleton
    var singleton = scope.Resolve<MySingleton>(); 

    //resolves from root because it has a singleton dependency. 
    //**** this locks the root scope for 5 seconds
    //****   no one else can resolve singletons.
    var troublemaker = scope.Resolve<BadTransient>();         
}
使用(var scope=container.BeginLifetimeScope(“嵌套范围”))
{
//从子范围解析
var myFriend=scope.Resolve();
//从根解析,因为它是单例
var singleton=scope.Resolve();
//从根目录解析,因为它具有单例依赖项。
//****这会将根作用域锁定5秒
//****没有其他人可以解决单身问题。
var troublemaker=scope.Resolve();
}
有什么方法可以避免这个瓶颈吗?

显而易见的答案是拥有快速的构造函数。事实上,并不是我的代码库中的所有构造函数都能保证是快速的。有很多遗留代码,有很多代码依赖于第三方代码,有一些代码看起来很快,但依赖于不快的代码,有一些代码通常很快,但在奇怪的情况下会崩溃,等等。对开发人员的教育只在一定程度上起作用

我们一直在修复构造函数,因为我们发现他们,但我需要一个更积极的解决方案。让我的用户做我的QA是不可接受的


注意:我不太关心那些不依赖单例的慢构造函数。它们将锁定其生存期范围,但不会阻止其他线程

我同意@nemesv的观点,即对象构造应该快速,但这也意味着不在激活的中初始化。相反,您应该在第一次使用组件时惰性地执行此操作。例如,通过在内部实现代理或一些
惰性


但如果您有一个具有相当极端吞吐量和并发特性的应用程序,并且通过性能分析验证锁定是一个瓶颈,那么您可以考虑切换到一个无锁的IOC容器。

< P>我通过使用AutoFAC的闭包语法重新注册所有的单体来修复这个问题。 这将在autofac中保留构造逻辑,但从子生命周期范围解析单例。实质上:

builder.Register<MySingleton>().AsSelf().AsImplementedInterfaces();
//.. other registrations

var container = builder.Build();

// now resolve each singleton, forcing all to be constructed if not already
//   and then register the instance
var builder2 = new ContainerBuilder();

var mySingleton = container.Resolve<MySingleton>();
builder2.Register(c => mySingleton).AsSelf().AsImplementedInterfaces();
builder2.Update(container);

//.....
var scope = container.BeginLifetimeScope("child scope");   
scope.Resolve<MySingleton>(); //not resolved from root!
builder.Register();
//.. 其他注册
var container=builder.Build();
//现在解析每个单例,如果尚未构造,则强制构造所有单例
//然后注册实例
var builder2=新的ContainerBuilder();
var mySingleton=container.Resolve();
builder2.Register(c=>mySingleton.AsSelf().AsImplementedInterfaces();
builder2.更新(容器);
//.....
var scope=container.BeginLifetimeScope(“子范围”);
scope.Resolve()//不是从根本上解决的!
然后,由于有许多单例,我可以通过编程方式查询它们的类型,因此我编写了一个函数,该函数接受类型列表并运行上述代码。它必须执行一些反射向导,尽管只在应用程序启动时在正常的autofac注册码末尾运行

void ReRegisterSingletons(IContainer container, List<Type> singletonTypes)
{
     var builder= new ContainerBuilder();
     foreach(var type in singletonTypes)
     {

          var resolved = container.Resolve(type);
          var method = this.GetType().GetMethod("ReRegister").MakeGenericMethod(new []{type});
          method.Invoke(this, new []{resolved});
     }

     builder.Update(container);
}

void Register<T>(ContainerBuilder builder, object singleton)
{
    var theObj = (T)singleton;

     //a typed lambda was the only way I could get both the class name and the interface names to resolve from the child scope. RegisterInstance still resolves from root, and the non-generic lamba register can resolve the class name from child scope but not the interface names...
    builder.Register(c => theObj).AsSelf().AsImplementedInterfaces();
}
void重新注册Singleton(IContainer容器,列出Singleton类型)
{
var builder=new ContainerBuilder();
foreach(singletonTypes中的变量类型)
{
var resolved=container.Resolve(类型);
var method=this.GetType().GetMethod(“重新注册”).MakeGenericMethod(新[]{type});
调用(这是新的[]{resolved});
}
生成器更新(容器);
}
无效寄存器(ContainerBuilder生成器,对象单例)
{
var-theObj=(T)单态;
//类型化lambda是我从子作用域获取要解析的类名和接口名的唯一方法。RegisterInstance仍然从根解析,非泛型lamba寄存器可以从子作用域解析类名,但不能解析接口名。。。
builder.Register(c=>theObj.AsSelf().asiimplementedinterfaces();
}

我认为Autofac的工作方式没有任何问题。这里我看到的问题是,您有一些
badtransive
类,其中构造函数中有昂贵的逻辑。我将把所有的逻辑从构造函数移到一个
Initialize
方法,这个方法可以被Autofac
OnActivated
事件调用。因此,在整个初始化过程中不需要锁定作用域。@nemesv请查看我的编辑,请查看我的编辑。我喜欢使用惰性慢构造函数的想法,但我正在寻找一种更全面的解决方案——假设我无法保证所有构造函数都是快速的,那么如何保持应用程序的响应性呢?您推荐任何无锁容器吗?您当然可以将所有单例注册为瞬态。如果我没弄错的话,这会阻止全局锁定。我知道的唯一(好的)无锁容器是(我是SimpleInjector的首席开发人员)。关于简单喷油器的一个警告。它有一套非常有限的精心挑选的功能。这种设计试图引导开发者进入成功的深渊。但既然你是d