Ich baue ASP.NET Core Web Api mit Simple Injector und habe folgendes Problem: Die Daten für jeden Benutzer werden in seiner individuellen Basis gespeichert (aber mit dem gleichen Schema), die nur bekannt ist, nachdem er sich selbst autorisiert hat. Also ... Ich kann DbContext
geben, es ist die richtige Verbindungszeichenfolge nur nach der Autorisierung des Benutzers. Was ist der beste Weg, dies zu erreichen?
HttpContext
speichere ich die Verbindungszeichenfolge im benutzerdefinierten HttpContext
Claim und verweise darauf mit einer statischen DbContext
in der DbContext
- OnConfiguring - Methode.
Der Helfer:
public class WebHelper
{
private static IHttpContextAccessor _httpContextAccessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
private static HttpContext HttpContext
{
get
{
return _httpContextAccessor.HttpContext;
}
}
public static string ConnectionString
{
get
{
return _httpContextAccessor?.HttpContext?.User.FindFirst(CustomClaimTypes.ConnectionString)?.Value;
}
}
}
Was ich so registriert habe:
private SimpleInjector.Container _container = new SimpleInjector.Container();
public void ConfigureServices(IServiceCollection services)
{
_container.Register(() =>
{
return new BaseDbContext(SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(),
"Data Source=127.0.0.1").Options);
}, Lifestyle.Scoped);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
WebHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
}
Und schließlich rufe ich die Verbindungszeichenfolge so auf:
public class BaseDbContext : DbContext
{
public BasetDbContext(DbContextOptions options)
: base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (WebHelper.ConnectionString != null)
{
optionsBuilder.UseSqlServer(WebHelper.ConnectionString);
}
}
}
Und ich bin nicht gerade zufrieden mit diesem Ansatz, also möchte ich fragen, ob jemand eine bessere Idee hat ...
Mein allgemeiner Rat ist, die Laufzeitdaten getrennt von der Objektdiagrammzusammensetzung zu halten . Das bedeutet, dass alles, was Objektgraphen sein soll, nur aus Komponenten bestehen sollte und alles, was aus (veränderbaren) Laufzeitdaten besteht, sollte nicht mit Konstruktorinjektion in den Objektgraphen injiziert werden, sondern durch die Methodenaufrufe der Komponenten des Existierenden Objektgraph.
Nach meiner Erfahrung führt diese Trennung zu einem vereinfachten Modell, das viel einfacher zu handhaben ist, und zu einem Objektgraphen, der einfacher auf Korrektheit zu überprüfen ist.
In Ihrer Frage gibt es zwei offensichtliche Teile von Laufzeitdaten: den DbContext
und die Verbindungszeichenfolge. In einer typischen Anwendung gibt es nur einen Verbindungszeichenfolgenwert, und es ist eine Konstante. Konstanten können sicher in Objektgraphen eingefügt werden. Dies ist anders, wenn sich eine Verbindungszeichenfolge aufgrund von Laufzeitbedingungen ändert, da dies dazu führt, dass die Verbindungszeichenfolge selbst zu Laufzeitdaten wird.
Stellen Sie die Laufzeitdaten nicht direkt ein, sondern rufen Sie die Laufzeitdaten über Methodenaufrufe auf. Eine naheliegende Lösung besteht darin, der Anwendung eine Abstraktion bereitzustellen, die beim Aufruf einer Methode die Laufzeitdaten zurückgibt. Zum Beispiel:
public interface IDbContextProvider
{
MyDbContext Context { get; }
}
Auf diese Weise können Sie den richtigen MyDbContext
für die aktuelle Anforderung MyDbContext
. Eine Implementierung könnte folgendermaßen aussehen:
public class DelegateDbContextProvider : IDbContextProvider
{
private readonly Func<MyDbContext> provider;
public DelegateDbContextProvider(Func<MyDbContext> provider)
=> this.provider = provider;
public MyDbContext Context => this.provider();
}
So können Sie es wie folgt registrieren:
var contextProducer = Lifestyle.Scoped.CreateProducer<MyDbContext>(
() => new MyDbContext(WebHelper.ConnectionString), container);
container.RegisterInstance<IDbContextProvider>(
new DelegateDbContextProvider(contextProducer.GetInstance));
Mit diesem Code, anstatt eine der Injektion MyDbContext
in Verbraucher, injizieren Sie einen IDbContextProvider
. Da IDbContextProvider
als Singleton
registriert ist, können seine Kunden möglicherweise auch zu Singletons
werden. Wenn sie IDbContextProvider.Context
, wird jedoch der richtige MyDbContext
für die aktuelle Anforderung / den aktuellen Bereich zurückgegeben.