Миграции на Entity Framework: Конструкторът на конфигурацията се извиква винаги

Създадох просто конзолно приложение, инсталирах EntityFramework от nuget и изпълних команда Enable-Migrations.

Ето кода на main:

using(var ctx = new AppDbContext())
{
   var persons = ctx.Persons.ToList();
   Console.ReadKey();
}

Ето кода на класа Configuration

internal sealed class Configuration : DbMigrationsConfiguration<AppDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
        Console.WriteLine("Configuration: Constructor");
    }

    protected override void Seed(AppDbContext context)
    {
        Console.WriteLine("Configuration: Seed");
    }
}

Ето частта от рамката на обекта в app.config:

<entityFramework>
  <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
    <parameters>
      <parameter value="mssqllocaldb" />
    </parameters>
  </defaultConnectionFactory>
  <providers>
    <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
  </providers>
</entityFramework>

Ако стартирам конзолното приложение, конструкторът на класа конфигурация на миграциите се изпълнява. Защо? Не променям инициализатора на базата данни.

Актуализация

Сега опитах някои инициализатори на бази данни и гледам дали се извиква конструкторът на класа за конфигурация. Ето моят резултат:

public class AppDbContext : DbContext
{
    static AppDbContext()
    {
        //Database.SetInitializer<AppDbContext>(new CreateDatabaseIfNotExists<AppDbContext>());
        // ==> Configuration class constructor called

        //Database.SetInitializer<AppDbContext>(new DropCreateDatabaseIfModelChanges<AppDbContext>());
        // ==> Configuration class constructor called

        //Database.SetInitializer<AppDbContext>(new DropCreateDatabaseAlways<AppDbContext>());
        // ==> Configuration class constructor called

        Database.SetInitializer<AppDbContext>(null);
        // ==> Configuration class constructor is NOT called
    }

    public IDbSet<Person> Persons { get; set; }
}

Зададох изрично инициализатора на базата данни, но се извиква конструкторът на класа за конфигурация. Странно поведение.


person malt    schedule 03.06.2015    source източник
comment
Имате ли включени автоматични миграции? Получавате ли грешка? Може да се наложи да видите контекстния си клас.   -  person Steve Greene    schedule 03.06.2015
comment
Значи казвате, че не използвате Database.SetInitializers никъде?   -  person Ruskin    schedule 04.06.2015
comment
Да, не задавам инициализатора на базата данни, нито в app.config, нито в кода.   -  person malt    schedule 04.06.2015


Отговори (1)


Както споменахте, че сте изпълнили командата Enable-Migrations. И така, какво прави, добавяне на нов клас конфигурация за миграция. Този клас ще приложи промените в конфигурацията, посочени в класа, свързан с миграции, когато стартирате вашето приложение.

DatabaseInitializers са стратегии за прилагане на миграции, дори и да не сте ги докоснали, обектът на класа Configuration ще бъде създаден.

Актуализация:

Ако не го правите във вашия код, тогава погледнете конфигурационния файл на вашето приложение. Това може да има нещо, което изглежда като:

    <contexts>
      <context type="AppDbContext, MyAssembly">
        <databaseInitializer 
type="System.Data.Entity.MigrateDatabaseToLatestVersion`2[[AppDbContext, MyAssembly], 
                             [Migrations.Configuration, MyAssembly]], EntityFramework" />  


</context>

За повече подробности вижте тази документация.

Актуализация 2:

Мисля, че намерих отговора. Когато се погледне вътре в кода на EntityFramework, ясно се показва, че базата данни Initializer се задейства, когато първата ви заявка за linq се реализира. Вижте стека за повиквания по-долу, заснет, докато отстранявате грешки в приложението си.

Екранна снимка на Callstack от с в конфигурационния конструктор

Случай 1: Когато използвате Database.SetInitializer<AppDbContext>(null);, който всъщност създава NullDatabaseInitializer<TContext>() като инициализатор по подразбиране, който не прави нищо, така че конфигурацията не е извикана.

Случай 2: Когато премахнете реда Database.SetInitializer<AppDbContext>(null);, нещата започнаха да използват стойности по подразбиране на доставчиците. В този случай не е предоставена външна спецификация на DatabaseInitializer, така че EF ще получи инициализатора по подразбиране, който в този случай е CreateIfNotExist<AppDbContext>. След това инициализаторът ще се опита да намери конфигурацията и, разбира се, има клас конфигурация, така че класът конфигурация ще бъде инстанциран и ще бъде извикан конструктор.

Въпреки че ми отне няколко часа, за да го разбера.

person vendettamit    schedule 03.06.2015
comment
Не промених инициализатора на базата данни, така че MigrateDatabaseToLatestVersion‹..› не се използва. Но конструкторът на класа Configuration се изпълнява, но не разбирам защо се извиква този конструктор. Защо това е необходимо? Ако добавя втори клас Configuration2, тогава конструкторите на Configuration и Configuration2 не се изпълняват. Каква е тази логика? - person malt; 04.06.2015
comment
Вижте актуализирания ми отговор. Добавих подробности относно това какво друго може да е отговорно за конфигурацията за мигриране на екземпляр. - person vendettamit; 04.06.2015
comment
Thx, но аз не променям конфигурационния файл. Не настройвам инициализатора чрез конфигурационен файл. Сега опитах много различни инициализатори. Вижте оригиналната публикация за резултатите... Много интересно. - person malt; 04.06.2015
comment
Можете ли да споделите примерен код, където може да бъде възпроизведен? Наистина бих искал да видя какво става там. - person vendettamit; 04.06.2015
comment
Това е наистина странно поведение. Тук можете да изтеглите кода dl.dropboxusercontent.com/u/2856423/EfMigrationTest2.zip - person malt; 04.06.2015
comment
В моя край работи добре, без да отивам в конструктора на конфигурацията. - person vendettamit; 04.06.2015
comment
виждате в кода, че задавам инициализатора на базата данни на нула. В този случай конструкторът на класа Configuration не е извикан. Но ако коментирате този ред Database.SetInitializer‹AppDbContext›(null); тогава се извиква конструкторът на класа Configuration, въпреки че се използва инициализаторът на db по подразбиране, а не инициализаторът MigrateDatabaseToLatestVersion. - person malt; 04.06.2015
comment
@malt Разбрах го. Вижте моя актуализиран отговор. Освен това научих няколко нови неща. ;) +1 за въпроса. - person vendettamit; 04.06.2015
comment
Благодаря за помощта и усилията. Междувременно отворих и проблем в github. Там също получавам някои интересни отговори през последните часове. Ето връзката github.com/aspnet/EntityFramework/issues/ - person malt; 04.06.2015
comment
Да, вече го видях, когато приключих с публикуването на отговора. Харесва ми идеята да имам множество конфигурационни класове, които поискахте. - person vendettamit; 05.06.2015