Как я могу получить свою базу данных для заполнения с помощью Entity Framework CodeFirst?

База данных успешно создана (как и таблицы), но не заполнена. Я потратил несколько часов и прочитал тонны статей, но так и не смог их получить. Какие-либо предложения?

Кстати, можно ли вызвать инициализатор без ссылки на мой DatabaseContext в клиенте?

Я включил весь соответствующий код, который мог придумать. Если что-то еще будет полезно, пожалуйста, дайте мне знать.

Что я пробовал:

  1. Я удалил свою строку подключения (поскольку по умолчанию она равна sqlexpress, просто изменилось имя)
  2. Я изменил DropCreateDatabaseIfModelChanges на DropCreateDatabaseAlways, все так же.

Редактировать: действительно странно, что это сработало один раз, но я понятия не имею, как и почему это снова сломалось. Я предполагаю строки подключения, но кто знает.

Инициализатор базы данных.cs

public class DatabaseInitializer : DropCreateDatabaseIfModelChanges<DatabaseContext>
{
  protected override void Seed(DatabaseContext context)
  {
    // Seeding data here
    context.SaveChanges();
  }
}

DatabaseContext.cs

public class DatabaseContext : DbContext
{
  protected override void OnModelCreating(DbModelBuilder mb)
  {
    // Random mapping code
  }

  public DbSet<Entity1> Entities1 { get; set; }
  public DbSet<Entity2> Entities2 { get; set; }

}

Global.asax.cs — Application_Start()

protected void Application_Start()
{
  Database.SetInitializer<DatabaseContext>(new DatabaseInitializer());
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
}

Клиент web.config

<connectionStrings>
  <add name="DatabaseContext" connectionString="data source=.\SQLEXPRESS;Database=Database;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
</connectionStrings>

РЕШЕНИЕ

Ради документации я делюсь своим решением здесь. В любом случае, просматривать все комментарии было бы больно. В конце концов у меня были DatabaseInitializer и DatabaseContext в отдельных классах. Я не очень понимаю, пока эти крошечные изменения исправили это, но вот оно.

Инициализатор базы данных.cs

public class DatabaseInitializer : CreateDatabaseIfNotExists<DatabaseContext>
{
  protected override void Seed(DatabaseContext context)
  {
    // Seed code here
  }
}

DatabaseContext.cs

public class DatabaseContext : DbContext
{
  public DatabaseContext() : base("MyDatabase") { }

  protected override void OnModelCreating(DbModelBuilder mb)
  {
    // Code here
  }

  public DbSet<Entity> Entities { get; set; }
  // Other DbSets
}

Global.asax.cs — Application_Start()

protected void Application_Start()
{
  Database.SetInitializer(new DatabaseInitializer());
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
}

