0

Je recherche depuis des heures et je n'arrive pas à trouver le problème.Entity Framework ajoute une colonne redondante discriminante en double au tableau

Je construis une Entity Framework Fluent Code Api Première application TPH. Lorsque l'Add-Migration EF ajoute ma colonne "Type", elle ajoute également une colonne Discriminator redondante (elle doit être remplacée par "Type"). J'utilise Map pour spécifier le nom de la colonne Type et les valeurs possibles, cette approche semble fonctionner correctement pour la plupart des modèles de domaine, mais celle-ci reçoit une seconde colonne discriminante redondante et je n'arrive pas à trouver la raison. Bond hérite de Asset dans le modèle de domaine.

Heres mon code:

public class BondConfiguration : EntityTypeConfiguration<Bond> 
{ 
    public BondConfiguration() 
    { 
     Property(b => b.IssueDate) 
      .HasColumnName("BondIssueDate") 
      .HasColumnType(DatabaseVendorTypes.TimestampField) 
      .IsRequired(); 

     Property(b => b.MaturityDate) 
      .HasColumnName("BondMaturityDate") 
      .HasColumnType(DatabaseVendorTypes.TimestampField) 
      .IsRequired(); 

     HasRequired(b => b.Currency).WithRequiredDependent(); 

     Property(b => b.Coupon.Rate); 

     Property(b => b.Coupon.Amount); 

     Property(b => b.FaceValue) 
      .HasColumnName("BondFaceValue") 
      .IsRequired(); 
    } 
} 

public class AssetConfiguration : EntityTypeConfiguration<Asset> 
{ 
    public AssetConfiguration() 
    { 
     Property(a => a.IsDeleted).HasColumnName("IsDeleted"); 

     HasKey(a => a.Id); 

     ToTable("tbl_Asset"); 

     Property(a => a.Id) 
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) 
      .HasColumnName("AssetId"); 

     Property(a => a.Name) 
      .HasColumnName("AssetName") 
      .IsRequired(); 

     Property(a => a.Isin) 
      .HasColumnName("AssetISIN"); 

     Map<Bond>(p => p.Requires("AssetClass").HasValue((int)AssetClass.Bond)); 
    } 
} 

Modèle Domaine:

public class Bond : Asset 
{ 
    public DateTime IssueDate { get; set; } 

    public DateTime MaturityDate { get; set; } 

    public BondCoupon Coupon { get; set; } 

    public Currency Currency { get; set; } 

    public decimal FaceValue { get; set; } 

    public IEnumerable<ValidationRule> SetCoupon(decimal amount, decimal rate) 
    { 
     var newCoupon = new BondCoupon 
     { 
      Rate = rate, 
      Amount = amount 
     }; 

     if (Validate(new SetBondCouponValidator(newCoupon),out IEnumerable<ValidationRule> brokenRules)) 
     { 
      Coupon = new BondCoupon 
      { 
       Rate = rate, 
       Amount = amount 
      }; 
     } 
     return brokenRules; 
    } 
} 

public abstract class BaseAsset<T> : BaseEntity<T> where T : BaseEntity<T>, new() 
{ 
    public string Name { get; set; } 

    public string Isin { get; set; } 
} 

public class Asset : BaseAsset<Asset>, IEntityRoot 
{ 
} 

public class BaseEntity<T> where T : BaseEntity<T>, new() 
{ 
    public int Id { get; set; } 

    public bool IsDeleted { get; set; } 

    public bool Validate(IValidator validator, out IEnumerable<ValidationRule> brokenRules) 
    { 
     brokenRules = validator.GetBrokenRules(); 
     return validator.IsValid(); 
    } 
} 
+0

Pourriez-vous ajouter les classes 'Asset' et' Bond' afin que nous puissions reproduire le problème? –

+0

Je viens d'ajouter le modèle de domaine. Merci d'avoir regarder ceci. –

+0

Impossible de reproduire avec le modèle fourni. Avez-vous une autre classe dans le même projet (assembly) qui hérite de 'Asset' ou' Bond'? –

Répondre

3

Vous devez être très prudent lorsque vous utilisez un de l'héritage EF6. EF utilise la réflexion pour découvrir toutes les classes du même assemblage qui héritent directement ou indirectement de certaines des entités faisant partie de l'héritage EF et les considère comme faisant partie de la hiérarchie des entités, même si elles ne sont utilisées/référencées/configurées nulle part Modèle EF.

