问题
This used to be a Web Forms
application and I'm rewriting it in MVC.
The connection name is stored in the user's details and depending on the user, I need to pass a different connection name.
In WebForms
this was solved by setting a Session
variable in Global.asax
and then every time the context is needed, I'd create the context in a using statement, passing the Session
variable in the context constructor like this:
using (IAccountContext db = new MainContext(Session["cn"]) { }
In MVC
I'm declaring the context as a field:
public class ManageController : BaseController
{
private readonly IAccountContext _db = new MainContext(ConnectionName);
// other actions
}
and I can't pass the non-static Session
variable. I tried using a BaseController
that the rest of the controllers would inherit and tried initializing the ConnectionName
in the constructor and in the Initialize method but neither worked
public class BaseController : Controller
{
public BaseController()
{
using (var db = new UserDbContext())
{
ConnectionName = (db.Users.Find(User.Identity.GetUserId())).ConnectionString;
}
}
public static string ConnectionName { get; set; }
protected override void Initialize(RequestContext requestContext)
{
using (var db = new UserDbContext())
{
ConnectionName = (db.Users.Find(User.Identity.GetUserId())).ConnectionString;
}
base.Initialize(requestContext);
}
}
I'm trying to avoid using a using
clause in every single Action
and rely on MVC
to dispose the context in the Dispose
method.
Any ideas on how to make this work?
EDIT:
Francesc Castells's answer looks promising, but when using the BaseController
the User.Identity.GetUserID()
call throws a NullReference exception. If the ManageController inherits from the original Controller
it works just fine. Am I forgetting to initialize something else?
This is the Stacktrace
[NullReferenceException: Object reference not set to an instance of an object.]
TestProj.Web.Controllers.BaseController..ctor() in E:\TestProj.Web\Controllers\BaseController.cs:14
TestProj.Web.Controllers.ManageController..ctor() in E:\TestProj.Web\Controllers\ManageController.cs:60
[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +119
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +232
System.Activator.CreateInstance(Type type, Boolean nonPublic) +83
System.Activator.CreateInstance(Type type) +11
System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +55
[InvalidOperationException: An error occurred when trying to create a controller of type 'TestProj.Web.Controllers.ManageController'. Make sure that the controller has a parameterless public constructor.]
System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +178
System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +76
System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +88
System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +194
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +50
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +103
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
回答1:
Use RequestContext
in Initialize()
method.
public class BaseController : Controller
{
protected string _connectionName;
protected override void Initialize(RequestContext requestContext)
{
if(requestContext.HttpContext.User.Identity.IsAuthenticated)
{
using (var db = new UserDbContext())
{
_connectionName = db.Users.Find(
requestContext.HttpContext.User.Identity.GetUserId())
.ConnectionString;
}
}
base.Initialize(requestContext);
}
}
Now you could simply inherit your controllers:
public class ManageController : BaseController
{
private IAccountContext _db
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
_db = new MainContext(_connectionName);
}
public ActionResult MyAction()
{
// now you have access _db variable
}
protected override void Dispose(bool disposing)
{
if(disposing)
{
_db.Dispose();
}
base.Dispose(disposing);
}
}
回答2:
Create the MainContext in the constructor, after the BaseController constructor has been executed and the ConnectionName initialized.
public class ManageController : BaseController
{
private readonly IAccountContext _db;
public ManageController()
{
// Here the base constructor has already been executed, so ConnectionName has the right value
_db = new MainContext(ConnectionName);
}
// other actions
}
But I would suggest a different approach: Use dependency injection with an IoC, for example Unity. Register your IAccountContext with an InjectionFactory which would be a simple method that would run the code you already have to get the connection string based on the User.Identity.GetUserId() and create the AccountContext passing the discovered connection string.
I don't know if you are familiar with what I am saying or not. If you need it I can write more detailed instructions.
来源:https://stackoverflow.com/questions/33454223/setting-the-contexts-connection-name-depending-on-the-user-that-logs-in