person Alec    schedule 10.06.2011    source источник
comment
Добавляете ли вы исходные элементы в свой контекст данных? Я знаю, что пропустил это раньше.   -  person Jim D'Angelo    schedule 11.06.2011
comment
Да я. Спасибо за проверку, хотя :).   -  person Alec    schedule 11.06.2011
comment
=D Не беспокойтесь. Можете ли вы поставить точку останова в коде и убедиться, что ваш Seed вызывается?   -  person Jim D'Angelo    schedule 11.06.2011
comment
Метод Seed не вызывается. Инициализатор базы данных есть. Я что-то упускаю?   -  person Alec    schedule 13.06.2011
comment
Это действительно странно. Попробуйте поместить свой Database.SetInitializer в конструктор DatabaseContext. Также попробуйте добавить base.Seed(context); для вашего семенного метода. Я не знаю, почему это имеет значение, но это стоит проверить. Изменить: я видел ваш комментарий к ответу @feanz, но вы можете поместить Database.SetInitializer в конструктор вашего контекста данных. Я сразу поместил свои классы инициализатора внутри моего класса datacontext.   -  person Jim D'Angelo    schedule 13.06.2011
comment
Хорошо, теперь я могу запустить базу данных, но у меня есть еще несколько вопросов. База данных теперь заполняется всякий раз, когда я вызываю конструктор DatabaseContext, но разве она не должна заполняться, когда я использую Database.SetInitializer в методе App_Start? В настоящее время он не будет создавать базу данных и/или начальное значение, если я действительно явно не вызову конструктор DatabaseContext. Изменить: я обязательно отмечу как ответ, не беспокойтесь :)   -  person Alec    schedule 13.06.2011
comment
Когда вы используете контекст в первый раз, он заполнит базу данных, используя мою настройку, что-то вроде отложенной загрузки. Вы, безусловно, можете настроить его так, как считаете нужным. Причина, по которой я использую это, заключается в том, что мне не нужно постоянно выключать Cassini или IIS Express (в зависимости от того, что я использую в то время), чтобы удалить базу данных, если я изменю свою модель. Как только ситуация стабилизируется, я рефакторинг смещаю начальное значение в Application_Start. Это, безусловно, просто предпочтение.   -  person Jim D'Angelo    schedule 13.06.2011
comment
Запускает ли он Seed, когда вы используете конструктор DatabaseContext или когда вы фактически делаете вызов с использованием DatabaseContext? Независимо от того, делаю ли я вызов Database.SetInitializer в App_Start или в конструкторе DatabaseContext, Seed на самом деле не срабатывает, пока я не сделаю вызов с помощью DatabaseContext.   -  person Alec    schedule 13.06.2011
comment
Семя будет запущено при первой попытке что-либо получить или поместить в базу данных. Я оборачиваю свои DbContexts в единицы работы и репозитории, поэтому этого не происходит, пока я не получу доступ к данным из репозитория.   -  person Jim D'Angelo    schedule 13.06.2011


Ответы (12)


Вот как выглядят все мои классы DbContext, и они прекрасно засеиваются:

public class MyDbContext : DbContext
{
    public DbSet<MyClass> MyClasses { get; set; }

    protected override void OnModelCreating (DbModelBuilder modelBuilder)
    {
        base.OnModelCreating (modelBuilder);
        modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention> ();

        // Add any configuration or mapping stuff here
    }

    public void Seed (MyDbContext Context)
    {
        #if DEBUG
        // Create my debug (testing) objects here
        var TestMyClass = new MyClass () { ... };
        Context.MyClasses.Add (TestMyClass);
        #endif

        // Normal seeding goes here

        Context.SaveChanges ();
    }

    public class DropCreateIfChangeInitializer : DropCreateDatabaseIfModelChanges<MyDbContext>
    {
        protected override void Seed (MyDbContext context)
        {
            context.Seed (context);

            base.Seed (context);
        }
    }

    public class CreateInitializer : CreateDatabaseIfNotExists<MyDbContext>
    {
        protected override void Seed (MyDbContext context)
        {
            context.Seed (context);

            base.Seed (context);
        }
    }

    static MyDbContext ()
    {
        #if DEBUG
        Database.SetInitializer<MyDbContext> (new DropCreateIfChangeInitializer ());
        #else
        Database.SetInitializer<MyDbContext> (new CreateInitializer ());
        #endif
    }
}

Я использовал этот шаблон несколько раз, и он сработал очень хорошо для меня.

person Jim D'Angelo    schedule 13.06.2011
comment
Это не то решение, которое я точно реализовал, я подробно описал свое решение в исходном вопросе выше. Просто публикую этот комментарий для удобства использования будущими пользователями. Спасибо jdangelo за помощь! - person Alec; 13.06.2011
comment
Это отличный способ сделать это, потому что он не требует никаких изменений в Application_Start() и, следовательно, может использоваться повторно. - person xkingpin; 20.02.2014

Мой метод Seed не вызывался даже при правильном вызове Database.SetInitializer в Application_Start... Причина этого была очень проста: инициализатор может вообще не вызываться, если у вас еще нет кода, который фактически использует контекст базы данных.

person user1068352    schedule 11.02.2012

Это моя грустная маленькая история.

Во-первых, извлеченные уроки:

  1. Метод seed не будет вызываться до тех пор, пока не будет использован контекст.
  2. Global.asax.cs не столкнется с точкой останова при первом запуске, потому что он запускается до подключения отладчика. Чтобы попасть в точку останова в Global.asax.cs, вы можете добавить немного пробела в Web.config и нажать на страницу; тогда будет удар.
  3. Если есть подключения VS к БД, раздача не произойдет. Приложение выдаст ошибку.

