2010-12-19 15 views
13

Actualmente estoy trabajando en un proyecto que está utilizando EF Code First con POCO. Tengo 5 POCO que hasta ahora depende del POCO "Usuario".Código EF Primero: Recrear la base de datos si el modelo cambia

El "Usuario" de POCO debe hacer referencia a mi tabla de miembros existente "aspnet_Users" (a la que asigno en el método OnModelCreating de DbContext).

El problema es que quiero aprovechar la función "Volver a crear la base de datos si el modelo cambia" como muestra Scott Gu en: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx - Lo que hace básicamente es recrear la base de datos tan pronto como ve algún cambio en mi POCOs. Lo que quiero que haga es Recrear la base de datos pero de alguna manera NO borrar toda la Base de datos para que aspnet_Users aún esté activo. Sin embargo, parece imposible ya que hace una base de datos completamente nueva o reemplaza la actual con ...

Entonces mi pregunta es: ¿estoy condenado a definir las tablas de mi base de datos a mano, o puedo fusionar de alguna manera mis POCO en mi actual base de datos y aún utilizar la característica sin borrar todo?

Respuesta

16

A partir de EF Code First en CTP5, esto no es posible. Code First soltará y creará su base de datos o no la tocará en absoluto. Creo que en su caso, debe crear manualmente su base de datos completa y luego tratar de llegar a un modelo de objetos que coincida con la base de datos.

Dicho esto, el equipo de EF está trabajando activamente en la característica que usted está buscando: la alteración de la base de datos en lugar de volver a crearlo:

Code First Database Evolution (aka Migrations)

+0

Alpha 3 está disponible ahora: http://blogs.msdn.com/b/adonet/archive/2011/09/21/code-first-migrations-alpha-3-no-magic-walkthrough.aspx –

5

Una cosa que podría considerar es el uso de un " clave externa desconectada. Puede dejar ASPNETDB solo y simplemente hacer referencia al usuario en su base de datos utilizando la clave de usuario (guid). Se puede acceder el usuario conectado de la siguiente manera:

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

Y a continuación, utilizar la clave del usuario como FK en su base de datos:

Guid UserId = (Guid) currentUser.ProviderUserKey ; 

Este enfoque desacopla su base de datos con el ASPNETDB y el proveedor asociado arquitectónicamente. Sin embargo, desde el punto de vista operativo, los datos estarán, por supuesto, poco conectados ya que los ID estarán en cada DB. Tenga en cuenta también que no habrá restricciones referenciales, que pueden o no ser un problema para usted.

7

yo era capaz de hacer esto en EF 4.1, con las siguientes consideraciones:

La base de datos es aún se elimina y vuelve a crear, tiene que ser para que el esquema refleje los cambios de su modelo, pero sus datos permanecen intactos.

Aquí se explica cómo: usted lee su base de datos en sus objetos POCO en memoria, y luego de que los objetos POCO hayan logrado ingresar a la memoria, deja que EF suelte y vuelva a crear la base de datos.Aquí está un ejemplo

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; 
     }   
    } 
} 

Este ejemplo se basa en una clase ObjectReader donde puede encontrar here si lo necesita.

No me molestaría con los artículos del blog, lea el documentation.

Por último, le sugiero que siempre haga una copia de seguridad de su base de datos antes de ejecutar la inicialización. (por ejemplo, si el método Seed arroja una excepción, todos sus datos están en la memoria, por lo que corre el riesgo de que se pierdan sus datos una vez que finaliza el programa.) Un cambio de modelo no es exactamente una acción de último momento, así que asegúrese de respaldar sus datos .

Cuestiones relacionadas