2015-09-18 193 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)); 
     }