2015-03-18 21 views
7

Ich möchte mehrere Implementierungen des IUserRepository haben jede Implementierung wird mit einem Datenbanktyp entweder MongoDB oder eine beliebige SQL-Datenbank arbeiten. Um dies zu tun, habe ich ITenant-Schnittstelle, die eine Verbindungszeichenfolge und andere Mandantenkonfiguration haben. Der Mandant wurde in IUserRepository entweder in MongoDB oder in eine andere SQLDB-Implementierung eingefügt. Was ich wissen muss, ist, wie das injizierte Repository richtig geändert wird, um die Datenbankbasis auf dem Mandanten auszuwählen.Ändern injizierten Objekt zur Laufzeit

Schnittstellen

public interface IUserRepository 
{ 
    string Login(string username, string password); 
    string Logoff(Guid id); 
} 

public class User 
{ 
    public Guid Id { get; set; } 
    public string Username { get; set; } 
    public string Password { get; set; } 
    public string LastName { get; set; } 
    public string FirstName { get; set; } 

} 

public interface ITenant 
{ 
    string CompanyName { get; } 
    string ConnectionString { get; } 
    string DataBaseName { get; } 
    string EncriptionKey { get; } 

} 

wichtig zu wissen ist, dass der Mieter ID Pass zu einer API via Header Anfrage gewesen ist

StartUp.cs

// set inject httpcontet to the tenant implemantion 
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>(); 

// inject tenant 
services.AddTransient<ITenant, Tenant>(); 

// inject mongo repository but I want this to be programmatically 
services.AddTransient<IUserRepository, UserMongoRepository>(); 

Beispiel Mongo Implementierung

public class UserMongoRepository : IUserRepository 
{ 

    protected ITenant Tenant 

    public UserMongoRepository(ITenant tenant) : 
     base(tenant) 
    { 
     this.Tenant = tenant; 
    } 

    public string Login(string username, string password) 
    { 

     var query = new QueryBuilder<User>().Where(x => x.Username == username); 
     var client = new MongoClient(this.Tenant.ConnectionString);var server = client.GetServer(); 
     var database = client.GetServer().GetDatabase(this.Tenant.DataBaseName); 
     var user = database.GetCollection<User>.FindAs<User>(query).AsQueryable().FirstOrDefault(); 

     if (user == null) 
      throw new Exception("invalid username or password"); 

     if (user.Password != password) 
      throw new Exception("invalid username or password"); 

     return "Sample Token"; 

    } 

    public string Logoff(Guid id) 
    { 

     throw new NotImplementedException(); 
    } 

} 

Tenant

public class Tenant : ITenant 
{ 

    protected IHttpContextAccessor Accesor; 
    protected IConfiguration Configuration; 

    public Tenant(IHttpContextAccessor accesor, IDBConfiguration config) 
    { 
     this.Accesor = accesor; 
     this.Configuration = new Configuration().AddEnvironmentVariables(); 
     if (!config.IsConfigure) 
      config.ConfigureDataBase(); 
    } 


    private string _CompanyName; 
    public string CompanyName 
    { 
     get 
     { 
      if (string.IsNullOrWhiteSpace(_CompanyName)) 
      { 
       _CompanyName = this.Accesor.Value.Request.Headers["Company"]; 
       if (string.IsNullOrWhiteSpace(_CompanyName)) 
        throw new Exception("Invalid Company"); 
      } 
      return _CompanyName; 
     } 
    } 

    private string _ConnectionString; 
    public string ConnectionString 
    { 
     get 
     { 
      if (string.IsNullOrWhiteSpace(_ConnectionString)) 
      { 
       _ConnectionString = this.Configuration.Get(this.CompanyName + "_" + "ConnectionString"); 
       if (string.IsNullOrWhiteSpace(_ConnectionString)) 
        throw new Exception("Invalid ConnectionString Setup"); 
      } 
      return _ConnectionString; 
     } 
    } 

    private string _EncriptionKey; 
    public string EncriptionKey 
    { 
     get 
     { 
      if (string.IsNullOrWhiteSpace(_EncriptionKey)) 
      { 
       _EncriptionKey = this.Configuration.Get(this.CompanyName + "_" + "EncriptionKey"); 
       if (string.IsNullOrWhiteSpace(_EncriptionKey)) 
        throw new Exception("Invalid Company Setup"); 
      } 
      return _EncriptionKey; 
     } 
    } 

    private string _DataBaseName; 
    public string DataBaseName 
    { 
     get 
     { 
      if (string.IsNullOrWhiteSpace(_DataBaseName)) 
      { 
       _DataBaseName = this.Configuration.Get(this.CompanyName + "_" + "DataBaseName"); 
       if (string.IsNullOrWhiteSpace(_DataBaseName)) 
        throw new Exception("Invalid Company Setup"); 
      } 
      return _DataBaseName; 
     } 
    } 
} 

-Controller

public class UsersController : Controller 
{ 
    protected IUserRepository DataService; 

    public UsersController(IUserRepository dataService) 
    { 
     this.DataService = dataService; 
    } 

    // the controller implematation 

} 

Antwort

4

Sie können versuchen, nicht die tatsächliche Repository eine Fabrik zu injizieren. Die Factory wird für das Erstellen des richtigen Repositorys basierend auf der aktuellen Benutzeridentität verantwortlich sein.

Es könnte ein wenig mehr Kesselplatte Code erfordern, aber es kann erreichen, was Sie wollen. Ein bisschen Vererbung könnte sogar den Controller-Code vereinfachen.

+1

Aber wenn Sie das tun, welches Objekt werden Sie in den Controller injizieren? –

+0

Eine 'IUserRepositoryFactory' mit einer Methode, die das Repository des aktuellen Mandanten auflösen kann –

7

Sie sollten eine Proxy-Implementierung für IUserRepository definieren und die tatsächlichen Implementierungen hinter diesem Proxy ausblenden und zur Laufzeit entscheiden, in welches Repository der Aufruf weitergeleitet werden soll. Zum Beispiel:

Mit dieser Proxy-Klasse können Sie problemlos ein Laufzeitprädikat erstellen, das entscheidet, welches Repository verwendet werden soll. Zum Beispiel:

services.AddTransient<IUserRepository>(c => 
    new UserRepositoryDispatcher(
     () => c.GetRequiredService<ITenant>().DataBaseName.Contains("Mongo"), 
     trueRepository: c.GetRequiredService<UserMongoRepository>() 
     falseRepository: c.GetRequiredService<UserSqlRepository>())); 
+0

Danke für die Antwort @steve Sinn machen, kann ich den Vorteil der Verwendung des Dispatcher sehen, Das einzige, was ich bemerke, ist, dass der Konstruktor wirklich groß wird Da ich mehrere DB-Typen unterstützen möchte, kann ich den Mandanten an den Dispatcher übergeben und die Auswahl im Dispatcher –

+0

hi @Steven treffen. Warum erstellen Sie nicht direkt eine Instanz der UserMongoRepository-Klasse wie "trueRepository: new UserMongoRepository()" . Liegt es daran, dass vorher etwas in diese Klasse gespritzt wurde? –

+1

@BarbarosAlp: Ich denke du hast deine eigene Frage beantwortet. Das 'UserMongoRepository' muss möglicherweise auch vom Container aufgebaut werden, da es eigene Abhängigkeiten hat. – Steven

Verwandte Themen