2016-04-23 6 views
3

Ich möchte SQL-Verbindungszeichenfolge im Controller, nicht in ApplicationDbContext ändern. Ich benutze Asp.Net Core und Entity Framework Core.Dynamisch ändern Verbindungszeichenfolge in Asp.Net Core

Zum Beispiel:

public class MyController : Controller { 
    private readonly ApplicationDbContext _dbContext 
    public MyController(ApplicationDbContext dbContext) 
    { 
     _dbContext = dbContext; 
    } 
    private void ChangeConnectionString() 
    { 
    // So, what should be here? 
    } } 

Wie kann ich das tun?

+0

dauerhaft oder vorübergehend für die Dauer der Anforderung? – Tseng

+0

@Tseng vorübergehend, während der Lebensdauer des Controllers. –

Antwort

1

Wir haben einen ähnlichen Fall wie Sie. Was wir getan haben, ist zu verwenden, um die implementationfactory Überlastung des IServiceCollection in den ConfigureServices Methode der Startup Klasse, etwa so:

//First register a custom made db context provider 
services.AddTransient<ApplicationDbContextFactory>(); 
//Then use implementation factory to get the one you need 
services.AddTransient(provider => provider.GetService<ApplicationDbContextFactory>().CreateApplicationDbContext()); 

Es ist sehr schwierig für mich, gerade jetzt zu implementieren CreateApplicationDbContext für Sie, weil es völlig davon abhängt, was Sie genau wollen. Aber sobald man diesen Teil herausgefunden haben, wie Sie es genau tun wollen, die Grundlagen der Methode sollte wie diese ohnehin aussehen:

public ApplicationDbContext CreateApplicationDbContext(){ 
    //TODO Something clever to create correct ApplicationDbContext with ConnectionString you need. 
} 

Sobald dies durchgeführt wird Sie die richtige ApplicationDbContext in Ihrem Controller wie Sie injizieren hat im Konstruktor:

public MyController(ApplicationDbContext dbContext) 
{ 
    _dbContext = dbContext; 
} 

oder eine Aktion Methode in der Steuerung:

public IActionResult([FromServices] ApplicationDbContext dbContext){ 
} 

auch immer Sie die Details implementieren, ist der Trick, dass die implementatio n factory erstellt Ihren ApplicationDbContext jedes Mal, wenn Sie ihn injizieren.

Sagen Sie mir, wenn Sie weitere Hilfe bei der Implementierung dieser Lösung benötigen.

Update # 1 Yuriy N. fragte, was ist der Unterschied zwischen AddTransient und AddDbContext, die eine berechtigte Frage ist ... Und es ist nicht. Lassen Sie mich erklären.

Dies ist nicht relevant für die ursprüngliche Frage.

ABER ... Nachdem das gesagt wurde, kann die Implementierung Ihrer eigenen "Implementierungsfabrik" (was das Wichtigste ist, was ich über meine Antwort notieren muss) in diesem Fall mit Entitätsframework etwas komplizierter sein als das, was wir brauchten.

Bei solchen Fragen können wir uns heute glücklicherweise den Sourcecode in GitHub ansehen, also habe ich nachgeschaut was AddDbContext genau macht. Und nun ... Das ist nicht wirklich schwierig. Diese 'add' (und 'use') Extension-Methoden sind nichts anderes als Convenience-Methoden, denken Sie daran. Also müssen Sie alle Dienste, die AddDbContext tut, plus die Optionen hinzufügen. Vielleicht können Sie sogar die AddDbContext-Erweiterungsmethode wiederverwenden, fügen Sie einfach Ihre eigene Überladung mit einer Implementierungsfactory hinzu.

Also, um auf Ihre Frage zurückzukommen. AddDbContext macht einige EF-spezifische Sachen. Wie Sie sehen können, werden sie Ihnen ermöglichen, eine Lebenszeit in einer späteren Version zu verbringen (transient, Singleton). AddTransient ist Asp.Net Core, mit dem Sie jeden Service hinzufügen können, den Sie brauchen. Und Sie brauchen eine Implementierungsfabrik.

Ist das klarer?

+0

Warum wählen Sie 'DbContext' mit' AddTransient'? Was ist der Unterschied zwischen dieser Methode des Hinzufügens und 'services.AddEntityFramework(). AddSqlServer(). AddDbContext (Optionen => Optionen.UseSqlServer (Konfiguration [" Data: DefaultConnection: ConnectionString "])))? Und mit Ihrer Methode haben wir Migrationen von der Konsole verloren, nicht wahr? –

+0

Ich habe meine Antwort als Antwort auf Sie aktualisiert. Ich hoffe es hilft. :) –

+0

Es ist wirklich hilfreich, danke! Aber noch unklar, was sollen wir mit Migrationen machen? 'ApplicationDbContext' nicht' DbContext', so dass Migrationen von der Konsole nicht funktionieren. –

6

Ich konnte die Verbindungszeichenfolge für jede Anforderung ändern, indem Sie die Verbindungszeichenfolgenlogik in die OnConfiguring-Methode des DbContext verschieben.

In Startup.cs#ConfigureServices Methode: services.AddDbContext<MyDbContext>();

In MyDbContext.cs, fügte ich die Dienste, die ich an den Konstruktor injiziert benötigt wird.

private IConfigurationRoot _config; 
    private HttpContext _httpContext; 

    public MyDbContext(DbContextOptions options, IConfigurationRoot config, IHttpContextAccessor httpContextAccessor) 
      : base(options) 
    { 
     _config = config; 
     _httpContext = httpContextAccessor.HttpContext; 
    } 

Dann OnConfiguring außer Kraft setzen:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
    { 
     var connString = BuildConnectionString(); // Your connection string logic here 

     optionsBuilder.UseSqlServer(connString); 
    } 
+0

Wie haben Sie eigentlich die Injektion von Config und httpContextAccessor gemacht? Vielleicht hast du etwas anderes als AddDbContext benutzt? – starmandeluxe

+0

Sie müssen ihre Injecting in 'ConfigureServices()' hinzufügen, etwa so: 'services.AddTransient ()'. Wichtig, sie vor dem 'addDbContext()' zu injizieren. –

+0

@ YuriyN. Wenn ich services.AddDbContext () hinzufüge, ruft es nur den Standardkonstruktor der MyDbContext-Klasse auf, die Public MyDbContext() {} ist. Bevor ich das anrufe, habe ich bereits httpcontext eingegeben. – user1447718

0

die für mich arbeiten:

public void ConfigureServices(IServiceCollection services) 
{ 
    // ..... 
    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 
    services.AddTransient<School360DbContext>(provider => 
    { 
     return ResolveDbContext(provider, hostingEnv); 
    }); 
    // .. 
} 

private MyDbContext ResolveDbContext(IServiceProvider provider, IHostingEnvironment hostingEnv) 
{ 
    string connectionString = Configuration.GetConnectionString("DefaultConnection"); 

    string SOME_DB_IDENTIFYER = httpContextAccessor.HttpContext.User.Claims 
     .Where(c => c.Type == "[SOME_DB_IDENTIFYER]").Select(c => c.Value).FirstOrDefault(); 
    if (!string.IsNullOrWhiteSpace(SOME_DB_IDENTIFYER)) 
    { 
     connectionString = connectionString.Replace("[DB_NAME]", $"{SOME_DB_IDENTIFYER}Db"); 
    } 

    var dbContext = new DefaultDbContextFactory().CreateDbContext(connectionString); 

    // .... 
    return dbContext; 
}