1

より良い見出しが見つからないことをお詫び申し上げます。MEFモジュールを介してEF4CTP5モデルを拡張しようとしています。アイデアは、いくつかの基本的なエンティティを指定することです。これらの基本エンティティは、ソリューションモデルアセンブリのContextクラスの隣にあります。

たとえば、Variableというエンティティがあります。変数は非常に一般的なエンティティであり、アプリケーションの一部のモジュールで特別な変数エンティティを指定して、より詳細なプロパティを提供する必要がありますが、それらは同じテーブル(TPH-階層ごとのテーブル)に格納する必要があります。

そのために、インターフェイスIModelContextExtensionを指定しました

public interface IModelContextExtension
{
    void OnModelCreating(IModelBuilderFacade modelBuilder);
}

カスタム変数を使用する各モジュールは、このインターフェイスを実装するクラスをエクスポートする必要があります。モデルのOnModelCreatingメソッドで、登録されている各モジュールをループし、そのモジュールのOnModelCreatingメソッドを呼び出します。次に、提供されたIModelBuilderFacadeで「RegisterVariableType」などを呼び出して、Variable-Derived-Type(MySpecialVariable2など)をアナウンスできます。

**興味深い部分:** Variable-Derived-Typeが別の(MEF-Loaded)アセンブリにある場合を除いて、RegisterVariableTypeメソッドは非常にうまく機能しているようです。別のモジュールから変数を登録すると、完全なマッピングが破損しているように見えます。なぜなら、リポジトリに変数を追加しようとすると、追加中に「シーケンスに要素が含まれていません」と表示されてクラッシュするからです。ロードされたモジュールからタイプを削除すると、期待どおりに機能します。

誰かが興味を持っているなら、私はIRepositoryのものを投稿しますが、それは問題ではないと確信しています。

ここに、私のコンテキスト(DbContextから派生)クラスのOnModelCreatingメソッドがあります。

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関数は、modelBuilderFacadeに格納されているIEnumerableに(Variable派生)型を追加します。

タイプを追加した後、自分のMap関数を呼び出してTPHマッピングを実行します。

[Export(typeof(IModelContextExtension))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class Context : IModelContextExtension
{
    public void OnModelCreating(IModelBuilderFacade modelBuilder)
    {
        modelBuilder.RegisterVariableType(typeof(MyVerySpecialVariable2));
    }
}

ここでMap関数:

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

うまくいけば、誰かが私を助けてくれるといいのですが、よろしくお願いします、

ケリオ、クリス

4

1 に答える 1

0

Entity Framework 4.1 RC がリリースされました! 私はそれを試してみましたが、成功しました:-)

http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-release-candidate-available.aspx

ここで、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");
    }
}

乾杯、クリス

于 2011-03-24T13:23:19.880 に答える