2013-04-25 8 views
5

Ich hoffe, dass jemand in der Lage ist, mir zu helfen, weil es scheint, ich bin total festgefahren.Entity Framework Code Zuerst ohne app.config

Für kommende Projekte in unserem Unternehmen möchten wir Entity Framework 5 mit einem Code First Approach verwenden. Ich habe eine Weile herumgespielt und jedes Mal, wenn ich versuche, EF mit unseren vorhandenen Bibliotheken zu verwenden, scheitere ich, weil es scheint, dass EF stark auf einer bestehenden app.config beruht.

In unserem Unternehmen verfügen wir über eine interne Datenbankbibliothek, die es uns ermöglicht, Verbindungen zu verschiedenen Datenquellen und Datenbanktechnologien herzustellen und die Vorteile von MEF (Managed Extensibility Framework) für Datenbankanbieter zu nutzen. Ich muss nur einige Datenbankeinstellungen wie Host (oder Datei), Katalog, Benutzeranmeldeinformationen und einen Datenbankprovidernamen übergeben, die Bibliothek sucht nach dem passenden Plugin und gibt mir eine benutzerdefinierte Verbindungszeichenfolge oder IDbConnection zurück. Wir möchten diese Bibliothek zusammen mit EF verwenden, da wir flexibel darüber sein können, welche Datenbank wir verwenden, um die Datenbank zur Laufzeit zu ändern.

So. Ich habe gesehen, dass ein typisches DbContext-Objekt keine Parameter im Konstruktor akzeptiert. Es sucht automatisch nach der entsprechenden Verbindungszeichenfolge in app.config. Wir mögen solche Dinge nicht, also habe ich den Standardkonstruktor geändert, um ein DbConnection-Objekt zu übernehmen, das an die DbContext-Basisklasse übergeben wird. Kein Problem.

Probleme treten auf, wenn sich das erste Codemodell ändert. EF bemerkt das automatisch und sucht nach Migrationsklassen/Konfiguration. Aber: Eine typische Migrationsklasse benötigt einen parameterlosen Standardkonstruktor für den Kontext! Was für eine Schande!

So erstellen wir unsere eigene Migrationsklasse mit der IDbContextFactory-Schnittstelle. Aber wieder scheint es, dass auch diese IDbContextFactory einen parameterlosen Konstruktor benötigt, sonst kann ich keine Migrationen hinzufügen oder die Datenbank aktualisieren.

Weiterhin habe ich meinen eigenen Datenmigrationskonfigurator erstellt, wo ich den Kontext, auch die Zieldatenbank, übergebe. Problem ist hier: Es findet keine Migrationsklassen, egal was ich versuche.

Ich bin völlig fest, weil es die einzige Möglichkeit scheint, EF zu verwenden, wenn Verbindungszeichenfolgen in app.config gespeichert werden. Und das ist dumm, weil wir Datenbankverbindungen zur Laufzeit ändern müssen, und app.config ist für Standardbenutzer schreibgeschützt!

Wie löst man das?

+0

Wenn Sie die Verbindungszeichenfolge nicht übergeben, dann muss sie aus der app.config gelesen werden? Ansonsten, wie würde es jemals finden? –

Antwort

3

Die Antwort finden Sie hier

https://stackoverflow.com/a/15919627/941240

Der Trick ist, leicht die Standard MigrateDatabaseToLatestVersion initializer so zu ändern, dass :

  • Die Datenbank wird immer initialisiert ...
  • ... mit der Verbindungszeichenfolge aus aktuellem Kontext

Die DbMigrator noch einen neuen Datenkontext schaffen, sondern wird die Verbindungszeichenfolge von Ihnen Kontext nach dem initializer kopieren. Ich konnte den Code sogar kürzen.

Und hier geht es:

public class MasterDetailContext : DbContext 
{ 
    public DbSet<Detail> Detail { get; set; } 
    public DbSet<Master> Master { get; set; } 

    // this one is used by DbMigrator - I am NOT going to use it in my code 
    public MasterDetailContext() 
    { 
     Database.Initialize(true); 
    } 

    // rather - I am going to use this, I want dynamic connection strings 
    public MasterDetailContext(string ConnectionString) : base(ConnectionString) 
    { 
     Database.SetInitializer(new CustomInitializer()); 
     Database.Initialize(true); 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 
    } 
} 

public class CustomInitializer : IDatabaseInitializer<MasterDetailContext> 
{ 

    #region IDatabaseInitializer<MasterDetailContext> Members 

    // fix the problem with MigrateDatabaseToLatestVersion 
    // by copying the connection string FROM the context 
    public void InitializeDatabase(MasterDetailContext context) 
    {    
     Configuration cfg = new Configuration(); // migration configuration class 
     cfg.TargetDatabase = new DbConnectionInfo(context.Database.Connection.ConnectionString, "System.Data.SqlClient"); 

     DbMigrator dbMigrator = new DbMigrator(cfg); 
     // this will call the parameterless constructor of the datacontext 
     // but the connection string from above will be then set on in 
     dbMigrator.Update();    
    } 

    #endregion 
} 

Client-Code:

static void Main(string[] args) 
    { 

     using (MasterDetailContext ctx = new MasterDetailContext(@"Database=ConsoleApplication801;Server=.\SQL2012;Integrated Security=true")) 
     { 
     } 

     using (MasterDetailContext ctx = new MasterDetailContext(@"Database=ConsoleApplication802;Server=.\SQL2012;Integrated Security=true")) 
     { 
     } 
    } 

Laufen dies führt dazu, dass die beiden Datenbanken nach der Migration Konfiguration erstellt und migriert werden.

+0

Schöne Lösung, die mein Problem perfekt gelöst hat. Es ist schrecklich, dass ich dies jedes Mal für jeden Kontext tun muss - es scheint nicht, dass Microsoft über dynamische Datenbankverbindungen nachgedacht hat und so. – Atrotygma

+0

Ich erinnere mich daran, 3 oder 4 Stunden damit verbracht zu haben, das herauszufinden. Schön ist jedoch, dass wenn Sie endlich die eigentliche Lösung haben, es ganz einfach scheint. –

+0

Ich weiß, dass diese Antwort schon eine Weile existiert, aber ich erhalte eine System.StackOverflowException, wenn ich sie implementiere. Es sieht so aus, als ob beim Konstruieren eines neuen DbMigrator in der InitializeDatabase-Methode der Standardkonstruktor des MasterDetailContext (laut den Kommentaren) und der Aufruf von 'Database.Initialize (true);' im Initialkonstruktor irgendwie InitialDatabase wieder aufruft, so dass es nicht weiter geht in einer Endlosschleife. Fehle ich etwas? – Kerby

0

Es benötigt einen parameterlosen Konstruktor, um es aufzurufen. Was Sie tun können, ist Ihr Standard-DbConntectionFactory im leeren Konstruktor bieten, so etwas wie:

public DbContext() 
{ 
    IDbContextFactory defaultFactory; //initialize your default here 
    DbContext(defaultFactory); 
} 

public DbContext(IDbContextFactory factory) 
{ 
} 
Verwandte Themen