5
votes

Configuration de la classe de base abstraite sans créer de table dans EF Core

J'ai ajouté les champs DateCreated , UserCreated , DateModified et UserModified à chaque entité en créant un Classe BaseEntity.cs . Mon exigence est de générer une table unique contenant tous les champs de la classe base comme colonnes.

Add-Migration -Name "InitialMigration" -Context "SchoolContext"
update-database

Voici ma classe Student

public class SchoolContext: DbContext
{       
    public LibraContext(DbContextOptions<SchoolContext> options)
        : base(options)
    { }

    public DbSet<Student> Students { get; set; }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
        //CreatedDate 
        modelBuilder.Entity<BaseEntity>().Property(x => x.DateCreated).HasDefaultValueSql("GETDATE()");
        //Updated Date
        modelBuilder.Entity<BaseEntity>().Property(x => x.DateModified).HasDefaultValueSql("GETDATE()");
 }

Voici ma classe de contexte

public class Student : BaseEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

J'ai exécuté les commandes de migration ci-dessous pour créer un script et mettre à jour la base de données p >

public class BaseEntity
{
     public DateTime? DateCreated { get; set; }
     public string UserCreated { get; set; }
     public DateTime? DateModified { get; set; }
     public string UserModified { get; set; }
}

Il a créé des tables distinctes BaseEntity et Student dans la base de données SQL Server. Je m'attends à créer une seule table Student avec tous les champs BaseEntity sous forme de colonnes.

Comment y parvenir?

J'utilise ASP.NET CORE2.1


3 commentaires

Vous voulez une table unique qui a tous les champs de la classe de base comme colonnes . Pourtant Mon attente est de créer une seule table Student avec tous les champs BaseEntity sous forme de colonnes . Pour moi, c'est contradictoire. Quant à ce dernier, vous aimerez peut-être cette approche.


regardez lien


Voir la réponse à la question marquée comme dupliquée. Le coupable est que vous ne devez pas faire référence directement à BaseEntity dans les propriétés de navigation du modèle ou dans l'API fluent. Le problème avec le code concret dans l'article est modelBuilder.Entity () qui marque BaseEntity comme entité de base TPH.


3 Réponses :


0
votes

Le modèle TPC se trouve actuellement dans le backlog , ce qui signifie qu'il est en cours considéré comme une fonctionnalité, mais aucune date n'a encore été fixée.

en savoir plus.


0 commentaires

4
votes

Selon la documentation Microsoft , vous pouvez utiliser [NotMapped] annotation de données ou modelBuilder.Ignore (); pour ignorer la création de table pour BaseEntity comme suit:

Mais dans votre cas, [NotMapped] ne vous aiderait pas car Fluent API a toujours une priorité plus élevée que les annotations de données (attributs). Vous pouvez donc appeler modelBuilder.Ignore (); après la configuration de BaseEntity dans OnModelCreating mais en appelant modelBuilder.Ignore (); perdra les configurations BaseEntity .

Donc, selon moi, la meilleure solution serait la suivante:

Écrivez la configuration pour BaseEntity comme suit:

migrationBuilder.CreateTable(
            name: "Students",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                DateCreated = table.Column<DateTime>(nullable: true, defaultValueSql: "GETDATE()"),
                UserCreated = table.Column<string>(nullable: true),
                DateModified = table.Column<DateTime>(nullable: true, defaultValueSql: "GETDATE()"),
                UserModified = table.Column<string>(nullable: true),
                Name = table.Column<string>(nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Students", x => x.Id);
            });

Puis écrivez la configuration pour Student comme suit: p >

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
     base.OnModelCreating(modelBuilder);

     modelBuilder.ApplyConfiguration(new StudentConfigurations());
}

Puis dans OnModelCreating comme suit:

public class StudentConfigurations : BaseEntityConfigurations<Student>
{
    public override void Configure(EntityTypeBuilder<Student> builder)
    {
        base.Configure(builder); // Must call this

       // other configurations here
    }
}

La migration générera le seul Student avec la configuration de BaseEntity comme suit:

public class BaseEntityConfigurations<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : BaseEntity
{
    public virtual void Configure(EntityTypeBuilder<TEntity> builder)
    {
        //CreatedDate 
        builder.Property(x => x.DateCreated).HasDefaultValueSql("GETDATE()");
        //Updated Date
        builder.Property(x => x.DateModified).HasDefaultValueSql("GETDATE()");
    }
}


11 commentaires

sonne bien, en attendant, j'ai également trouvé un moyen différent de le résoudre. s'il vous plaît voir ma réponse et l'examiner.


L'API Fluent ( modelBuilder.Entity () dans ce cas) a une priorité plus élevée que les annotations de données (attributs). Ce n'est certainement pas une solution car l'attribut entre en conflit avec la configuration fluide.


@IvanStoev Je n'ai pas compris! Expliquez-moi davantage. Attendez-vous d'appeler modelBuilder.Ignore (); ?


@IvanStoev Plus important encore, j'ai testé ce code de mon côté et il n'est pas en conflit. Il génère tout gracieusement comme prévu!


Le problème était dû à l'utilisation de BaseEntity comme argument de type générique dans l'appel modelBuilder.Entity () et [NotMapped] ou Ignorer n'aidera pas. Je viens de tester votre code (juste au cas où il y aurait une certaine "intelligence" EF Core que je ne connais pas), et j'obtiens l'erreur "Le type d'entité" BaseEntity "nécessite la définition d'une clé primaire."


@IvanStoev J'ai compris votre point et j'en étais conscient lors de la rédaction de la réponse ici. J'ai d'abord testé avec le code de réponse et cela a fonctionné, puis je l'ai publié ici. Regardez, j'ai également partagé le code de migration. Est-ce qu'il s'est passé quelque chose de magique dans mon cas? Laissez-moi vérifier à nouveau.


@IvanStoev j'ai vérifié! J'ai appelé modelBuilder.Ignore (); après les paramètres de configuration de l'entité de base et c'est pourquoi cela fonctionnait. Regardez, j'ai mis à jour ma réponse! Merci beaucoup d'avoir remarqué cela.


Cela a éliminé l'erreur, mais maintenant la configuration de la propriété ( DefaultValueSql etc.) est perdue (non incluse dans le code de migration généré). Donc, malheureusement, le hack ne fonctionne pas. La manière correcte de gérer un tel scénario est indiquée dans le lien dupliqué et d'autres articles similaires itérant modelBuilder.Model.GetEntityTypes () et appelant un code de configuration commun pour tous les types d'entités dérivés. La classe de base dans un tel scénario ne doit jamais être spécifiée comme entité, et ses propriétés ne peuvent pas être configurées une seule fois pour toutes les entités dérivées.


@IvanStoev Vous avez absolument raison! Merci beaucoup d'avoir passé votre temps! Je mets à jour à nouveau ma réponse!


@IvanStoev Up a voté pour votre réponse! Parce que vous le méritez vraiment! :)


J'aime ta créativité et ton enthousiasme, vraiment :)



0
votes

J'ai résolu le problème par un simple changement! Nous devons utiliser l'entité Student dans modelbuilder . J'ai été utilisé par erreur BaseEntity

  protected override void OnModelCreating(ModelBuilder modelBuilder)
  {
     modelBuilder.Entity<Student>().Property(x => x.Created).HasDefaultValueSql("GETDATE()");
     modelBuilder.Entity<Student>().Property(x => x.Updated).HasDefaultValueSql("GETDATE()");
  }

Problème ouvert: L'ordre des colonnes de table générées ne peut pas être contrôlé par le numéro de commande . Ce n'est actuellement pas pris en charge par .net core.


8 commentaires

Ce n'est pas la meilleure solution. Si vous avez une autre classe de modèle appelée Teacher qui hérite de BaseEntity , cela ne fonctionnera pas. Vous devez à nouveau écrire la configuration de la classe de modèle Enseignant . Maintenant, pensez que faire si vous avez 100 entités? vous devez écrire 100 fois. Alors suivez ma solution.


C'est vrai, je suis d'accord avec vous. Laissez-moi essayer votre solution et vous tenir au courant


D'accord! Vérifiez-le et soyez confirmé!


J'ai un petit problème. J'ai une exigence de clé composite qui est une combinaison de clé de la classe de base et de la classe dérivée. modelBuilder.Entity () .HasKey (c => new {c.Key, c.Id}); après le changement suggéré par vous, j'obtiens "Une clé ne peut pas être configurée sur 'Student' car il s'agit d'un type dérivé. La clé doit être configurée sur le type racine 'BaseEntity'. Est-ce que je fais une mauvaise conception de table?


Pensez d'abord pourquoi voulez-vous créer une clé composite avec la clé BaseEntity et la clé de classe modèle You? BaseEntity n'a pas de table dans la base de données. Donc, id sur l'étudiant, assurez-vous toujours de l'unicité.


à droite, en fait ma clé de classe de base est "guid" et il y a quelque chose d'unique que je veux dans la classe des étudiants comme "ID" qui peut être lisible. comme "STUD01", c'est juste un exemple, le vrai scénario est pour une autre table qui nécessite vraiment des données uniques lisibles. J'ai pensé à utiliser la contrainte "unique" aussi. faites-moi savoir votre point de vue.


D'accord! Vous pouvez le faire, je l'ai fait de mon côté. Composite avec la clé BaseEntity et la clé étudiant. Veuillez vérifier ma réponse en vert, puis posez une autre question avec un problème de clé composite. Je réponds à cette question.


continuons cette discussion en chat .