EF4 CTP5: расширение модели с помощью модуля MEF - TPH - OnModelCreating
Извините, что не нашел лучшего заголовка. Я пытаюсь расширить модель EF4 CTP5 с помощью модулей MEF. Идея состоит в том, чтобы указать некоторые базовые объекты. Эти базовые объекты расположены рядом с моим классом Context в моих решениях Сборка модели.
Например, будет сущность с именем Variable. Переменная является очень общей сущностью, и я хочу, чтобы некоторые модули приложения определяли специальные переменные, которые предоставляют более подробные свойства, но они должны храниться в одной таблице (TPH - таблица на иерархию).
Для этого я указал интерфейс IModelContextExtension
public interface IModelContextExtension
{
void OnModelCreating(IModelBuilderFacade modelBuilder);
}
Каждый модуль, который хочет использовать пользовательские переменные, должен экспортировать класс, который реализует этот интерфейс. В методе OnModelCreating модели я зацикливаю каждый зарегистрированный модуль и вызываю метод OnModelCreating этого модуля. Затем он может вызвать, например, "RegisterVariableType" в предоставленном IModelBuilderFacade, чтобы объявить тип переменной-переменной (например, MySpecialVariable2).
** Интересная часть: ** Метод RegisterVariableType, кажется, работает очень хорошо, за исключением случаев, когда Variable-Derived-Type находится в другой (MEF-Loaded) сборке. Если я зарегистрирую переменную из другого модуля, то полное отображение будет повреждено. Потому что, когда я сейчас пытаюсь добавить переменную в свой репозиторий, происходит сбой во время добавления поговорки: "Последовательность не содержит элементов". Если я удаляю тип из загруженного модуля, он работает как ожидалось.
Я опубликую материал IRepository, если кому-то интересно, но я уверен, что это не проблема..
Вот метод OnModelCreating из моего контекста (производного от DbContext) класса:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var modelBuilderFacade = new ModelBuilderFacade(modelBuilder);
modelBuilder.Entity<Variable>().HasKey(x => new { x.Id });
////////////////////////////////////
// register derived VariableTypes
modelBuilderFacade.RegisterVariableType(typeof(MySpecialCyclicVariable));
modelBuilderFacade.RegisterVariableType(typeof(MyVerySpecialVariable));
//modelBuilder.Entity<Variable>().HasKey(x => new { x.Id }); modelBuilder.Entity<Variable>()
// .Map<MySpecialVariabe>(m => m.Requires(DiscriminatorColumn).HasValue(typeof(MySpecialVariabe).Name))
// .Map<MyVerySpecialVariable>(m => m.Requires(DiscriminatorColumn).HasValue(typeof(MyVerySpecialVariable).Name))
// .ToTable(VariableTable);
if (ModelExtensions != null)
{
foreach (var modelContextExtension in ModelExtensions)
{
modelContextExtension.OnModelCreating(modelBuilderFacade);
}
}
Map<Variable>(modelBuilder, modelBuilderFacade.VariableTypes, VariableTable);
////////////////////////////////////
}
Функция RegisterVariableType добавляет типы (производные от переменных) к IEnumerable, хранящемуся в modelBuilderFacade.
После того, как типы добавлены, я вызываю свою собственную функцию Map, чтобы сделать отображение TPH.
[Export(typeof(IModelContextExtension))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class Context : IModelContextExtension
{
public void OnModelCreating(IModelBuilderFacade modelBuilder)
{
modelBuilder.RegisterVariableType(typeof(MyVerySpecialVariable2));
}
}
Вот функция карты:
private static void Map<T>(ModelBuilder modelBuilder, IEnumerable<Type> types, string table) where T : class
{
var entityTypeConfigurarion = modelBuilder.Entity<T>();
foreach (var variableType in types)
{
if (!typeof(T).IsAssignableFrom(variableType))
{
throw new InvalidOperationException(string.Format("Cannot map type '{0}' to type {1}", variableType, typeof(T)));
}
// #1: Get the generic Map method of the EntityTypeConfiguration<T>
MethodInfo genericMapMethod = GetGenericEntityTypeConfigurationMapMethod<T>(variableType);
// #2: Get generic type of RequiredMappingActionFactory
var requiredMappingFactoryType = typeof(RequiredMappingActionFactory<>).MakeGenericType(variableType);
// #3 get the action from generic mapping factory
var action = requiredMappingFactoryType.GetProperty("RequiredMappingAction").GetValue(null, null);
entityTypeConfigurarion =
genericMapMethod.Invoke(
entityTypeConfigurarion,
BindingFlags.Public | BindingFlags.Instance,
null,
new [] { /* and the */ action /* goes here */ },
null) as EntityTypeConfiguration<T>;
}
if (entityTypeConfigurarion == null)
{
throw new CompositionException("Something went terrible wrong!");
}
entityTypeConfigurarion.ToTable(table);
}
private static MethodInfo GetGenericEntityTypeConfigurationMapMethod<T>(Type variableType) where T : class
{
var mapMethod =
typeof(EntityTypeConfiguration<T>).GetMethods().Where(
mi => mi.Name == "Map" && mi.IsGenericMethodDefinition).FirstOrDefault();
return mapMethod.MakeGenericMethod(variableType);
}
а вот RequiredMappingActionFactory
internal static class RequiredMappingActionFactory<T> where T : class
{
public static string DiscriminatorColumn = "Discriminator";
public static Action<EntityMappingConfiguration<T>> RequiredMappingAction { get { return RequiredAction; } }
public static void RequiredAction(EntityMappingConfiguration<T> configuration)
{
configuration.Requires(DiscriminatorColumn).HasValue(typeof(T).Name);
}
}
надеюсь, кто-то может мне помочь, с наилучшими пожеланиями,
Черио, Крис
1 ответ
Entity Framework 4.1 RC выпущен! Я попробовал, и мне это удалось:-)
http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-release-candidate-available.aspx
Смотрите здесь, пользовательскую функцию Mapping, которая позволяет динамически добавлять сопоставления TPH.
protected void MapEntity(
DbModelBuilder modelBuilder, Type entityType, string toTable, string discriminatorColumn = "Discriminator")
{
var method =
GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(
mi => mi.Name.StartsWith("MapEntity") && mi.IsGenericMethodDefinition).FirstOrDefault();
var genericMethod = method.MakeGenericMethod(entityType);
genericMethod.Invoke(this, new object[] { modelBuilder, toTable, discriminatorColumn });
}
protected void MapEntity<T>(
DbModelBuilder modelBuilder, string toTable, string discriminatorColumn = "Discriminator")
where T : class, IEntity
{
var config = modelBuilder.Entity<T>().Map(
entity =>
{
entity.MapInheritedProperties();
entity.Requires(discriminatorColumn).HasValue(typeof(T).FullName).IsOptional();
});
config.ToTable(toTable);
}
и пример использования:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
MapEntity<Variable>(modelBuilder, toTable: "Variables");
MapEntity<Variable2>(modelBuilder, toTable: "Variables");
foreach (var entityType in ModelExtensions.SelectMany(modelExtension => modelExtension.IntroduceModelEntities()))
{
MapEntity(modelBuilder, entityType, toTable: "Variables");
}
}
ура, Крис