9

Sto utilizzando Entity Framework 4.3 con il codice prima e le migrazioni manuali. Sto tentando di mappare una configurazione TPH (tabella per gerarchia) che utilizza due campi discriminatori personalizzati. Uno per il discriminatore stesso e l'altro per le eliminazioni software (molto simile all'opzione "dove" nei mapping di classe NH). La stessa identica configurazione funziona bene in un altro progetto che gira su EF 4.2.Entity Framework 4.3 - Errore di mappatura e migrazione TPH

Viene visualizzato l'errore quando si tenta di aggiungere una migrazione utilizzando il comando "aggiungi-migrazione" nella console NuGet. Ho provato tutte le combinazioni di definire il nome della tabella - attributi sulla classe, nel metodo "OnModelCreating", nelle classi EntityTypeConfiguration ecc. Le mie migrazioni precedenti che non coinvolgono i complessi mapping delle gerarchie hanno funzionato bene.

C'è qualcosa di diverso in EF 4.3 su cui sono incappato?

Il codice:

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

L'errore:

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

risposta

12

Questo è un problema noto con 4.3 e 4.3.1. (Abbiamo trovato troppo tardi per mettere la correzione in 4.3.1.) Per fortuna c'è un modo abbastanza semplice per cambiare il codice che dovrebbe farlo funzionare.

In breve, eravate in grado di effettuare chiamate di mappe concatenate su una singola EntityConfiguration in 4.1. e 4.2. Qualcosa di simile a questo schema:

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

Questo non funziona in 4.3 e invece si deve fare ogni chiamata mappa su un EntityConfiguration per tale entità. Quindi un qualcosa di modello come questo:

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

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

Prendendo il vostro caso specifico, questo dovrebbe funzionare:

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

(ho rimosso alcuni dei parametri generici in quanto non sono necessari, ma questo è . non è importante)

fare questo utilizzando EntityConfigurations espliciti si può usare qualcosa di simile:

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

E poi

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

Abbiamo in programma di risolvere questo problema in 5.0.

+0

Grazie, questo fa proprio quello che volevo. E in un modo strano sembra giusto - più esplicito - una configurazione per ogni sottotipo. Anche se la compatibilità con le versioni precedenti sarebbe buona e spero che ciò venga risolto nelle versioni future. – Mihkel

+2

Questo è in ritardo di 3 anni, ma chiama 'Map (m => m.Requires ...' funzionerà solo se è fatto ** BEFORE ** chiama a 'ToTable (" nome tabella ")'. Entity Framework 6.1.3 btw. –

Problemi correlati