Alors que rajouter une classe (dans votre cas réel, il est appelé Equity)

public Asset2 : Asset { } 

suffit d'introduire la colonne Discriminator standard, car il est pas configuré pour utiliser la configuration de la colonne discriminante pour la classe Bond.

Ce comportement est la source d'erreurs inattendues comme la vôtre et a été modifié dans EF Core où seules les classes dérivées explicitement configurées sont prises en compte.

Dans EF6, vous pouvez marquer ces classes avec l'attribut NotMapped, utiliser Ignore API fluide ou les mapper correctement en tant qu'entité.

+1

Ok donc c'est un bug dans EF6 qui a été corrigé dans Core? Merci, j'ai ajusté le modèle et inclus la configuration d'équité, après j'ai régénéré la migration. La colonne discriminante est supprimée dans la migration. Problème résolu. –

1

est ici un non-repro complète. Assurez-vous que vos EntityTypeConfigurations sont câblées dans OnModelCreating et qu'elles s'exécutent réellement lorsque le modèle est initialisé. Ne nommez pas non plus les tables avec un préfixe "tbl_".

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations.Schema; 
using System.Data.Entity; 
using System.Data.Entity.ModelConfiguration; 
using System.Data.SqlClient; 
using System.Linq; 

namespace ConsoleApp8 
{ 

    public class Bond : Asset 
    { 
     public DateTime IssueDate { get; set; } 

     public DateTime MaturityDate { get; set; } 

     //public BondCoupon Coupon { get; set; } 

     //public Currency Currency { get; set; } 

     public decimal FaceValue { get; set; } 


    } 

    public abstract class BaseAsset<T> : BaseEntity<T> where T : new() 
    { 
     public string Name { get; set; } 

     public string Isin { get; set; } 
    } 

    public class Asset : BaseAsset<Asset> 
    { 
    } 

    public class BaseEntity<T> where T : new() 
    { 
     public int Id { get; set; } 

     public bool IsDeleted { get; set; } 


    } 
    public class BondConfiguration : EntityTypeConfiguration<Bond> 
    { 
     public BondConfiguration() 
     { 

      Property(b => b.FaceValue) 
       .HasColumnName("BondFaceValue") 
       .IsRequired(); 
     } 
    } 
    public enum AssetClass 
    { 
     Bond = 1 
    } 
    public class AssetConfiguration : EntityTypeConfiguration<Asset> 
    { 
     public AssetConfiguration() 
     { 
      Property(a => a.IsDeleted).HasColumnName("IsDeleted"); 

      HasKey(a => a.Id); 

      ToTable("Asset"); 

      Property(a => a.Id) 
       .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) 
       .HasColumnName("AssetId"); 

      Property(a => a.Name) 
       .HasColumnName("AssetName") 
       .IsRequired(); 

      Property(a => a.Isin) 
       .HasColumnName("AssetISIN"); 

      Map<Bond>(p => p.Requires("AssetClass").HasValue((int)AssetClass.Bond)); 
     } 
    } 

    class Db : DbContext 
    { 
     public DbSet<Bond> Bonds { get; set; } 
     public DbSet<Asset> Assets { get; set; } 

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



    class Program 
    {  

     static void Main(string[] args) 
     { 

      Database.SetInitializer(new DropCreateDatabaseAlways<Db>()); 

      using (var db = new Db()) 
      { 
       db.Database.Log = m => Console.WriteLine(m); 

       db.Database.Initialize(true); 






      } 


      Console.WriteLine("Hit any key to exit"); 
      Console.ReadKey(); 


     } 
    } 
} 

sorties (en partie):

CREATE TABLE [dbo].[Asset] (
    [AssetId] [int] NOT NULL IDENTITY, 
    [AssetName] [nvarchar](max) NOT NULL, 
    [AssetISIN] [nvarchar](max), 
    [IsDeleted] [bit] NOT NULL, 
    [IssueDate] [datetime], 
    [MaturityDate] [datetime], 
    [BondFaceValue] [decimal](18, 2), 
    [AssetClass] [int], 
    CONSTRAINT [PK_dbo.Asset] PRIMARY KEY ([AssetId]) 
)