简体   繁体   English

在运行时从ASP.Net MVC Identity 2的URL构造一个connectionString

[英]Construct a connectionString at runtime from the url for ASP.Net MVC Identity 2

I'm working on a multi-tenant web application using ASP.Net C# MVC 5. For reasons not relevant to this question, I want a separate database for each tenant, including the Identity 2.0 part. 我正在使用ASP.Net C#MVC 5开发多租户Web应用程序。出于与该问题无关的原因,我希望为每个租户(包括Identity 2.0部分)建立一个单独的数据库。 Also, I do not want to use the tenant name as hostname before my domain (ie http://tenant.myapp.com ). 另外,我不想在域之前使用租户名称作为主机名(即http://tenant.myapp.com )。 One obvious solution that's generally easy to implement is to have the tenant name in the MVC route configuration: 通常很容易实现的一个显而易见的解决方案是在MVC路由配置中使用租户名称:

namespace MyApp
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{tenant}/{controller}/{action}/{id}",
                defaults: new { tenant = "Demo", controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

This works fine, but the approach seems to have one drawback. 这很好用,但是这种方法似乎有一个缺点。 I was hoping to construct a connectionString at runtime, using the tenant from the RouteData.Values and passing that string to my ApplicationDbContext. 我希望在运行时使用RouteData.Values中的租户构造一个connectionString,并将该字符串传递给我的ApplicationDbContext。

I suspect that, during initialization, the Identity framework is initialized as a singleton. 我怀疑在初始化期间,将Identity框架初始化为单例。 From the template MVC code you get the following Startup partial. 从模板MVC代码中,您可以获得以下“启动”部分。

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        // Configure the db context, user manager and signin manager to use a single instance per request
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

        ...
    }
}

The remark states that a single instance is created "per request", but this code looks to be hit only once when the app initializes, not per request. 备注指出,将按“每个请求”创建一个实例,但是该代码看起来仅在应用初始化时被命中一次,而不是每个请求被命中一次。 What I was hoping to do, is do something like this, passing the tenant into the constructor of ApplicationDbContext. 我希望做的是做类似的事情,将租户传递给ApplicationDbContext的构造函数。

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        // Get the tenant from the routeData
        string tenant = "YourCompany"; // Switch to something different from the default set during route configuration, for testing purposes.
        if (HttpContext.Current.Request.RequestContext.RouteData.Values["tenant"] != null) tenant = HttpContext.Current.Request.RequestContext.RouteData.Values["tenant"].ToString();

        // Configure the db context, user manager and signin manager to use a single instance per request
        app.CreatePerOwinContext(() => ApplicationDbContext.Create(tenant));
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

        ...
    }
}

But, alas, the HttpContext.Current.Request.RequestContext has no RouteData.Values since we're initiliazing when the code hits and there is no request. 但是,可惜,HttpContext.Current.Request.RequestContext没有RouteData.Values,因为我们在代码命中并且没有请求时进行初始化。 This code always results in a connectionString with 'YourCompany' as tenant. 此代码始终导致一个以“ YourCompany”为租户的connectionString。

I have developed several years ago exactly the same scenario... 几年前,我已经开发出完全相同的场景...

Startup & RouteConfig are executed normally in global.asax/Application_Start , only once per iis-site start. Startup和RouteConfig通常在global.asax / Application_Start中执行,每个iis站点启动仅执行一次。

Try to create and set your DbContext in global.asax/Application_BeginRequest. 尝试在global.asax / Application_BeginRequest中创建并设置您的DbContext

And some other hint: perhaps you should have only one DbContext per HttpRequest, by holding it in HttpContext.Current.Items["dbcontext"] for use in child actions or similiar. 还有其他一些提示:通过将其保存在HttpContext.Current.Items [“ dbcontext”]中,以便在子操作或类似操作中使用,每个HttpRequest可能只应具有一个DbContext。

Let me know if you succeed or need more hints 让我知道您是否成功或需要更多提示

Frank 坦率

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM