Mappage des clés composites en utilisant d'abord le code EF

115

Table du serveur SQL:

SomeId PK varchar(50) not null 
OtherId PK int not null

Comment dois-je mapper cela dans le code EF 6 en premier?

public class MyTable
{
    [Key]
    public string SomeId { get; set; }

    [Key]
    public int OtherId { get; set; }
}

J'ai vu des exemples où vous devez définir l'ordre de chaque colonne, est-ce nécessaire?

Y a-t-il une documentation officielle à ce sujet quelque part?

loyalflow
la source
Est-ce SomeIdun stringou un int?
Corey Adler
@ IronMan84 C'est une chaîne, je vais corriger ça.
loyalflow

Réponses:

187

Vous devez absolument mettre dans l'ordre des colonnes, sinon comment SQL Server est-il censé savoir lequel est le premier? Voici ce que vous devez faire dans votre code:

public class MyTable
{
  [Key, Column(Order = 0)]
  public string SomeId { get; set; }

  [Key, Column(Order = 1)]
  public int OtherId { get; set; }
}

Vous pouvez également consulter cette question SO . Si vous voulez une documentation officielle, je vous recommande de consulter le site Web officiel d'EF . J'espère que cela t'aides.

EDIT: Je viens de trouver un article de blog de Julie Lerman avec des liens vers toutes sortes de bienfaits EF 6. Vous pouvez trouver tout ce dont vous avez besoin ici .

Corey Adler
la source
Comment faites-vous cela via une EntityConfiguration? Je n'ai pas réellement d'entité pour la table de jointure ... J'ai juste les deux entités et une EntityConfiguration sur l'une d'entre elles avec un .Map () pour configurer le mappage.
Mir
31
otherwise how is SQL Server supposed to know which one goes first?- pourquoi pas de la même manière qu'il connaît l'ordre de toutes les autres colonnes?
Davor
1
EF ne connaît pas l'ordre des autres colonnes, vous pouvez insérer des colonnes dans n'importe quel ordre tant que les noms sont spécifiés. Si EF requiert la commande pour le PK composite, il doit être lié à l'indexation.
Sylvain Gantois
@Davor J'imagine que les créateurs d'EF auraient pu utiliser la réflexion pour déduire l'ordre des clés / colonnes, mais peut-être y a-t-il des considérations de performances pour ne pas le faire. Je prendrai la spécificité au moment de la conception sur des performances plus lentes chaque jour, en particulier dans mon DAL.
Jacob Stamm
47

Pour le mappage de la clé primaire composite à l'aide du framework Entity, nous pouvons utiliser deux approches.

1) En remplaçant la méthode OnModelCreating ()

Par exemple: j'ai la classe de modèle nommée VehicleFeature comme indiqué ci-dessous.

public class VehicleFeature
{
    public int VehicleId { get; set; }
    public int FeatureId{get;set;}
    public Vehicle Vehicle{get;set;}
    public Feature Feature{get;set;}
}

Le code dans mon DBContext serait comme,

public class VegaDbContext : DbContext
{
    public DbSet<Make> Makes{get;set;}

    public DbSet<Feature> Features{get;set;}
    public VegaDbContext(DbContextOptions<VegaDbContext> options):base(options)        
    {           

    }
    // we override the OnModelCreating method here.
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<VehicleFeature>().HasKey(vf=> new {vf.VehicleId, vf.FeatureId});
    }
}

2) Par annotations de données.

public class VehicleFeature
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]  
    [Key]
    public int VehicleId { get; set; }
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]   
    [Key]
    public int FeatureId{get;set;}
    public Vehicle Vehicle{get;set;}
    public Feature Feature{get;set;}
}

Veuillez consulter les liens ci-dessous pour plus d'informations.

1) https://msdn.microsoft.com/en-us/library/jj591617(v=vs.113).aspx

2) Comment ajouter une clé unique composite à l'aide d'EF 6 Fluent Api?

Krishna
la source
5
Pour info pour EF Core, l'option n ° 2 n'est pas possible , «Les clés composites ne peuvent être configurées qu'à l'aide de l'API Fluent - les conventions ne configureront jamais une clé composite et vous ne pouvez pas utiliser les annotations de données pour en configurer une».
Tobias J
7

Grâce à la configuration, vous pouvez le faire:

Model1
{
    int fk_one,
    int fk_two
}

Model2
{
    int pk_one,
    int pk_two,
}

puis dans la configuration du contexte

public class MyContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Model1>()
            .HasRequired(e => e.Model2)
            .WithMany(e => e.Model1s)
            .HasForeignKey(e => new { e.fk_one, e.fk_two })
            .WillCascadeOnDelete(false);
    }
}
philn5d
la source
Où dans la configuration du contexte?
Charlie
Si vous configurez le contexte via du code à l'aide de l'API Fluent ... section 7 . public class MyContext: DbContext {protected override void OnModelCreating (DbModelBuilder modelBuilder) {modelBuilder.Entity <Model1> () .HasRequired (e => e.Model2) .WithMany (e => e.Model1s) .HasForeign = newK {e.fk_one, e.fk_two}) .WillCascadeOnDelete (false); }}
philn5d
J'ai trouvé que je devais utiliser ModelBuilder au lieu de DbModelBuilder sur dotnet core.
kiml42
6

J'ai pensé que j'ajouterais à cette question car c'est le meilleur résultat de recherche Google.

Comme cela a été noté dans les commentaires, dans EF Core, il n'y a pas de support pour l'utilisation des annotations (attribut Key) et cela doit être fait avec fluent.

Comme je travaillais sur une grande migration d'EF6 vers EF Core, ce n'était pas recommandable et j'ai donc essayé de le pirater en utilisant Reflection pour rechercher l'attribut Key, puis l'appliquer pendant OnModelCreating

// get all composite keys (entity decorated by more than 1 [Key] attribute
foreach (var entity in modelBuilder.Model.GetEntityTypes()
    .Where(t => 
        t.ClrType.GetProperties()
            .Count(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(KeyAttribute))) > 1))
{
    // get the keys in the appropriate order
    var orderedKeys = entity.ClrType
        .GetProperties()
        .Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(KeyAttribute)))
        .OrderBy(p => 
            p.CustomAttributes.Single(x => x.AttributeType == typeof(ColumnAttribute))?
                .NamedArguments?.Single(y => y.MemberName == nameof(ColumnAttribute.Order))
                .TypedValue.Value ?? 0)
        .Select(x => x.Name)
        .ToArray();

    // apply the keys to the model builder
    modelBuilder.Entity(entity.ClrType).HasKey(orderedKeys);
}

Je n'ai pas complètement testé cela dans toutes les situations, mais cela fonctionne dans mes tests de base. J'espère que cela aide quelqu'un

cmorgan091
la source