2010-12-19 181 views
13

我目前正在使用EF代码优先使用POCO的项目。到目前为止,我有5个POCO取决于POCO“用户”。EF代码优先 - 重建数据库如果模型更改

POCO“User”应该引用我已经存在的MemberShip表“aspnet_Users”(我将它映射到DbContext的OnModelCreating方法中)。

问题是,我想利用“重建数据库如果模型更改”功能,如Scott Gu所示:http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx - 该功能的基本功能是在看到我的任何更改时重新创建数据库波苏斯。我想要它做的是重新创建数据库,但以某种方式不删除整个数据库,以便aspnet_Users仍然活着。然而,它似乎不可能,因为它或者使一个全新的数据库或替换当前的一个..

所以我的问题是:我注定要手工定义我的数据库表,或者我可以以某种方式合并我的POCO到我目前数据库,并仍然使用该功能而不用全部擦除?

回答

16

从CTP5的EF Code First开始,这是不可能的。 Code First将删除并创建您的数据库,或者根本不会触及它。我认为在你的情况下,你应该手动创建完整的数据库,然后尝试创建一个匹配数据库的对象模型。

这就是说,EF团队正在积极努力,你正在寻找的功能:改变数据库,而不是重新创建的:

你可能会考虑Code First Database Evolution (aka Migrations)

+0

Alpha 3现已发布:http://blogs.msdn.com/b/adonet/archive/2011/09/21/code-first-migrations-alpha-3-no-magic-walkthrough.aspx – 2011-10-12 22:58:03

5

的一件事是使用'断开“外键。您可以单独保留ASPNETDB,只需使用用户密钥(guid)将用户引用到数据库中。您可以访问登录的用户,如下所示:

MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */); 

然后使用用户的密钥为FK在DB:

Guid UserId = (Guid) currentUser.ProviderUserKey ; 

这种方法解耦与ASPNETDB您的数据库和相关的供应商体系结构。但是,从操作上讲,数据当然会松散地连接起来,因为这些ID将存储在每个数据库中。请注意也不会有参考约束,这可能会或可能不会成为您的问题。

7

我只是能够与以下考虑这样做在EF 4.1:

的数据库仍然被删除并重新创建 - 它必须是模式才能反映您的模型更改 - 但您的数据保持不变。

以下是您将数据库读入内存中的POCO对象,然后在POCO对象成功将其写入内存之后,您可以让EF删除并重新创建数据库。下面是一个例子

public class NorthwindDbContextInitializer : DropCreateDatabaseAlways<NorthindDbContext> { 

    /// <summary> 
    /// Connection from which to ead the data from, to insert into the new database. 
    /// Not the same connection instance as the DbContext, but may have the same connection string. 
    /// </summary> 
    DbConnection connection; 
    Dictionary<Tuple<PropertyInfo,Type>, System.Collections.IEnumerable> map; 
    public NorthwindDbContextInitializer(DbConnection connection, Dictionary<Tuple<PropertyInfo, Type>, System.Collections.IEnumerable> map = null) { 
     this.connection = connection;   
     this.map = map ?? ReadDataIntoMemory();   
    } 

    //read data into memory BEFORE database is dropped 
    Dictionary<Tuple<PropertyInfo, Type>, System.Collections.IEnumerable> ReadDataIntoMemory() { 
     Dictionary<Tuple<PropertyInfo,Type>, System.Collections.IEnumerable> map = new Dictionary<Tuple<PropertyInfo,Type>,System.Collections.IEnumerable>(); 
     switch (connection.State) { 
      case System.Data.ConnectionState.Closed: 
       connection.Open(); 
       break; 
     } 
     using (this.connection) { 
      var metaquery = from p in typeof(NorthindDbContext).GetProperties().Where(p => p.PropertyType.IsGenericType) 
          let elementType = p.PropertyType.GetGenericArguments()[0] 
          let dbsetType = typeof(DbSet<>).MakeGenericType(elementType) 
          where dbsetType.IsAssignableFrom(p.PropertyType) 
          select new Tuple<PropertyInfo, Type>(p, elementType); 

      foreach (var tuple in metaquery) { 
       map.Add(tuple, ExecuteReader(tuple)); 
      } 
      this.connection.Close(); 
      Database.Delete(this.connection);//call explicitly or else if you let the framework do this implicitly, it will complain the connection is in use. 
     }  
     return map; 
    } 

    protected override void Seed(NorthindDbContext context) { 

     foreach (var keyvalue in this.map) { 
      foreach (var obj in (System.Collections.IEnumerable)keyvalue.Value) { 
       PropertyInfo p = keyvalue.Key.Item1; 
       dynamic dbset = p.GetValue(context, null); 
       dbset.Add(((dynamic)obj)); 
      } 
     } 

     context.SaveChanges(); 
     base.Seed(context); 
    } 

    System.Collections.IEnumerable ExecuteReader(Tuple<PropertyInfo, Type> tuple) { 
     DbCommand cmd = this.connection.CreateCommand(); 
     cmd.CommandText = string.Format("select * from [dbo].[{0}]", tuple.Item2.Name); 
     DbDataReader reader = cmd.ExecuteReader(); 
     using (reader) { 
      ConstructorInfo ctor = typeof(Test.ObjectReader<>).MakeGenericType(tuple.Item2) 
             .GetConstructors()[0]; 
      ParameterExpression p = Expression.Parameter(typeof(DbDataReader)); 
      LambdaExpression newlambda = Expression.Lambda(Expression.New(ctor, p), p); 
      System.Collections.IEnumerable objreader = (System.Collections.IEnumerable)newlambda.Compile().DynamicInvoke(reader); 
      MethodCallExpression toArray = Expression.Call(typeof(Enumerable), 
      "ToArray", 
      new Type[] { tuple.Item2 }, 
      Expression.Constant(objreader)); 
      LambdaExpression lambda = Expression.Lambda(toArray, Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(tuple.Item2))); 
      var array = (System.Collections.IEnumerable)lambda.Compile().DynamicInvoke(new object[] { objreader }); 
      return array; 
     }   
    } 
} 

这个例子依赖于一个ObjectReader类,你可以找到here如果你需要它。

我不会打扰博客文章,阅读documentation

最后,我仍然建议您在运行初始化之前始终备份数据库。 (例如,如果Seed方法抛出异常,则所有数据都在内存中,因此一旦程序终止,您可能会丢失数据。)模型更改不完全是事后的行为,因此请务必将数据备份。