2010-12-19 136 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方法拋出異常,則所有數據都在內存中,因此一旦程序終止,您可能會丟失數據。)模型更改不完全是事後的行爲,因此請務必將數據備份。