5

La situazioneIniettare/Managing a runtime mutevoli connectionStrings utilizzando Entity Framework, Dependency Injection, unità di lavoro e modelli Repository

Sto costruendo un'applicazione web utilizzando le tecniche dei titoli menzionati. Questa applicazione sarà simile a un sistema CMS per più client. Il client deve accedere a questo sistema utilizzando il suo nome aziendale e le credenziali di accesso. Con il nome della società fornito, mi collego a un database (DbContext statico, stessa connessione ogni volta) in cui sono archiviate tutte le informazioni del database dei clienti e cerco il database specifico di questo client (ogni client ha il proprio con lo stesso design). Tutto funziona bene Ora ecco la parte difficile. Per continuare la procedura di login ho bisogno di iniettare in qualche modo o pigro il repository usando l'altro DbContext con una stringa di connessione che viene generata dal risultato dell'altro database.

quello che ho

2 DB Contesti generato da un db esistente, uno statico e uno dinamico, se possibile.

Quindi le classi. Generici Repository classi/interfacce

public interface IRepository 
{ 
    void Submit(); 
} 

public interface IRepository<TEntity, TContext> : IRepository 
    where TEntity : class 
    where TContext : DbContext 
{ 
    //crud stuff 
} 
public abstract class GenericRepository<TEntity, TContext> : IRepository<TEntity, TContext> 
    where TEntity : class 
    where TContext : DbContext 
{ 
    private TContext _dataContext; 
    private IUnitOfWork _unitOfWork; 
    private readonly IDbSet<TEntity> dbset; 

    protected GenericRepository(IUnitOfWork unitOfWork) 
    { 
     _unitOfWork = unitOfWork; 
     _unitOfWork.Register(this); 
    } 
} 

Unità di classe lavoro/interfaccia

public interface IUnitOfWork 
{ 
    void Register(IRepository repository); 
    void Commit(); 
} 
public class UnitOfWork : IUnitOfWork 
{ 
    private readonly Dictionary<string, IRepository> _repositories; 
    private HttpContextBase _httpContext; 


    public UnitOfWork(HttpContextBase httpContext)   
    {    
     _httpContext = httpContext; 
    } 

    public void Register(IRepository repository) 
    { 
     _repositories.Add(repository.GetType().Name, repository); 
    }   

    public void Commit() 
    { 
     _repositories.ToList().ForEach(x => x.Value.Submit()); 
    } 
} 

Poi un contesto/entità repository specifico

public class EmployeeRepository : GenericRepository<tbl_Medewerker, CustomerDbEntities>, IEmployeeRepository 
{ 
    public EmployeeRepository(IUnitOfWork unitOfWork) 
     : base(unitOfWork) 
    { 
    } 
} 

public interface IEmployeeRepository : IRepository<tbl_Medewerker, CustomerDbEntities> 
{ 

} 

Quindi il servizio che implementa il repository

public interface IEmployeeLoginService 
{ 
    tbl_Medewerker GetEmployeeByLogin(string username, string password); 
    tbl_Medewerker GetEmployeeByID(Guid id); 
} 

public class EmployeeLoginService : IEmployeeLoginService 
{ 
    private readonly IEmployeeRepository _employeeRepository; 

    public EmployeeLoginService(IEmployeeRepository employeeRepository) 
    { 
     _employeeRepository = employeeRepository; 
    } 

    public tbl_Medewerker GetEmployeeByLogin(string username, string password) 
    { 
     return _employeeRepository.Get(e => e.MedewerkerNaam.ToLower() == username.ToLower() && e.Password == password); 
    } 

    public tbl_Medewerker GetEmployeeByID(Guid id) 
    { 
     return _employeeRepository.GetById(id); 
    } 
} 

, infine, il controllore che implementa tale servizio e lo utilizza nell'azione login

public class AccountController : BaseController 
{ 
    IConnectionService _connectionService; 
    IEmployeeLoginService _employeeService; 

    public AccountController(IConnectionService connectionService, IEmployeeLoginService employeeService) 
    { 
     _connectionService = connectionService; 
     _employeeService = employeeService; 
    } 



