如何在共享项目控制器中覆盖上下文与会话管理器实例?
解决共享MVC控制器替换Context和SessionManager的问题
你的问题本质是共享项目里的控制器硬编码了具体依赖的实例化逻辑,导致即使Proj.MVC有自己的ProjContext和ProjSessionManager,共享控制器还是会用原来的SharedContext和SharedSessionManager。要解决这个问题,我们可以通过**依赖注入(DI)**重构共享项目的代码,让控制器依赖抽象而非具体实现,这样在Proj.MVC里就能轻松替换成自定义实现,同时完全保留共享控制器的复用性。
第一步:重构共享项目,让控制器依赖抽象
首先,我们需要把共享项目里的控制器、SessionManager改成依赖抽象接口,而不是直接实例化具体类。这样就能把依赖的创建权交给外部容器,而不是控制器自己。
修改后的共享项目代码:
namespace Shared.MVC { // 确保已定义这两个核心接口(如果还没定义的话) public interface ISharedContext : IDisposable { DbSet<MyType> MyList { get; set; } } public interface ISessionManager { // 这里放上SessionManager需要对外暴露的方法 } public abstract class BaseMVCController : Controller { // 改为只读字段,通过构造函数注入依赖 protected readonly ISharedContext DB; protected readonly ISessionManager SessionManager; public BaseMVCController(ISharedContext dbContext, ISessionManager sessionManager) { DB = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); SessionManager = sessionManager ?? throw new ArgumentNullException(nameof(sessionManager)); } protected override void Initialize(RequestContext requestContext) { base.Initialize(requestContext); // 移除原来的DB和SessionManager实例化代码,依赖已经通过构造注入了 } } public abstract class SharedApiController : ApiController { protected readonly ISharedContext DB; protected readonly ISessionManager SessionManager; public SharedApiController(ISharedContext dbContext, ISessionManager sessionManager) { DB = dbContext; SessionManager = sessionManager; } protected override void Initialize(HttpControllerContext controllerContext) { base.Initialize(controllerContext); // 移除实例化代码 } } public class SharedSessionManager : ISessionManager { protected readonly ISharedContext DB; // 只保留依赖ISharedContext的构造函数,移除无参构造(避免硬编码实例化) public SharedSessionManager(ISharedContext myContext) { DB = myContext ?? throw new ArgumentNullException(nameof(myContext)); } } public class SharedContext : DbContext, ISharedContext { public SharedContext() : base("contextName") { } public virtual DbSet<MyType> MyList { get; set; } } }
第二步:在Proj.MVC中实现自定义的Context和SessionManager
接下来,在你的Proj.MVC项目里实现自己的扩展类,继承共享项目的基础类:
namespace Proj.MVC { // 扩展SharedContext,添加自定义逻辑 public class ProjContext : SharedContext { public ProjContext() : base("ProjConnectionString") { // 这里可以替换成项目专属的连接字符串 } // 添加项目特有的DbSet或重写逻辑 public DbSet<ProjCustomType> ProjCustomList { get; set; } } // 扩展SharedSessionManager,添加自定义逻辑 public class ProjSessionManager : SharedSessionManager { public ProjSessionManager(ISharedContext dbContext) : base(dbContext) { // 在这里添加项目专属的Session管理逻辑 } // 重写或新增方法,实现自定义逻辑 } }
第三步:配置依赖注入容器,替换实现
现在需要在Proj.MVC里配置DI容器,告诉系统当需要ISharedContext时提供ProjContext,需要ISessionManager时提供ProjSessionManager。这里以ASP.NET MVC 5常用的Unity容器为例:
- 先通过NuGet安装
Unity.Mvc5和Unity.WebApi(如果用到Web API的话)。 - 在
App_Start文件夹下创建UnityConfig.cs:
using Unity; using Unity.Mvc5; using Unity.WebApi; using Shared.MVC; using Proj.MVC; namespace Proj.MVC.App_Start { public static class UnityConfig { public static void RegisterComponents() { var container = new UnityContainer(); // 注册自定义实现,使用HierarchicalLifetimeManager确保每个请求一个实例(适合DbContext) container.RegisterType<ISharedContext, ProjContext>(new HierarchicalLifetimeManager()); container.RegisterType<ISessionManager, ProjSessionManager>(new HierarchicalLifetimeManager()); // 配置MVC的依赖解析器 DependencyResolver.SetResolver(new UnityDependencyResolver(container)); // 如果用到Web API,还要配置API的解析器 GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container); } } }
- 在
Global.asax的Application_Start方法里调用配置:
protected void Application_Start() { // 其他初始化代码... UnityConfig.RegisterComponents(); }
为什么这个方案可行?
- 遵循依赖倒置原则:共享控制器依赖抽象接口,不再直接依赖具体类,所以可以灵活替换实现。
- 完全保留复用性:共享项目的控制器不需要做任何修改,所有项目都能复用,只是在具体项目里配置不同的实现即可。
- 生命周期管理:DI容器可以帮你管理依赖的生命周期(比如每个请求创建一个DbContext实例),避免资源泄漏。
如果不想用第三方DI容器,也可以自己实现简单的IDependencyResolver,但成熟的容器会更省心,尤其是处理复杂的依赖关系和生命周期时。
内容的提问来源于stack exchange,提问作者Cyber
相关产品推荐
相关产品推荐

