2015-09-18 37 views
1

介绍ASP.NET MVC多租户应用程序使用多个数据库

我对我在ASP.NET MVC与EF6和SQL Server AM程序多租户应用程序的工作。

我的数据库结构:保持该承租人性质(例如名称/子域/目录)

  • 1个碱基的数据库。

    (目录是分配给他的数据库的名称)

  • 1数据库为每一个客户

对于知道我用一个子查询房客:

实体框架

基地,我已经加了ADO.NET Entity data model(EDMX)文件的默认应用程序的数据库。

控制器

为了得到我已经创建了覆盖OnActionExecuting方法的新的自定义控制器正确的租户。如果租户存在,我将租户的Id添加到路由变量中。

// Override OnActionExecuting method which is called every time before the action is called 
protected override void OnActionExecuting(ActionExecutingContext filterContext) 
{ 
    var fullAddress = filterContext.HttpContext.Request.Headers["Host"].Split('.'); 
    if (fullAddress.Length < 3) 
    { 
     // some code.... 
    } 
    else 
    { 
     var tenantSubdomain = fullAddress[0]; 

     Account currentTenant = db.Accounts.FirstOrDefault(t => t.Subdomain.Equals(tenantSubdomain, StringComparison.CurrentCultureIgnoreCase)); 

     if (currentTenant != null) 
      filterContext.RouteData.Values["tenant"] = currentTenant.Id; 
     else 
      filterContext.Result = new HttpStatusCodeResult(404); 
    } 

    base.OnActionExecuting(filterContext); 
} 

直到这里一切工作正常。现在我被卡住了,用于创建和存储与租户分配的数据库的连接。

要连接到我需要使用下面的代码数据库:

public SkycountAppEntities dbApp = new SkycountAppEntities(GetConnString(tenant.Catalogue)); 
//GetConnString renders the connectionstring which includes the tenants catalogue. 

但我在哪里放置这一行,所以我没有在每一个行动,把这个做?或者我可以将它缓存在身份验证cookie的某个位置?

有人能指导我正确的方向吗?

UPDATE

这样的工作,但现在我要创建的每个控制器的每一个动作的连接。

// POST: Account/Login 
[HttpPost] 
[AllowAnonymous] 
[ValidateAntiForgeryToken] 
public ActionResult Login([Bind(Include = "Username,Password")] User user, string returnUrl) 
{ 
    using (SkycountAppEntities dbApp = new SkycountAppEntities(DbEntityFramework.RenderConnectionString(_SkycountAccount.Catalog))) 
    { 
     User _user = dbApp.Users.FirstOrDefault(u => u.Username.Equals(user.Username)); 
     if(_user != null && _user.Active && Crypto.VerifyHashedPassword(_user.Password, user.Password)) 
     { 
      FormsAuthentication.SetAuthCookie(user.Username, false); 

      if (String.IsNullOrEmpty(returnUrl) || !Url.IsLocalUrl(returnUrl)) 
       return RedirectToAction("Index", "Home"); 
      else 
       return Redirect(returnUrl); 
     } else 
     { 
      TempData["failed"] = true; 

      return RedirectToAction("Login", new { returnUrl = returnUrl }); 
     } 
    } 
} 
+0

你应该学习和理解依赖注入。 – DarthVader

回答

0

我一直使用IoC到多租户应用程序。在IoC中,我使用PerWebRequest LifeStyle注册适配器,以便从HttpContext获取当前租户的配置。我的addapter进入构造函数Func从HttpContext获取Url。

public class CurrentTenantAdapter : ICurrentTenantAdapter 
    { 
     private readonly IGlobalCache cache; 
     private readonly IMongoRepository repository; 

     public CurrentTenantAdapter(Func<string> getTenantIdFunc, IMongoRepository repository, IGlobalCache cache) 
     { 
      this.GetTenantIdFunc = getTenantIdFunc; 
      this.repository = repository; 
      this.cache = cache; 
     } 

     public async Task<ITenant> GetTenantAsync() 
     { 
      string tenantId = GetTenantIdFunc(); 
      if (string.IsNullOrEmpty(tenantId)) 
      { 
       return null; 
      } 

      Tenant tenant = await this.cache.GetAsync<Tenant>(tenantId); 
      if (tenant == null) 
      { 
       tenant = await this.repository.QueryAsync(new GetTenantById(tenantId)); 
       if (tenant != null) 
       { 
        await this.cache.StoreAsync(tenantId, tenant, null); 
       } 
      } 

      return tenant; 
     } 

     public string GetTenantId() 
     { 
      return GetTenantIdFunc(); 
     } 

     protected Func<string> GetTenantIdFunc { get; set; } 
    } 
} 

此适配器,您可以在您的存储库中使用。例如,存储库可以使用来自租户配置的连接字符串创建新的EF上下文。我使用这个适配器的MongoDb来连接正确的数据库。

private async Task<IMongoCollection<T>> ConnectDbAndGetCollectionAsync<T>(string databaseName) 
     { 
      var isMainModel = IsMainModel<T>(); 
      if (string.IsNullOrEmpty(databaseName)) 
      { 
       databaseName = mainDatabaseName.Value; 
       if (isMainModel == false && this.tenantConfiguration != null) 
       { 
        ITenant tenant = await this.tenantConfiguration.GetTenantAsync(); 
        DatabasesConfiguration databaseConfiguration = tenant.DatabasesConfiguration; 
        if (databaseConfiguration != null) 
        { 
         if (databaseConfiguration.MongoDatabaseConfiguration != null) 
         { 
          databaseName = databaseConfiguration.MongoDatabaseConfiguration.DatabaseName; 
         } 
        } 
       } 
      } 

      var attribute = AttributesHelper.GetAttributeValue<MongoCollectionName>(typeof(T)); 
      string collectionName = attribute.CollectionName; 

      IMongoDatabase db = await GetDatabaseAsync(databaseName); 
      return await Task.FromResult(db.GetCollection<T>(collectionName)); 
     }