我的主(根)容器以这种方式定义:
builder.RegisterType<DbContext>().InstancePerLifetimeScope(); // EF Db Context builder.RegisterType<ChatService>().As<IChatService>().SingleInstance(); // classic "service",has dependency on DbContext builder.RegisterType<ChatHub>().ExternallyOwned(); // SignalR hub builder.RegisterType<UpdateStatusesJob>().InstancePerDependency(); // Hangfire job builder.RegisterType<HomeController>().InstancePerRequest(); // ASP.NET MVC controller IContainer container = builder.Build();
对于MVC,我使用的是Autofac.MVC5 nuget包.依赖性解析器:
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
对于SignalR,我正在使用Autofac.SignalR nuget包.依赖性解析器:
GlobalHost.DependencyResolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container);
我的signalR hub以这种方式实例化(http://autofac.readthedocs.org/en/latest/integration/signalr.html#managing-dependency-lifetimes):
private ILifetimeScope _hubScope; protected IChatService ChatService; public ChatHub(ILifetimeScope scope) { _hubScope = scope.BeginLifetimeScope(); // scope ChatService = _hubScope.Resolve<IChatService>(); // this service is used in hub methods } protected override void dispose(bool disposing) { // Dipose the hub lifetime scope when the hub is disposed. if (disposing && _hubScope != null) { _hubScope.dispose(); } base.dispose(disposing); }
对于Hangfire,我使用的是Hangfire.Autofac包:
config.UseActivator(new AutofacJobActivator(container));
作业以这种方式实例化:
private readonly ILifetimeScope _jobScope; protected IChatService ChatService; protected BaseJob(ILifetimeScope scope) { _jobScope = scope.BeginLifetimeScope(); ChatService = _jobScope.Resolve<IChatService>(); } public void dispose() { _jobScope.dispose(); }
问题/问题:
我总是在集线器和作业中获得相同的DbContext实例.我希望所有集线器实例都能获得相同的ChatService,但DbContext(它是ChatService的依赖项)将始终是一个新实例. Hangfire的工作也应该是一样的.
可以这样做,还是我错过了什么?
更新1:
思考(睡过头)后,我想我有两个选择.我仍然希望保持“每个请求的会话”(“每个集群的会话”,“每个作业的会话”).
选项1:
更改所有服务将具有InstancePerLifetimeScope.实例化服务并不昂贵.对于维护某种状态的服务,我将创建另一个“存储”(类),它将是SingleInstance并且不依赖于session(DbContext).我认为这也适用于枢纽和工作.
选项2:
创建@Ric .Net建议的某种工厂.像这样的东西:
public class DbFactory: IDbFactory { public MyDbContext GetDb() { if (HttpContext.Current != null) { var db = HttpContext.Current.Items["db"] as MyDbContext; if (db == null) { db = new MyDbContext(); HttpContext.Current.Items["db"] = db; } return db; } // What to do for jobs and hubs? return new MyDbContext(); } } protected void Application_EndRequest(object sender,EventArgs e) { var db = HttpContext.Current.Items["db"] as MyDbContext; if (db != null) { db.dispose(); } }
我认为这对MVC有用,但我不知道它是否适用于集线器(每个集线器调用是集线器的新实例)和作业(每次运行的作业都是作业类的新实例) .
我倾向于选项1.你怎么看?
非常感谢!
解决方法
I want that all hub instances will get the same ChatService,but the DbContext (which is dependency of ChatService) will always be a new instance.
你在这里基本上说的是:
“我的汽车是由依赖车库的汽车公司进行维修的,但每次我带车时我都希望车库成为新车”.
当您在其他组件中注入(完全构建实例,包括依赖项)ChatService时,当然还会构建其他依赖项,无论它们是否具有其他类型的生活方式.当创建一个比它注入的对象更短的生命周期的对象时,你创建了一个所谓的’captive dependency‘
在ChatService中获取DbContext的新“实例”的唯一方法不是注入DbContext本身,而是注入一个DbContextFactory,只要你使用它就会为你创建DbContext.
实现看起来像:
public class DbContextFactory { public DbContext Create() { return new DbContext(); } } //usage: public class ChatService { private readonly DbContextFactory dbContextFactory; public ChatService(DbContextFactory dbContextFactory) { this.dbContextFactory = dbContextFactory; } public void SomeMethodInChatService() { using (var db = this.dbContextFactory.Create()) { //do something with DbContext } } }
可以使用Singleton Lifestyle在AutoFac中注册DbContextFactory.
然而,这可能不是你的目标.因为在这种情况下,每次使用DbContext时都会得到一个新的.另一方面,新的DbContext可能是最安全的方法,你可以阅读here.
这个伟大的答案值得阅读,原因不止一个,因为它解释了如何使用command / handler pattern,这应该非常适合您的情况.
这将使您的聊天服务完全不知道DbContext,它改进了应用程序的’SOLID‘设计,并创建了测试ChatService的可能性,这是在直接注入DbContext或DbContextFactory时几乎可以撤销的.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。