Disons que j'ai les entités suivantes:
public class Parent
{
public int Id { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
}
Quelle est la première syntaxe API fluide du code pour imposer la création de ParentId dans la base de données avec une contrainte de clé étrangère sur la table Parents, sans avoir besoin d'une propriété de navigation ?
Je sais que si j'ajoute une propriété de navigation Parent to Child, je peux le faire:
modelBuilder.Entity<Child>()
.HasRequired<Parent>(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
Mais je ne veux pas de la propriété de navigation dans ce cas particulier.
entity-framework
ef-code-first
RationalGeek
la source
la source
Réponses:
Avec EF Code First Fluent API, c'est impossible. Vous avez toujours besoin d'au moins une propriété de navigation pour créer une contrainte de clé étrangère dans la base de données.
Si vous utilisez Code First Migrations, vous avez la possibilité d'ajouter une nouvelle migration basée sur le code sur la console du gestionnaire de packages (
add-migration SomeNewSchemaName
). Si vous avez modifié quelque chose avec votre modèle ou votre mappage, une nouvelle migration sera ajoutée. Si vous n'avez rien changé, forcez une nouvelle migration en utilisantadd-migration -IgnoreChanges SomeNewSchemaName
. La migration ne contiendra que des méthodesUp
et des videsDown
dans ce cas.Ensuite, vous pouvez modifier la
Up
méthode en y ajoutant le suivant:public override void Up() { // other stuff... AddForeignKey("ChildTableName", "ParentId", "ParentTableName", "Id", cascadeDelete: true); // or false CreateIndex("ChildTableName", "ParentId"); // if you want an index }
L'exécution de cette migration (
update-database
sur la console de gestion des packages) exécutera une instruction SQL similaire à celle-ci (pour SQL Server):ALTER TABLE [ChildTableName] ADD CONSTRAINT [FK_SomeName] FOREIGN KEY ([ParentId]) REFERENCES [ParentTableName] ([Id]) CREATE INDEX [IX_SomeName] ON [ChildTableName] ([ParentId])
Sinon, sans migrations, vous pouvez simplement exécuter une commande SQL pure en utilisant
context.Database.ExecuteSqlCommand(sql);
où
context
est une instance de votre classe de contexte dérivée etsql
est simplement la commande SQL ci-dessus sous forme de chaîne.Sachez qu'avec tout cela EF n'a aucun indice qui
ParentId
est une clé étrangère qui décrit une relation. EF ne le considérera que comme une propriété scalaire ordinaire. D'une manière ou d'une autre, tout ce qui précède n'est qu'un moyen plus compliqué et plus lent que d'ouvrir simplement un outil de gestion SQL et d'ajouter la contrainte à la main.la source
[ForeignKey("ParentTableName")]
. Cela liera la propriété à la clé de la table parent. Maintenant, vous avez un nom de table codé en dur.Bien que cet article soit pour
Entity Framework
nonEntity Framework Core
, il pourrait être utile pour quelqu'un qui veut réaliser la même chose en utilisant Entity Framework Core (j'utilise V1.1.2).Je ne ai pas besoin des propriétés de navigation (bien qu'ils soient bien) parce que je pratique DDD et je veux
Parent
etChild
d'être deux racines globales distinctes. Je veux qu'ils puissent se parler via une clé étrangère et non via desEntity Framework
propriétés de navigation spécifiques à l'infrastructure .Tout ce que vous avez à faire est de configurer la relation d'un côté en utilisant
HasOne
etWithMany
sans spécifier les propriétés de navigation (elles ne sont pas là après tout).public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {} protected override void OnModelCreating(ModelBuilder builder) { ...... builder.Entity<Parent>(b => { b.HasKey(p => p.Id); b.ToTable("Parent"); }); builder.Entity<Child>(b => { b.HasKey(c => c.Id); b.Property(c => c.ParentId).IsRequired(); // Without referencing navigation properties (they're not there anyway) b.HasOne<Parent>() // <--- .WithMany() // <--- .HasForeignKey(c => c.ParentId); // Just for comparison, with navigation properties defined, // (let's say you call it Parent in the Child class and Children // collection in Parent class), you might have to configure them // like: // b.HasOne(c => c.Parent) // .WithMany(p => p.Children) // .HasForeignKey(c => c.ParentId); b.ToTable("Child"); }); ...... } }
Je donne également des exemples sur la façon de configurer les propriétés des entités, mais le plus important ici est
HasOne<>
,WithMany()
etHasForeignKey()
.J'espère que cela aide.
la source
HasOne<Parent>()
and.WithMany()
on thie. Ils ne font pas du tout référence aux propriétés de navigation, car aucune propriété de navigation n'est définie de toute façon. Je vais essayer de le rendre plus clair avec mes mises à jour.Petit conseil pour ceux qui veulent utiliser DataAnotations et ne veulent pas exposer la propriété de navigation - utilisez
protected
public class Parent { public int Id { get; set; } } public class Child { public int Id { get; set; } public int ParentId { get; set; } protected virtual Parent Parent { get; set; } }
C'est tout - la clé étrangère avec
cascade:true
aprèsAdd-Migration
sera créée.la source
Parent
ou simplementParentId
?virtual
propriétés @MarcWittke ne peuvent pas êtreprivate
.Dans le cas d' EF Core, vous n'avez pas nécessairement besoin de fournir une propriété de navigation. Vous pouvez simplement fournir une clé étrangère d'un côté de la relation. Un exemple simple avec l'API Fluent:
using Microsoft.EntityFrameworkCore; using System.Collections.Generic; namespace EFModeling.Configuring.FluentAPI.Samples.Relationships.NoNavigation { class MyContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Post>() .HasOne<Blog>() .WithMany() .HasForeignKey(p => p.BlogId); } } public class Blog { public int BlogId { get; set; } public string Url { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } } }
la source
J'utilise .Net Core 3.1, EntityFramework 3.1.3. J'ai cherché et la solution que j'ai trouvée utilisait la version générique de
HasForeginKey<DependantEntityType>(e => e.ForeginKeyProperty)
. vous pouvez créer une relation un à un comme ceci:builder.entity<Parent>() .HasOne<Child>() .WithOne<>() .HasForeginKey<Child>(c => c.ParentId); builder.entity<Child>() .Property(c => c.ParentId).IsRequired();
J'espère que cela aide ou du moins fournit d'autres idées sur la façon d'utiliser la
HasForeginKey
méthode.la source
Ma raison de ne pas utiliser les propriétés de navigation est les dépendances de classe. J'ai séparé mes modèles en quelques assemblages, qui peuvent être utilisés ou non dans différents projets dans toutes les combinaisons. Donc, si j'ai une entité qui a une propriété de nagivation à la classe d'un autre assemblage, je dois faire référence à cet assemblage, ce que je veux éviter (ou tout projet qui utilise une partie de ce modèle de données complet transportera tout avec lui).
Et j'ai une application de migration distincte, qui est utilisée pour les migrations (j'utilise des automatisations) et la création de base de données initiale. Ce projet fait référence à tout par des raisons évidentes.
La solution est de style C:
alt
clé dans VS)#if _MIGRATION
Contact
classe dans l'exemple).Échantillon:
public int? ContactId { get; set; } #if _MIGRATION [ForeignKey(nameof(ContactId))] public Contact Contact { get; set; } #endif
Bien sûr, vous devez de la même manière désactiver la
using
directive et modifier l'espace de noms.Après cela, tous les consommateurs peuvent utiliser cette propriété comme un champ DB habituel (et ne pas référencer des assemblys supplémentaires s'ils ne sont pas nécessaires), mais le serveur DB saura qu'il s'agit de FK et peut utiliser la mise en cascade. Solution très sale. Mais ça marche.
la source