9

Używam Entity Framework 4.3 z migracją kodu i manualną. Próbuję odwzorować konfigurację TPH (tabela-na-hierarchię), która używa dwóch niestandardowych pól dyskryminatora. Jeden dla samego dyskryminatora, a drugi dla miękkich usunięć (podobnie jak opcja "gdzie" w odwzorowaniach klas NH). Dokładnie ta sama konfiguracja działa dobrze w innym projekcie, który działa na EF 4.2.Entity Framework 4.3 - mapowanie TPH i błąd migracji

Wystąpił błąd podczas próby dodania migracji za pomocą polecenia "dodaj-migracja" w konsoli NuGet. Próbowałem wszystkich kombinacji definiowania nazwy tabeli - atrybuty na klasie, w metodzie "OnModelCreating", w klasach EntityTypeConfiguration itd. Moje poprzednie migracje, które nie obejmowały złożonych odwzorowań hierarchii, działały dobrze.

Czy jest jakiś przełom w EF 4.3, o którym się natknąłem?

Kod:

//---- Domain classes --------------------- 

public abstract class ParentClass 
{ 
    public string ParentString { get; set; } 
} 

public class Foo : ParentClass 
{ 
    public string FooString { get; set; } 
} 

public class Bar : ParentClass 
{ 
    public string BarString { get; set; } 
} 

//---- Mapping configuration -------------- 

public class ParentConfiguration : EntityTypeConfiguration<ParentClass> 
{ 
    public ParentConfiguration() 
    { 
     Map<Foo>(m => 
     { 
      m.Requires("IsActive").HasValue(1); 
      m.Requires("Type").HasValue("Foo"); 
     }) 
     .ToTable("Parent"); 

     Map<Bar>(m => 
     { 
      m.Requires("IsActive").HasValue(1); 
      m.Requires("Type").HasValue("Bar"); 
     }) 
     .ToTable("Parent"); 
    } 
} 

//---- Context ---------------------------- 

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Configurations.Add(new ParentConfiguration()); 
} 

Błąd:

System.InvalidOperationException: The type 'Foo' has already been mapped to table 'Parent'. Specify all mapping aspects of a table in a single Map call. 
    at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.AddMappingConfiguration(EntityMappingConfiguration mappingConfiguration) 
    at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ReassignSubtypeMappings() 
    at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo) 
    at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection) 
    at System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(DbContext context, XmlWriter writer) 
    at System.Data.Entity.Migrations.Extensions.DbContextExtensions.<>c__DisplayClass1.<GetModel>b__0(XmlWriter w) 
    at System.Data.Entity.Migrations.Extensions.DbContextExtensions.GetModel(Action`1 writeXml) 
    at System.Data.Entity.Migrations.Extensions.DbContextExtensions.GetModel(DbContext context) 
    at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext) 
    at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration) 
    at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.GetMigrator() 
    at System.Data.Entity.Migrations.Design.ToolingFacade.GetPendingMigrationsRunner.RunCore() 
    at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run() 

Mihkel

Odpowiedz

12

Jest to znany problem z 4.3 i 4.3.1. (Za późno znaleźliśmy poprawkę w 4.3.1.) Na szczęście istnieje dość prosty sposób na zmianę kodu, który powinien sprawić, że zadziała.

W skrócie, w przeszłości można było wykonywać powiązane połączenia mapowe w pojedynczej konfiguracji EntityConfiguration w wersji 4.1. i 4.2. Coś jak tego wzorca:

modelBuilder.Entity<Parent>() 
    .Map<Foo>(...) 
    .Map<Bar>(...); 

To nie działa w 4.3, a zamiast tego trzeba dokonać każdego połączenia mapę w EntityConfiguration dla tego podmiotu. Więc coś takiego wzoru:

modelBuilder.Entity<Foo>() 
    .Map<Foo>(...); 

modelBuilder.Entity<Bar>() 
    .Map<Bar>(...); 

Biorąc sprawę szczegółowo, to powinno działać:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<ParentClass>() 
     .ToTable("Parent"); 

    modelBuilder.Entity<Foo>() 
     .Map(m => 
       { 
        m.Requires("IsActive").HasValue(1); 
        m.Requires("Type").HasValue("Foo"); 
       }); 

    modelBuilder.Entity<Bar>() 
     .Map(m => 
       { 
        m.Requires("IsActive").HasValue(1); 
        m.Requires("Type").HasValue("Bar"); 
       }); 
} 

(I zostały usunięte niektóre z parametrów ogólnych, ponieważ nie są one potrzebne, ale to . nie ma znaczenia)

ten sposób za pomocą wyraźnych EntityConfigurations byłoby użyć coś takiego:

public class ParentConfiguration : EntityTypeConfiguration<ParentClass> 
{ 
    public ParentConfiguration() 
    { 
     ToTable("Parent"); 
    } 
} 

public class FooConfiguration : EntityTypeConfiguration<Foo> 
{ 
    public FooConfiguration() 
    { 
     Map(m => 
     { 
      m.Requires("IsActive").HasValue(1); 
      m.Requires("Type").HasValue("Foo"); 
     }); 
    } 
} 

public class BarConfiguration : EntityTypeConfiguration<Bar> 
{ 
    public BarConfiguration() 
    { 
     Map(m => 
     { 
      m.Requires("IsActive").HasValue(1); 
      m.Requires("Type").HasValue("Bar"); 
     }); 
    } 
} 

A potem

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Configurations 
     .Add(new ParentConfiguration()) 
     .Add(new FooConfiguration()) 
     .Add(new BarConfiguration()); 
} 

Planujemy naprawić w 5,0.

+0

Dziękuję, to robi dokładnie to, co chciałem. I w dziwny sposób ma się rację - bardziej jednoznacznie - jedna konfiguracja dla każdego podtypu. Chociaż kompatybilność wsteczna byłaby miła i mam nadzieję, że zostanie to naprawione w przyszłych wersjach. – Mihkel

+2

To jest 3 lata późno, ale wywołania do 'Map (m => m.Requires ...' będą działać tylko wtedy, gdy są wykonywane ** ZANIM ** wywołań do 'ToTable (" nazwa tabeli ")'. Jestem na Entity Framework 6.1.3 btw. –

Powiązane problemy