Voici mon modèle:
public class TMUrl
{
//many other properties
//only property with type Keyword
public List<Keyword> Keywords{get;set;}
}
public class Keyword
{
//many other properties
//only property with type TMUrl
public List<TMUrl> Urls{get;set;}
}
Il est donc clair que les deux entités ont une relation plusieurs à plusieurs. J'ai choisi l'api couramment pour parler à l'entité-cadre de cette relation, c'est-à-dire.
modelBuilder.Entity<TMUrl>
.HasMany(s => s.Keywords)
.WithMany(s => s.URLs).Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
mais quand je fais
url.Keywords.Add(dbKey); //where url is object of TMUrl,
//dbKey is an existing/new object of Keyword
db.SaveChanges();
Je reçois une exception
An error occurred while saving entities that do not expose foreign key
properties for their relationships....
InnerException:
The INSERT statement conflicted with the FOREIGN KEY constraint
"KeywordMaster_Keyword". The conflict occurred in database "DbName",
table "dbo.KeywordMaster", column 'Id'.The statement has been terminated.
mais quand j'ajoute également la configuration de l'autre côté, tout fonctionne bien. c'est à dire.
modelBuilder.Entity<KeyWord>
.HasMany(s => s.URLs)
.WithMany(s => s.Keywords)
.Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
Pourquoi?. Pourquoi je dois ajouter la configuration des deux entités, où j'ai lu ici et de nombreux autres endroits, la configuration pour l'une des entités devrait faire.
Quel est le cas, quand je devrais ajouter une configuration pour les deux entités impliquées dans la relation?
J'ai besoin de comprendre ça. Pourquoi. Veuillez aider.
Les termes Left
et Right
dans MapLeftKey
et MapRightKey
dans le mappage plusieurs-à-plusieurs avec l'API Fluent peuvent être mal compris et je suppose que votre problème est causé par ce malentendu.
On pourrait penser que cela signifie qu'ils décrivent les colonnes qui sont "gauche" et "droite" dans la table de jointure plusieurs-à-plusieurs. C'est en fait le cas si vous laissez EF Code-First créer la base de données et la table de jointure en fonction de votre mappage Fluent.
Mais ce n'est pas nécessairement le cas lorsque vous créez un mappage vers une base de données existante.
Pour illustrer cela avec l'exemple prototypique plusieurs à plusieurs d'un modèle User
- Role
, supposez que vous avez une base de données existante avec un Users
, Roles
et Table RoleUsers
:
Maintenant, vous voulez mapper ce schéma de table à un modèle simple:
public class User
{
public User()
{
Roles = new List<Role>();
}
public int UserId { get; set; }
public string UserName { get; set; }
public ICollection<Role> Roles { get; set; }
}
public class Role
{
public int RoleId { get; set; }
public string RoleName { get; set; }
}
Et vous ajoutez le mappage Fluent pour l'entité Users
(vous devez le faire de cette façon, car par convention le modèle ci-dessus serait un-à-plusieurs et vous ne pouvez pas commencer à partir de Role
côté entité car il n'a pas de collection Users
):
modelBuilder.Entity<User>()
.HasMany(u => u.Roles)
.WithMany()
.Map(m =>
{
m.MapLeftKey("RoleId"); // because it is the "left" column, isn't it?
m.MapRightKey("UserId"); // because it is the "right" column, isn't it?
m.ToTable("RoleUsers");
});
Ce mappage est incorrect et si vous essayez de mettre "Anna" dans le rôle "Marketing" ...
var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);
anna.Roles.Add(marketing);
ctx.SaveChanges();
... SaveChanges
lèvera exactement l'exception que vous rencontrez. La raison devient claire lorsque vous capturez la commande SQL envoyée avec SaveChanges
:
exec sp_executesql N'insert [dbo].[RoleUsers]([RoleId], [UserId])
values (@0, @1)
',N'@0 int,@1 int',@0=1,@1=2
Donc, EF veut insérer ici une ligne dans la table de jointure RoleUsers
avec un RoleId
de 1
et un UserId
de 2
qui provoque la violation de la contrainte de clé étrangère car il n'y a aucun utilisateur avec UserId
2
dans la table Users
.
En d'autres termes, le mappage ci-dessus a configuré la colonne RoleId
comme clé étrangère vers la table Users
et la colonne UserId
comme clé étrangère vers la table Roles
. Pour corriger le mappage, nous devons utiliser le nom de la colonne "gauche" dans la table de jointure dans MapRightKey
et la colonne "droite" dans MapLeftKey
:
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
En regardant réellement Intellisense, la description montre clairement ce que "gauche" et "droite" signifient vraiment:
MapLeftKey
Configure le nom des colonnes pour la clé étrangère gauche. La clé étrangère gauche représente la propriété de navigation spécifiée dans l'appel HasMany.
MapRightKey
Configure le nom de la ou des colonnes pour la clé étrangère droite. La clé étrangère droite représente la propriété de navigation spécifiée dans l'appel WithMany.
Ainsi, "Gauche" et "Droite" se réfèrent à l'ordre dans lequel les entités apparaissent dans le mappage Fluent, pas à l'ordre des colonnes dans la table de jointure. L'ordre dans le tableau n'a pas d'importance, vous pouvez le changer sans casser quoi que ce soit car le INSERT
envoyé par EF est un "étendu" INSERT
qui contient également les noms des colonnes et pas seulement le valeurs.
Peut-être que MapFirstEntityKey
et MapSecondEntityKey
auraient été un choix moins trompeur de ces noms de méthode - ou peut-être MapSourceEntityKey
et MapTargetEntityKey
.
C'était un long post de deux mots.
Si je suppose que cela a quelque chose à voir avec votre problème, je dirais que votre premier mappage est incorrect et que vous n'avez besoin que du second mappage correct.