    [AllowAnonymous, HttpPost] 
    public ActionResult Login(LoginModel login) 
    { 
     if ((Settings)Session["Settings"] == null) 
     { 
      Settings settings = new Settings(); 

      settings.company = _connectionService.GetCompanyName(login.CompanyName); 
      if (settings.company != null) 
      { 
       settings.licence = _connectionService.GetLicenceByCompanyID(settings.company.Company_id); 
       if (settings.licence != null) 
       { 
        settings.connectionStringOrName = string.Format(@"Data Source={0};Initial Catalog={1};User ID={2};Password={3};Application Name=EntityFrameworkMUE", settings.licence.WS_DatabaseServer, settings.licence.WS_DatabaseName, settings.licence.WS_DatabaseUID, settings.licence.WS_DatabasePWD);       
        Session["Settings"] = settings; 

        settings.user = _employeeService.GetEmployeeByLogin(login.UserName, login.Password); 
        if (settings.user != null) 
        { 
         FormsAuthentication.SetAuthCookie(string.Format("{0},{1}", settings.company.Company_id.ToString(), settings.user.Medewerker_ID.ToString()) , login.RememberMe); 
         return RedirectToAction("index", "home");        
        } 
       } 
      } 
     } 
     else 
     { 
      return RedirectToAction("index", "home"); 
     } 
     return View(); 
    } 


} 

E naturalmente il programma di avvio automatico autofac

private static void SetAutoFacContainer() 
    { 
     var builder = new ContainerBuilder(); 
     builder.RegisterControllers(Assembly.GetExecutingAssembly()); 
     builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerHttpRequest();   
     builder.RegisterAssemblyTypes(typeof(UserRepository).Assembly) 
      .Where(t => t.Name.EndsWith("Repository")) 
      .AsImplementedInterfaces().InstancePerHttpRequest(); 
     builder.RegisterAssemblyTypes(typeof(ConnectionService).Assembly) 
      .Where(t => t.Name.EndsWith("Service")) 
      .AsImplementedInterfaces().InstancePerHttpRequest(); 

     builder.Register(c => new HttpContextWrapper(HttpContext.Current)).As<HttpContextBase>().InstancePerLifetimeScope(); 
     builder.RegisterModule(new AutofacWebTypesModule()); 

     builder.Register(att => new AuthorizeFilter(att.Resolve<IConnectionService>(), att.Resolve<IEmployeeLoginService>())).AsAuthorizationFilterFor<Controller>().InstancePerHttpRequest(); 
     builder.RegisterFilterProvider(); 

     IContainer container = builder.Build(); 
     DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 
    } 

La mia idea di come fare questo, è la creazione di una variabile di sessione con la stringa di connessione dopo che i dati sono retreaval da quello statico db in cui le informazioni sono memorizzate e iniettano la sessione nell'unità di lavoro e in qualche modo la usano lì, ma non posso girarmi intorno.

La domanda (s)

Sto andando nella giusta direzione cercando di raggiungere questo obiettivo, o addirittura è possibile? Se non che cosa fareste prendere per raggiungere questo

So che è un lungo lettura spero voi ragazzi mi può aiutare, im abbastanza nuovo per utilizzare queste tecniche tutti insieme. Grazie in anticipo lo apprezzo davvero!

risposta

4

tuo sulla strada giusta, ho usato

var mtc = new MultitenantContainer(container.Resolve<ITenantIdentificationStrategy>(), container); 
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc)); 

La strategia di identificazione sarebbe basata su l'utente connesso. Con valori predefiniti per quando non sono registrati.

public class CompanyNameIdentificationStrategy : ITenantIdentificationStrategy 
    { 

     public bool TryIdentifyTenant(out object tenantId) 
     { 
      var context = HttpContext.Current; 
      if(context != null) 
      { 
       var myUser = context.User as MyUserObject; 
       if(myUser != null) 
       { 
        tenantId = myUser.CompanyName; 
        return true; 
       } 
      } 
      return false; 
     } 
} 

quindi si aggiunge al vostro setup autofact:

var s = c.Resolve<ITenantIdentificationStrategy>(); 
        object id; 
        if (s.TryIdentifyTenant(out id) && id != null) 
        { 
         return id; 
        } 
        return "default"; 
       }).Keyed<string>("CompanyName"); 



builder.Register<Settings>(c => 
       { 
        var companyName = c.ResolveKeyed<string>("companyName"); 
        if (companyName == "default") 
        { 
         return new DefaultSettings(); 
        } 
        var settings = new Settings(); 
        return settings; 

       }).InstancePerLifetimeScope(); 

È possibile risolvere roba all'interno di questi blocchi di codice. Probabilmente configurerei le impostazioni predefinite con chiave, e poi quando l'utente è connesso le impostazioni passerebbero alla loro configurazione e il resto dell'applicazione dovrebbe funzionare.

+0

Ultimi documenti. [collegamento] (http://autofac.readthedocs.org/en/latest/advanced/multitenant.html?highlight=multitenantcontainer) – user3420988

+0

Grazie mille per aver trovato il tempo di rispondere alla mia domanda, nel frattempo ho ottenuto il lavoro in un modo diverso ma im sicuramente lo ricodificherete a modo tuo dato che è il modo più elegante. – edgarpetrauskas

Problemi correlati