Entity Framework Code-First Issues (таблица UserProfile SimpleMembership)
Если вы использовали ASP.NET MVC 4, вы заметите, что по умолчанию для интернет-приложения используется поставщик SimpleMembership, все это хорошо, хорошо и отлично работает.
Проблема связана с генерацией базы данных по умолчанию, у них есть POCO для UserProfile
, определенной так:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
.., который затем генерируется следующим образом:
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
Это прекрасно работает, база данных генерируется просто отлично и работает без проблем. Однако, если я должен изменить POCO, как это, и удалить базу данных:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string EmailAddress { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public string Country { get; set; }
public string CompanyName { get; set; }
}
Создаются только первые 2 столбца, UserId
и EmailAddress
. Он работает очень хорошо по коду (говорит логин/регистрация), но, очевидно, ни одна из моих других пользовательских данных не сохраняется.
Я что-то упустил? Разумеется, он должен генерировать базу данных на основе всего объекта UserProfile
.
Ответы
Ответ 1
Кажется, я, возможно, наконец получил это, и это могло быть просто гигантским недоразумением.
Как оказалось, я ожидал, что ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
сделает то, что просто не делает, что создает все таблицы в базе данных, которые не существуют, или просто обновляют их, если они это делают, и они отличается.
Что на самом деле происходит, так это то, что он буквально запускает оператор CREATE DATABASE
, который для меня является самой бесполезной вещью. Если вы не работаете в действительно странной среде, у вас всегда будет база данных, и она всегда будет существовать (и впоследствии создание таблицы никогда не произойдет!), Я бы предпочел не предоставлять доступ к реальным пользователям для создания базы данных в любом случае.
Во всяком случае, я решил решить проблему с UserProfile
(и связанными с ней таблицами) для создания базы данных с помощью инициализатора DropCreateDatabaseIfModelChanges
и принудительной инициализации, как показано ниже:
public SimpleMembershipInitializer()
{
#if DEBUG
Database.SetInitializer<DataContext>(new DropCreateDatabaseIfModelChanges<DataContext>());
#else
Database.SetInitializer<DataContext>(null);
#endif
try
{
using (var context = new DataContext())
{
if (!context.Database.Exists())
{
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
context.Database.Initialize(true);
}
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
.. это работает и идеально подходит для разработки, но на практике практически бесполезно, так как буквально отбросит базу данных и воссоздает ее с нуля, если модель изменится. Для меня это делает практически всю кодовую практику практически бесполезной в своей форме по умолчанию, и я, вероятно, вернусь назад к генерации edmx от DB.
"Тайна" за созданной таблицей UserProfile
заключается в том, что WebSecurity.InitializeDatabaseConnection
инициализирует таблицу, если она не существует на основе полей, которые вы передаете в нее, поэтому вместо этого был создан EmailAddress
из UserName
, потому что я изменил его в этом.
Ответ 2
1 - Необходимо включить миграцию, предпочтительно с EntityFramework 5. Используйте Enable-Migrations
в диспетчере пакетов NuGet.
2 - Переместите
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true);
к вашему методу Seed в классе YourMvcApp/Migrations/Configuration.cs
protected override void Seed(UsersContext context)
{
WebSecurity.InitializeDatabaseConnection(
"DefaultConnection",
"UserProfile",
"UserId",
"UserName", autoCreateTables: true);
if (!Roles.RoleExists("Administrator"))
Roles.CreateRole("Administrator");
if (!WebSecurity.UserExists("lelong37"))
WebSecurity.CreateUserAndAccount(
"lelong37",
"password",
new {Mobile = "+19725000000", IsSmsVerified = false});
if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
Roles.AddUsersToRoles(new[] {"lelong37"}, new[] {"Administrator"});
}
Теперь EF5 будет отвечать за создание вашей таблицы UserProfile, после этого вы вызовите WebSecurity.InitializeDatabaseConnection, чтобы зарегистрировать SimpleMembershipProvider с уже созданной таблицей UserProfile, а также рассказать SimpleMembershipProvider, какой столбец UserId и UserName. Я также показываю пример того, как вы можете добавлять пользователей, роли и связывать их в методе Seed с пользовательскими свойствами/полями UserProfile, например. пользователя Mobile (number).
3 - Теперь, когда вы запускаете базу данных обновлений из Консоли диспетчера пакетов, EF5 предоставит вашей таблице все ваши пользовательские свойства.
Дополнительные ссылки см. в этой статье с исходным кодом:
http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/
Ответ 3
У меня была такая же проблема. Я добавил код, чтобы миграции выполнялись непосредственно перед "CreateDatabase" в SimpleMembershipInitializer.
Это исправило проблему для меня, за исключением того, что я считаю, что теперь мои миграции будут применяться в Azure независимо от настройки в профиле публикации.
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
Database.SetInitializer<WomContext>(null);
// forcing the application of the migrations so the users table is modified before
// the code below tries to create it.
var migrations = new MigrateDatabaseToLatestVersion<WomContext, Wom.Migrations.Configuration>();
var context = new WomContext();
migrations.InitializeDatabase(context);
try
{....
Ответ 4
Если у вас нет планов вносить изменения после того, как система работает вживую, и это происходит только в процессе разработки и не стремится к миграции. Попробуйте обрезать таблицу __MigrationHistory.
truncate table __MigrationHistory