asp.net-mvc – EF,ASP MVC依赖注入.多个并发请求和数据库连接的问题

前端之家收集整理的这篇文章主要介绍了asp.net-mvc – EF,ASP MVC依赖注入.多个并发请求和数据库连接的问题前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在开发一个基于NopCommerce的项目,该项目使用ASP MVC,Autofac和Entity Framework.我在从MVC Route中调用服务上的方法时会发生异常,这将使用EF调用DB.

在开发期间,一切正常 – 但是在负载测试期间,当有并发用户时,1或2个请求将崩溃,并且以下错误之一将记录到ELMAH.

System.InvalidOperationException ExecuteReader requires an open and available Connection. The connection’s current state is open.

System.InvalidOperationException ExecuteReader requires an open and available Connection. The connection’s current state is closed.

System.InvalidOperationException The connection was not closed. The connection’s current state is connecting.

System.ObjectDisposedException The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

System.ObjectDisposedException The operation cannot be completed because the DbContext has been disposed.

System.ArgumentException An item with the same key has already been added.

我通过在网站上打开许多链接然后使用Chrome插件刷新所有选项卡来测试这一点,同时模拟约25个请求到达网站.

该服务有两个从路径内部调用方法,然后这些相同方法中的一个可以从控制器操作中调用50次.有时异常是从Route内部触发的,有时它来自控制器内部.意味着路径的GetRouteData已经完成,将’flow’传递给控制器​​,然后在那里失败.但是,大多数情况下,路由中发生异常.当异常确实从控制器内部发生时,它位于异常发生的不同行.

有时一个方法会失败,另一个方法将运行正常,然后调用堆栈中的下一个方法失败.它每次都不同,但这些方法调用使用常用方法数据库中检索.

在此之前注册的其他2个路由映射到* {url},它们执行传入URL的数据库查找,并且在那里不会发生异常.因此,这种路由不是执行任何数据库工作的第一个操作.

该服务是依赖注册的: –

builder.RegisterControllers(typeFinder.GetAssemblies().ToArray());

builder.Register<IDbContext>(c => new NopObjectContext(DataSettings.DataConnectionString)).InstancePerLifetimeScope();

builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();

builder.RegisterType<MemoryCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_static").SingleInstance();

builder.RegisterType<MyService>().As<IMySerivce>()    
               .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))    
               .InstancePerLifetimeScope();

Controller通过Constructor Injection接收服务: –

protected readonly IMyService _myService;

public MyController(IMyService myService)
{
    _myService = myService;
}

该路由解析了该服务如下:

public override RouteData GetRouteData(HttpContextBase httpContext)
{
    var _myService = EngineContext.Current.Resolve<IMyService>();
    myService.databaSEOperation(); <--- falls over here w/ concurrency
}

有没有想过为什么会出现这些错误?以及如何解决它们?

根据我的理解,我们的DBContext似乎是在两个请求之间共享,但是在我的依赖注册中,我告诉它要解析为一个生命周期得分 – 这对于每个请求都应该是唯一的.我已经阅读了很多关于这个异常的原因以及如何依赖于依赖注入框架来控制依赖关系的生命周期并管理其资源的处理 – 事情就是这样,事情似乎正在崩溃.

关于服务本身,它的工作方式与应用程序中的所有其他服务一样 – 没有什么特别突出和不同的.

我已经粘贴了完整的异常堆栈跟踪http://pastebin.com/XYEwRQsv以及有问题的代码行.

编辑:我在连接字符串中使用MultipleActiveResultSets = True.该服务处理的实体是独立的,即它与其他实体没有关系,因此一旦执行了查询,就不会有子实体的多次迭代,因为关于这些异常的其他答案指出了.

编辑:这是抛出异常的行.

public string GetFriendlyUrlString(string originalUrl)
{
    var friendly =  _cacheManager.Get(originalUrl.ToLower(),() =>
            (from f in _friendlyUrlRepository.ReadOnlyTable      <--------- Here
             where f.OriginalUrl == originalUrl
             select f.Url).ToList().SingleOrDefault());

    return friendly ?? originalUrl;
}

例外是

ExecuteReader requires an open and available Connection. The connection's current state is closed.

这太奇怪了.在我的路线中,有4个地方可以进行数据库调用.我的例外是95%的时间来自这4次呼叫中的一次 – 通常是第一次失败,但有时第一次数据库呼叫将会正常,而其他呼叫则会失败.我很少看到异常来自控制器内部,这条路线呃……路线到.再次,使用该控制器异常,然后实际的数据库连接问题发生在5行代码中的一行 – 再次表明它已经使许多数据库调用失败了.

解决方法

Autofac的InstancePerLifetimeScope不保证每个http请求的唯一范围.它保证的是,在解析它的生命周期范围内只有一个组件实例.
因此,例如,如果从根容器中解析InstancePerLifetimeScope组件,则该组件在应用程序的过程中将基本上充当单例.

如果您在单独注册的服务(例如,全局操作过滤器)中解析您的依赖项,那么您的依赖项(dbContext或其他)将不会在每个请求上被处理 – 它将保持打开状态,泄漏内存并流血连接,直到它被处理或关闭您的应用程序.

假设您正在使用Autofac Mvc集成,作为一个实验,也许尝试TEMPORARILY将您的dbContext注册为InstancePerMatchingLifetimeScope(“AutofacWebRequest”) – 这实际上相当于完成Will Appleby所说的,但与新版本没有亲和力Autofac比你可能使用的.

如果这解决了你的问题,那么基本上,@ Apple Appleby是对的.您需要将所有InstancePerLifetimeScope组件注册为InstancePerRequest(从版本3.4.0开始新添加到Autofac核心)或InstancePerHttpRequest(在3.4.0之前的Autofac MVC集成中可用).

否则,如果我猜对了,你可能会遇到这样的异常:

DependencyResolutionException: No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.

如果发生这种情况,您仍然希望将注册更改为InstancePerRequest / InstancePerHttpRequest,但现在您手头有更深入的任务.您可能拥有一个包含dbContext人质的单例(或其他比httpRequest更长的对象).您需要确定更长寿命的组件,并弄清楚如何释放该自组织依赖 – 这可能需要重构组件以更好地遵守Autofac的范围行为.

您需要参考此处的问题排查部分:
http://autofac.readthedocs.org/en/latest/faq/per-request-scope.html#troubleshooting-per-request-dependencies

这是一段摘录:

Common causes for this include:

  • Application registrations are being shared across application types.
  • A unit test is running with real application registrations but isn’t simulating per-request lifetimes.
  • You have a component that lives longer than one request but it takes a dependency that only lives for one request. For example,a singleton component that takes a service registered as per-request.
  • Code is running during application startup (e.g.,in an ASP.NET Global.asax) that uses dependency resolution when there isn’t an active request yet.
  • Code is running in a “background thread” (where there’s no request semantics) but is trying to call the ASP.NET MVC DependencyResolver to do service location.

猜你在找的asp.Net相关文章