2012-09-29 14 views
11

Estoy buscando sugerencias sobre cómo abordar el uso de un ORM (en este caso, EF5) en el diseño de aplicaciones no monolíticas modulares, con La parte central y los módulos de terceros, donde el núcleo no tiene referencia directa a los módulos de terceros, y los módulos solo tienen una referencia a las tablas y clases principales/comunes.EF: En busca de una estrategia de diseño para DatabaseFirst DbContext de una Aplicación modular

Por razones de argumento, una analogía suficientemente cercana sería DNN.

CodeFirst:

Con CodeFirst, el enfoque que utilicé fue para construir el modelo de la Db era a través de la reflexión: en la fase DbInitialation de DbContext del Núcleo, solía Reflexión de encontrar cualquier clase en cualquier DLL (por ejemplo, Core o varios módulos) decorados con IDbInitializer (un contrato personalizado que contiene un método Execute()) para definir solo la estructura del dll. Cada DLL agregó al DbModel lo que sabía de sí mismo. Cualquier Siembra subsecuente también se manejó en el mismo wa (buscando un contrato IDbSeeder específico, y ejecutándolo).

Pro: * el enfoque funciona por ahora. * El mismo núcleo DbContext se puede usar en todos los repositorios, siempre que cada repositorio use dbContext.GetSet(), en lugar de esperar que sea una propiedad de dbContext. No es gran cosa. Contras: * solo funciona al inicio (es decir, agregar nuevos módulos requeriría una actualización de AppPool). * CodeFirst es ideal para un POC. Pero en EF5, no es lo suficientemente maduro para el trabajo empresarial pero (y no puedo esperar a que se agregue EF6 para StoredProcs y otras características). * Mi DBA odia CodeFirst, al menos para el Core, que quiere optimizar esa parte con Stored Procs tanto como sea posible ... Somos un equipo, así que tengo que tratar de encontrar una manera de complacerlo, si se puede encontrar una manera ...

base de datos en primer lugar:

la fase DbModel parece estar ocurriendo antes del constructor de la DbContext (al leer el archivo de recursos .edmx * incorporado). DbInitialization nunca se invoca (ya que el modelo se considera completo), por lo que no puedo agregar más tablas de las que el Core conoce.

Si no puedo agregar elementos al Modelo, dinámicamente, como se puede hacer con CodeFirst, significa que * o el Modelo de Core DbContext tiene que tener conocimiento de cada tabla en el Db-Core Y cada 3 módulo. Haciendo la aplicación monolítica y altamente acoplada, derrotando lo que estoy tratando de lograr. * O cada tercero tiene que crear su propio DbContext, importar tablas Core, lo que lleva a * problemas de versionado (el módulo no actualiza sus * .edmx's cuando Core's * .edmx está actualizado, etc.) * duplicación en todas partes, en diferentes memorias contextos = difícil de rastrear problemas de concurrencia.

En este punto, me parece que el enfoque CodeFirst es la única forma en que se puede lograr el software Modular con EF. Pero, con suerte, alguien más sabe cómo hacer que DatabaseFirst brille: ¿hay alguna manera de "agregar" DbSet al modelo creado a partir del archivo * .edmx incrustado?

¿O alguna otra idea?

Respuesta

1

Si bien es un enfoque CodeFirst, y no la solución más limpia, ¿qué hay de forzar la inicialización para ejecutarse incluso después de la puesta en marcha, como en la respuesta publicada aquí: Forcing code-first to always initialize a non-existent database? (Sé exactamente nada sobre CodeFirst, pero viendo esto es un mes viejo sin publicaciones, vale la pena intentarlo)

4

Considero utilizar una especie de arquitectura plugin, ya que ese es su diseño general para la aplicación en sí.

