2013-04-25 11 views
5

Mam nadzieję, że ktoś jest w stanie mi pomóc, ponieważ wydaje mi się, że utknąłem całkowicie.Entity Framework Code Najpierw bez app.config

W przypadku nadchodzących projektów w naszej firmie chcielibyśmy użyć Entity Framework 5 z pierwszym podejściem do kodu. Grałem przez jakiś czas i za każdym razem, gdy próbuję używać EF z naszymi istniejącymi bibliotekami, zawodzę, ponieważ wydaje się, że EF silnie polega na istniejącym app.config.

W naszej firmie mamy wbudowaną bibliotekę baz danych, która pozwala nam łączyć się z różnymi źródłami danych i technologiami baz danych, korzystając z zalet MEF (zarządzanej rozszerzalnej struktury) dla dostawców baz danych. Muszę tylko przekazać pewne ustawienia bazy danych, takie jak host (lub plik), katalog, poświadczenia użytkownika i nazwa dostawcy bazy danych, biblioteka szuka odpowiedniej wtyczki i zwraca mi niestandardowy ciąg połączenia lub IDbConnection. Chcielibyśmy użyć tej biblioteki razem z EF, ponieważ pozwala nam być elastycznym w kwestii tego, która baza danych, której używamy, również zmienia bazę danych w czasie wykonywania.

So. Widziałem, że typowy obiekt DbContext nie przyjmuje parametrów w konstruktorze. Automatycznie wyszukuje odpowiedni ciąg połączenia w pliku app.config. Nie lubimy takich rzeczy, więc zmieniłem domyślny konstruktor, aby pobrać obiekt DbConnection, który zostanie przekazany do klasy bazowej DbContext. Brak zgody.

Problemy pojawiają się, gdy zmienia się pierwszy model kodu. EF automatycznie zauważa to i szuka klas migracji/konfiguracji. Ale: Typowa klasa migracji wymaga domyślnego konstruktora bez parametrów dla kontekstu! Jaka szkoda!

Dlatego tworzymy własną klasę migracji za pomocą interfejsu IDbContextFactory. Ale znowu wydaje się, że również ten IDbContextFactory potrzebuje konstruktora bez parametrów, w przeciwnym razie nie będę mógł dodać migracji ani zaktualizować bazy danych.

Ponadto stworzyłem własny konfigurator migracji danych, w którym przekazuję kontekst, a także docelową bazę danych. Problem jest tutaj: Nie znajduje żadnych klas migracji, bez względu na to, co próbuję.

Całkowicie utknąłem, ponieważ wydaje się, że jedynym sposobem użycia EF jest zapisywanie ciągów połączenia w pliku app.config. I to jest głupie, ponieważ musimy zmienić połączenia z bazami danych w czasie wykonywania, a app.config jest tylko do odczytu dla domyślnych użytkowników!

Jak rozwiązać ten problem?

+0

Jeśli nie przekazujesz połączenia, to musisz go odczytać z pliku app.config? W przeciwnym razie, jak by to kiedykolwiek znalazł? –

Odpowiedz

3

Odpowiedzią na ten temat znajdują się tutaj

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

Sztuką jest nieco zmodyfikować domyślne MigrateDatabaseToLatestVersion inicjatora tak, że :

  • baza danych jest zawsze inicjowana ...
  • ... używając ciąg połączenia z obecnym kontekście

DbMigrator nadal będzie tworzyć nowy kontekst danych, ale skopiuje ciąg połączenia z kontekstu Ciebie według inicjatora. Byłem nawet w stanie skrócić kod.

I tu idzie: Kod

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 
} 

Klient:

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")) 
     { 
     } 
    } 

Running spowoduje dwóch baz danych mają być utworzone i migracji w zależności od konfiguracji migracji.

+0

Piękne rozwiązanie, które doskonale rozwiązało mój problem. To okropne, że muszę to robić za każdym razem w każdym kontekście - nie wydaje mi się, że Microsoft myśli o dynamicznych połączeniach z bazą danych i tym podobnych. – Atrotygma

+0

Pamiętam, że spędziłem 3 lub 4 godziny, próbując to rozgryźć. Fajną rzeczą jest jednak to, że kiedy masz już rzeczywiste rozwiązanie, wydaje się to całkiem proste. –

+0

Wiem, że ta odpowiedź była już od jakiegoś czasu, ale po wdrożeniu dostaję wyjątek System.StackOverflowException. Wydaje się, że przy konstruowaniu nowego DbMigratora w metodzie InitializeDatabase wywołuje domyślny konstruktor MasterDetailContext (jak w komentarzach) i wywołanie 'Database.Initialize (true);' w konstruktorze domyślnym jakoś inwokuje InitializeDatabase ponownie, więc utknie w nieskończonej pętli. Czy czegoś brakuje? – Kerby

0

Potrzebny jest konstruktor bez parametrów w celu wywołania go. Co można zrobić, to podać domyślne DbConntectionFactory w pustym konstruktora, coś takiego:

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

public DbContext(IDbContextFactory factory) 
{ 
} 
Powiązane problemy