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 Réponses :
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.
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()"); } }
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] code > 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 :)
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.
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
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 .
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 estmodelBuilder.Entity ()
qui marqueBaseEntity
comme entité de base TPH.