Миграция базы данных EF

Я работаю над приложением MVC4, которое использует Entity Framework 5, миграцию базы данных и API членства asp. В моей среде разработки я могу полностью удалить свою базу данных, запустить пакет (.zip), обновить страницу, и все работает как положено; создается база данных, вызывается начальный метод, и некоторые значения по умолчанию сохраняются в базе данных.

Идеально, очень просто! Но... да, всегда есть но!

Когда я собираюсь развернуть этот же пакет (изменив только строку подключения к базе данных) и запустить его в удаленной среде, поведение изменится. Опять же, если я войду и полностью удалю базу данных, запущу пакет (.zip), обновлю страницу, база данных будет создана только с таблицами api членства asp. Я проверил строку подключения, и это точно не причина, иначе не удалось создать базу данных и таблицы членства.

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

У кого-нибудь есть предложения? Может ли это быть побочным эффектом смешанной истории миграции?

Заранее спасибо!


person Greg Sipes    schedule 15.04.2013    source источник
comment
Есть ли шанс, что у вас есть две строки подключения? Для членства и один для вашего приложения?   -  person Floremin    schedule 16.04.2013
comment
Хорошая мысль, но нет, только 1. Я использую преобразования при сборке пакета, так что, может быть, там что-то зарыто не так?   -  person Greg Sipes    schedule 16.04.2013


Ответы (1)


Проект интернет-приложения MVC4 по умолчанию путает многих людей. Microsoft хотела продемонстрировать функциональность SimpleMembership, для которой требуется контекст EF, но не совсем разумно, что вам нужно фактически изменять эту часть, чтобы эффективно использовать остальную часть вашего приложения.

Итак, небольшое введение в то, как работает Entity Framework, поможет прояснить, почему это происходит, я думаю.

Entity Framework (по крайней мере, версия 5, она может измениться в 6 или последующих версиях) допускает только один DbContext для вашего приложения. Скорее всего, у вас их как минимум два: AccountsContext, который был автоматически сгенерирован шаблоном проекта, и контекст, который вы используете для остальной части вашего приложения. Если бы вы отключили автоматические миграции и попытались создать миграцию, EF услужливо сообщит вам, что вам нужно указать, какой контекст использовать. Тем не менее, автоматические миграции используются в Code First по умолчанию, очень немногие отключают их, и поэтому очень немногие получают предупреждения.

Если включена автоматическая миграция и у вас нет существующей базы данных, EF с удовольствием (и в фоновом режиме) создаст для вас базу данных из вашего контекста. Но что, если у вас несколько контекстов? Что ж, еще одна неприятная маленькая особенность EF заключается в том, что он создает базу данных точно в срок. Таким образом, какой контекст используется, зависит от того, к какому из них обращаются в первую очередь. Красиво, да? Таким образом, если вы попытаетесь выполнить что-то вроде входа в систему, база данных будет создана из AccountsContext, а затем, когда вы попытаетесь получить доступ к чему-либо из контекста вашего приложения, база данных уже существует, и EF ничего не делает. .

Итак, что с этим делать? Что ж, вам нужен один контекст, чтобы устранить двусмысленность. Вы по-прежнему можете иметь более одного контекста, если хотите, в своем приложении, но вы должны, по сути, сообщить EF, что все остальные контексты относятся к базе данных в первую очередь, т.е. ничего не делать.

public class AccountsContext : DbContext
{
    public AccountsContext()
        : base("name=YourConnectionStringName")
    {
        Database.SetInitializer<AccountsContext>(null);
    }

    ...
}

public class MyAppContext : DbContext
{
    public MyAppContext()
        : base("name=YourConnectionStringName")
    {
    }

    // All your entities here, including stuff from AccountsContext

}

MyAppContext будет вашим "основным" контекстом, в котором будут все сущности в вашей базе данных, о которых должен знать EF. Вызов base на обоих служит для того, чтобы убрать предположения EF и явно убедиться, что все являются одной и той же страницей в отношении того, какое соединение с базой данных следует использовать. Ваш AccountsContext теперь, по сути, ориентирован на базу данных, поэтому он не заставит EF создать базу данных или попытаться выполнить какие-либо миграции - для этого и предназначен MyAppContext. Вы можете создать другие контексты так же, как AccountsContext, чтобы разбить функциональность вашего приложения; вам просто нужно отразить любые объявления свойств DbSet в вашем «основном» контексте.

Джули Лерман в своей книге Programming называет эту концепцию "ограниченными контекстами". Entity Framework: DbContext, и представляет универсальный класс, от которого вы можете наследовать все свои "ограниченные контексты", поэтому вам не нужно каждый раз указывать имя строки подключения и задавать инициализатору базы данных значение null.

person Chris Pratt    schedule 15.04.2013
comment
Спасибо за ответ. Я добавил вызов, чтобы сначала создать БД контекста учетных записей, но почему это будет нормально работать в одной среде, а не в другой? Надеюсь, у меня будет возможность протестировать это изменение на этой неделе, и тогда я продолжу. - person Greg Sipes; 16.04.2013
comment
Я могу ошибаться, но, похоже, вы не поняли смысл моего ответа. Убедиться, что тот или иной контекст запускается первым, не решает всеобъемлющей проблемы. Вы не можете перенести более одного контекста, поэтому, если контекст учетных записей запускается первым, ни в одной из других ваших моделей приложений никогда не будет таблиц, созданных в базе данных. Вам нужно иметь только один контекст со всем в нем, а затем отключить любые другие контексты, которые у вас есть. - person Chris Pratt; 16.04.2013
comment
Наконец-то у меня появилась возможность проверить это, и вы, кажется, правы... работает только один из двух контекстов, в зависимости от первого запроса. У меня будет возможность запустить код с предложенным вами изменением на следующей неделе, что должно решить проблему, и тогда я отмечу это как ответ. Единственное, на чем я все еще застрял, это почему это работает нормально в моей тестовой среде? - person Greg Sipes; 20.04.2013