2015-09-18 2 views
1

ВведенияASP.NET MVC на несколько Арендатор приложения с несколькими базами данных

я работаю над приложением несколько квартиросъемщиков, что я программирование в ASP.NET MVC с EF6 и Sql Server.

Моя структура базы данных:

  • 1 базовой база данных, которая содержит свойство арендатора (например, имя/поддомен/Каталог).

    (Catalog этого имя его присвоенные баз данных)

  • 1 базы данные для каждого клиента

Для зная арендатора я использую поддомен поиск:

Entity Framework

базы и базы данных приложения по умолчанию я добавил файл в ADO.NET Entity data model (EDMX).

Контроллеры

Чтобы получить правильный жилец я создал новый пользовательский контроллер, который переопределяет метод OnActionExecuting. Если арендатор существует, я добавляю идентификатор арендатора к переменным маршрута.

// 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

I alwayse использовать IoC для применения нескольких арендаторов. В IoC я регистрирую адаптер с PerWebRequest LifeStyle, чтобы получить конфигурацию текущего арендатора из HttpContext. Мой аддитер получает в конструкторе Func для получения Url из HttpContext.

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)); 
     } 
Смежные вопросы