Итак, чтобы не было грусти:

  • Отключите соединение с VS.
  • Переключите базовый класс DropCreateDatabaseAlways за один раз.
  • Нажмите на страницу, которая использует контекст.

Теперь грусть:

  1. У меня был собственный класс Initializer в файле Global.asax.cs. У меня была точка останова в моем методе Initializer Seed; Я запустил приложение, и метод так и не сработал. :(
  2. Я указываю точку останова в вызове Database.SetInitializer в Application_Start. Это никогда не попадало. :(
  3. Я понял, что у меня нет изменений схемы БД, поэтому я изменил DropCreateDatabaseIfModelChanges на DropCreateDatabaseAlways. Еще ничего. :(
  4. Наконец я перешел на страницу, которая использует контекст, и это сработало. :/
person Davious    schedule 22.11.2012
comment
это работает, и спасибо за эту полезную информацию. Я закрыл соединение с базой данных, а затем переключил базовый класс на DropCreateDatabaseAlways. Это сработало хорошо. - person Rasshme Chawla; 01.02.2013
comment
Global запускается только в первый раз. Так что закройте IISExpress. Установите точку останова в глобальном масштабе и запустите. Это произойдет один раз, потому что это уровень приложения IIS. - person Piotr Kula; 06.11.2014

Вы можете вызвать update-database, чтобы вручную запустить начальный метод внутри класса Configuration. Для этого также необходимо, чтобы enable-migrations был включен.

PM> update-database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
No pending code-based migrations.
Running Seed method.

internal sealed class Configuration : DbMigrationsConfiguration<ProjectManager.Data.Database.ProjectDb>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(ProjectManager.Data.Database.ProjectDb context)
    {
        context.Status.AddOrUpdate(
            new Status() { Id = 1, Text = "New" },
            new Status() { Id = 2, Text = "Working" },
            new Status() { Id = 3, Text = "Completed" },
            new Status() { Id = 4, Text = "Skipped" }
        );
    }
}
person Despertar    schedule 15.04.2013

У меня сработало следующее изменение в файле Global.asax:

Старый код:

    protected void Application_Start()
    {
        Database.SetInitializer<mycontextclassname>(new DropCreateDatabaseAlways<mycontextclassname>());             
       ...
    }

Новый код:

    protected void Application_Start()
    {
        Database.SetInitializer<mycontextclassname>(new DropCreateDatabaseAlways<mycontextclassname>()); 
        Database.SetInitializer(new Initializer()); 
        ...
    }
person Steven Burton    schedule 13.06.2012

У меня тоже были трудности с вызовом Seed(). И я ценю все полезные советы, приведенные выше, и мне повезло использовать DropCreateDatabaseAlways... но не ВСЕГДА!!

Совсем недавно я добавил следующую строку кода в конструктор моего репозитория для хорошего эффекта:

    public CatalogRepository()
    {
        _formCatalog.FormDescriptors.GetType();

    }

Этого было достаточно, чтобы вызвать вызов Seed(). Если вы перепробовали все выше этого ответа и все равно не повезло, попробуйте. Удачи, это был действительно трудоемкий опыт.

person Joe    schedule 28.12.2012

Начальное событие в вашем примере будет запущено только один раз, поскольку вы используете DropCreateDatabaseIfModelChanges, вы можете изменить это на DropCreateDatabaseAlways, я думаю, и оно должно запускать начальное событие каждый раз.

Изменить

Это мой DataContext

public WebContext()
{   
    DbDatabase.SetInitializer(new DropCreateDatabaseIfModelChanges<WebContext>());
}
person Richard Forrest    schedule 10.06.2011
comment
Я пробовал переключаться. Не повезло. Все равно создал БД и все таблицы но не запустил. - person Alec; 11.06.2011
comment
Я вызываю Database.SetInitializer‹DatabaseContext›(new DatabaseInitializer()); в конструкторе DatabaseContext, который работает для меня - person Richard Forrest; 11.06.2011
comment
Я не могу ссылаться на DatabaseInitializer в DatabaseContext, так как это будет циклическая ссылка. Также не нужно переходить в конструктор DatabaseContext, так как он должен вызываться конструктором DatabaseInitializer в App_Start (насколько я понимаю). - person Alec; 13.06.2011

Это случилось со мной, когда я открывал для себя функции Code First. Такая ситуация часто возникает, когда вы впервые использовали Code First для создания базы данных без какой-либо стратегии инициализации.

Если вы решите сделать это позже, внедрив стратегию, основанную на DropCreateDatabaseIfModelChanges, но без изменения вашей модели, то ваш метод Seed не будет вызываться с момента создания базы данных, и ваша стратегия будет применяться только при следующем изменении вашей модели.

Если это произойдет с вами, просто попробуйте немного изменить свою модель, чтобы проверить эту гипотезу, и я уверен, что ваша база данных будет заполнена;)

У меня пока нет решения, за исключением использования стратегии, которая всегда генерирует базу данных, но меня действительно не устраивает факт размещения стратегии инициализации в вашем DbContext, поскольку этот класс будет использоваться в вашей производственной среде, хотя стратегия инициализации, по-видимому, в основном используется для свободной среды разработки.

person Bruno    schedule 05.01.2012

Я только что столкнулся с этой проблемой. Я удалил раздел "connectionstrings" из файла Web.config, и в настоящее время приложение запустилось - без раздела connectionstrings! Я добавляю раздел обратно, и база данных снова не заполняется. Это не правильное решение, но я просто добавляю сюда данные о том, что потенциально может решить проблему.

К счастью, это всего лишь небольшое «одноразовое» приложение, которое я все равно скоро выброшу...

person Evgeny    schedule 16.06.2012

Тщательно убедитесь, что вы не объявляли переменную контекста более одного раза. Если вы объявите его снова после раздачи, семя будет перезаписано.

person KSHMR    schedule 05.01.2016

У меня была такая же проблема, и после изменения как в файле Global.asax, так и в файле Intializer все заработало. Я надеюсь, что это сработает для тех, у кого все еще есть проблемы с заполнением данных.

Новый код в Global.asax:

    protected void Application_Start()
    {
        Database.SetInitializer<mycontextclassname>(new DropCreateDatabaseAlways<mycontextclassname>()); 
        Database.SetInitializer(new Initializer()); 
        ...
    }

код для файла инициализатора:

public class Initializer : System.Data.Entity.DropCreateDatabaseAlways<Context>
person mudassir sheikh    schedule 21.08.2016

Обновлено, чтобы отметить, что этот ответ неверен! Причина, по которой моя БД не была заполнена, остается загадкой (но это не отсутствие вызова базового конструктора по умолчанию, как отметил @JaredReisinger)

Я понимаю, что этот вопрос немного устарел, но я оказался здесь, чтобы кто-то другой мог. Вот мой двухпенсовик:

Моя БД создавалась нормально, но не заполнялась, даже если я удалил базу данных и снова начал использовать DropDatabaseInitialiser.

Прочитав приведенный выше код, я заметил, что мой конструктор контекста был таким

public MyApp_Context()
{
    // some code
}

тогда как приведенный выше пример будет выглядеть следующим образом для моей настройки

public MyApp_Context() : base("name=MyApp_Context")
{
    // some code
}

Да, я не вызывал конструктор базового объекта! Я бы не ожидал, что в этом случае сработает все, кроме заполнения, но это, похоже, (повторяющийся) случай.

NB, на самом деле мне не нужно указывать имя контекста в вызове базового конструктора; Я написал это так только изначально, потому что копировал формат решения выше. Итак, мой код теперь такой, и заполнение работает при первоначальном создании базы данных.

public MyApp_Context() : base()
{
    // some code
}
person Jon    schedule 09.07.2013
comment
Конструктор базового класса по умолчанию вызывается автоматически каждый раз, когда вы не вызываете конкретный его вариант напрямую. См. stackoverflow.com /questions/13166019/ для получения дополнительной информации. - person JaredReisinger; 29.10.2016
comment
Да, я должен был бы согласиться с вами. Единственное, когда я писал это три года назад, я, кажется, заметил, что исключение (избыточного) вызова base() приводит к другому результату, чем его включение. Я мог бы раскопать этот код в какой-то момент, но, вероятно, безопаснее предположить, что там проявлялся какой-то другой эффект. - person Jon; 08.11.2016