he estado buscando una respuesta a th es una pregunta y descubrí que es fácil hacerlo usando el Marco de Extensibilidad Administrada. Hay una manera más rápida en la parte inferior de esta publicación, sin embargo, MEF permite un enfoque mucho más escalable.
MEF le permite crear complementos de acceso dinámico desde ensamblajes dispares; sin embargo, se puede usar para rellenar rápidamente colecciones dentro de una sola aplicación de ensamblaje. En esencia, lo usaremos como una forma segura de reflejar nuestro ensamblaje de nuevo en la clase. Para hacer esto completamente funcional, también voy a implementar el Patrón de Estrategia para el Modelo de Marco de la Entidad.
Agregue una referencia a su proyecto, señalando System.ComponentModel.Composition
. Esto dará acceso a la biblioteca de MEF.
Ahora, tenemos que implementar el Patrón de estrategia. Si no tiene una carpeta Interfaces, cree una y agregue IEntity.cs, como se muestra a continuación.
IEntity.cs
namespace Your.Project.Interfaces
{
/// <summary>
/// Represents an entity used with Entity Framework Code First.
/// </summary>
public interface IEntity
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
int Id { get; set; }
}
}
Ahora, cada uno de ustedes entidades concretas necesita para implementar esta interfaz:
public class MyEntity : IEntity
{
#region Implementation of IEntity
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
public int Id { get; set; }
#endregion
// Other POCO properties...
}
me parece que es la mejor práctica, para no crear interfaces individuales para cada entidad, a menos que esté trabajando en un ambiente de prueba alto. Pragmáticamente, las interfaces solo deberían usarse cuando se necesita ese nivel de abstracción; principalmente cuando heredará más de una clase concreta o cuando se trabaje con un motor de inversión de control demasiado entusiasta. Si tiene interfaces para todo en su modelo de producción, su arquitectura es más que probable, tiene fallas importantes. De todos modos, basta de las divagaciones.
Ahora que tenemos todas nuestras entidades "estratégicas", podemos utilizar MEF para cotejarlas y completar una colección dentro de su contexto.
Dentro de su contexto, añadir una nueva propiedad:
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
[ImportMany(typeof(DbSet<IEntity>))]
public IEnumerable<DbSet<IEntity>> Sets { get; private set; }
El [ImportMany(typeof(DbSet<IEntity>))]
aquí, permite MEF para poblar la colección.
A continuación, añadir el atributo correspondiente Export
a cada DbSet dentro del contexto:
[Export(typeof(DbSet<IEntity>))]
public DbSet<MyEntity> MyEntities { get; set; }
Cada una de las propiedades Import
ed y Export
ed se conoce como una "parte". La última pieza del rompecabezas es componer esas partes. Agregue lo siguiente al constructor de su contexto:
// Instantiate the Sets list.
Sets = new List<DbSet<IEntity>>();
// Create a new Types catalogue, to hold the exported parts.
var catalogue = new TypeCatalog(typeof (DbSet<IEntity>));
// Create a new Composition Container, to match all the importable and imported parts.
var container = new CompositionContainer(catalogue);
// Compose the exported and imported parts for this class.
container.ComposeParts(this);
Ahora, con un poco de suerte, usted debe tener una lista dinámica de población de DbSets, dentro de su contexto.
He usado este método para permitir truncar fácilmente todas las tablas mediante un método de extensión.
/// <summary>
/// Provides extension methods for DbSet objects.
/// </summary>
public static class DbSetEx
{
/// <summary>
/// Truncates the specified set.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <param name="set">The set.</param>
/// <returns>The truncated set.</returns>
public static DbSet<TEntity> Truncate<TEntity>(this DbSet<TEntity> set)
where TEntity : class, IEntity
{
set.ToList().ForEach(p => set.Remove(p));
return set;
}
}
He añadido un método al contexto para truncar toda la base de datos.
/// <summary>
/// Truncates the database.
/// </summary>
public void TruncateDatabase()
{
Sets.ToList().ForEach(s => s.Truncate());
SaveChanges();
}
EDIT (Revisión):
La solución anterior ha sido ahora depreciado. Hubo que hacer algunos tweets para que esto funcione ahora. Para que esto funcione, debe importar los DbSets en una colección temporal de DbSet del tipo "objeto", luego, eche esta colección a DbSet del tipo de interfaz requerido. Para propósitos básicos, la interfaz IEntity será suficiente.
#region Dynamic Table List
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
public List<DbSet<IEntity>> Tables { get; private set; }
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
[ImportMany("Sets", typeof (DbSet<object>), AllowRecomposition = true)]
private List<object> TableObjects { get; set; }
/// <summary>
/// Composes the sets list.
/// </summary>
/// <remarks>
/// To make this work, you need to import the DbSets into a temporary collection of
/// DbSet of type "object", then cast this collection to DbSet of your required
/// interface type. For basic purposes, the IEntity interface will suffice.
/// </remarks>
private void ComposeSetsList()
{
// Instantiate the list of tables.
Tables = new List<DbSet<IEntity>>();
// Instantiate the MEF Import collection.
TableObjects = new List<object>();
// Create a new Types catalogue, to hold the exported parts.
var catalogue = new TypeCatalog(typeof (DbSet<object>));
// Create a new Composition Container, to match all the importable and imported parts.
var container = new CompositionContainer(catalogue);
// Compose the exported and imported parts for this class.
container.ComposeParts(this);
// Safe cast each DbSet<object> to the public list as DbSet<IEntity>.
TableObjects.ForEach(p => Tables.Add(p as DbSet<IEntity>));
}
#endregion
A continuación, ejecute el CompileSetsList()
fachada del constructor (con las mejores prácticas para la Web se muestra):
public MvcApplicationContext()
{
// Enable verification of transactions for ExecuteSQL functions.
Configuration.EnsureTransactionsForFunctionsAndCommands = true;
// Disable lazy loading.
Configuration.LazyLoadingEnabled = false;
// Enable tracing of SQL queries.
Database.Log = msg => Trace.WriteLine(msg);
// Use MEF to compile a list of all sets within the context.
ComposeSetsList();
}
Entonces, simplemente decorar tu DbSet <> s como esto:
/// <summary>
/// Gets or sets the job levels.
/// </summary>
/// <value>
/// The job levels.
/// </value>
[Export("Sets", typeof(DbSet<object>))]
public DbSet<JobLevel> JobLevels { get; set; }
Ahora funcionará correctamente.
Un enfoque totalmente diferente en un contexto diferente, pero tal vez sea interesante para usted: http://stackoverflow.com/questions/9762808/change-fluent-api-mapping-dynamically –