Usted puede lograr los fundamentos de este haciendo algo como lo siguiente (tenga en cuenta que este ejemplo se utiliza StructureMap - aquí hay un enlace a la StructureMap Documentation):

  1. crear una interfaz desde la que sus DbContext objetos pueden derivar.

    public interface IPluginContext { 
        IDictionary<String, DbSet> DataSets { get; } 
    } 
    
  2. En su inyección de dependencias configuración (utilizando StructureMap) - hacer algo como lo siguiente:

    Scan(s => { 
        s.AssembliesFromApplicationBaseDirectory(); 
        s.AddAllTypesOf<IPluginContext>(); 
        s.WithDefaultConventions(); 
    }); 
    For<IEnumerable<IPluginContext>>().Use(x => 
        x.GetAllInstances<IPluginContext>() 
    ); 
    
  3. Para cada uno de sus complementos, ya sea alterar el archivo {plugin}.Context.tt - o añadir un partial class archivo que causa que el DbContext se genere para derivar de IPluginContext.

    public partial class FooContext : IPluginContext { } 
    
  4. alterar el archivo {plugin}.Context.tt para cada plugin para exponer algo como:

    public IDictionary<String, DbSet> DataSets { 
        get { 
         // Here is where you would have the .tt file output a reference 
         // to each property, keyed on its property name as the Key - 
         // in the form of an IDictionary. 
        } 
    } 
    
  5. Ahora puede hacer algo como lo siguiente:

    // This could be inside a service class, your main Data Context, or wherever 
    // else it becomes convenient to call. 
    public DbSet DataSet(String name) { 
        var plugins = ObjectFactory.GetInstance<IEnumerable<IPluginContext>>(); 
        var dataSet = plugins.FirstOrDefault(p => 
         p.DataSets.Any(ds => ds.Key.Equals(name)) 
        ); 
    
        return dataSet; 
    } 
    

Perdóname si la sintaxis no es perfecta, estoy haciendo esto dentro de la publicación, no dentro del compilador.

El resultado final le da la flexibilidad necesaria para hacer algo como:

// Inside an MVC controller... 
    public JsonResult GetPluginByTypeName(String typeName) { 
     var dataSet = container.DataSet(typeName); 
     if (dataSet != null) { 
      return Json(dataSet.Select()); 
     } else { 
      return Json("Unable to locate that object type."); 
     } 
    } 

evidente que, en el largo plazo - usted quiere que el control se invierte, en el que el plugin es la que realmente vincularse a la arquitectura, en lugar de que el servidor espere un tipo. Sin embargo, puede lograr el mismo tipo de cosas usando este tipo de carga diferida, donde la aplicación principal expone un punto final al que se vinculan todos los complementos.

Eso sería algo así como:

public interface IPlugin : IDisposable { 
    void EnsureDatabase(); 
    void Initialize(); 
} 

Ahora puede exponer a esta interfaz para cualquier desarrolladores de aplicaciones que se van a crear plugins para su arquitectura (estilo DNN) - y la configuración de StructureMap funciona algo así como:

Scan(s => { 
    s.AssembliesFromApplicationBaseDirectory(); // Upload all plugin DLLs here 
    // NOTE: Remember that this gives people access to your system!!! 
    //  Given what you are developing, though, I am assuming you 
    //  already get that. 
    s.AddAllTypesOf<IPlugin>(); 
    s.WithDefaultConventions(); 
}); 
For<IEnumerable<IPlugin>>().Use(x => x.GetAllInstances<IPlugin>()); 

Ahora, cuando usted hace funcionar su aplicación, puede hacer algo como:

// Global.asax 
public static IEnumerable<IPlugin> plugins = 
    ObjectFactory.GetInstance<IEnumerable<IPlugin>>(); 

public void Application_Start() { 
    foreach(IPlugin plugin in plugins) { 
     plugin.EnsureDatabase(); 
     plugin.Initialize(); 
    } 
} 

Cada uno de sus objetos IPlugin ahora puede contener su propio contexto de base de datos, administrar el proceso de instalación (si es necesario) su propia instancia de base de datos/tablas, y deshacerse de sí mismo con elegancia.

Claramente, esta no es una solución completa, pero espero que lo inicie en una dirección útil. :) Si puedo ayudar a aclarar algo aquí, por favor hágamelo saber.

Cuestiones relacionadas