实体框架:创建多对多关系

时间:2018-05-02 20:02:02

标签: c# mysql entity-framework

我们正在为应用程序使用“DB-First”方法,因为数据库在各种应用程序之间共享,因此它应该是“主”。 (MySQL的)

我们有3个简单的表,负责角色到权限分配,如下所示:

enter image description here

Visual-Studio模型设计器(在从数据库构建模型之后)非常精细地将其重新定义为“多对多”关系,甚至不生成“Role_to_permission”-Entity,因为没有其他属性作业)

enter image description here

到目前为止,我们在数据库中创建了这些条目,这导致了应用程序中的预期结果。 (访问映射)

目前我们正在开发一个接口,允许将“权限”分配给“角色”。在这里,我有点卡住了:

  • 如果此类关系具有其他属性(例如requireddate),则EMF会为关系创建一个自己的实体 - 让我们假设Permission_To_Role

然后,我可以“轻松”创建一个关系,使用以下代码:

using (MyDb db = new MyDB()){
   Permission_To_Role ptr = new Permission_To_Role();
   ptr.PermissionId = 5;
   ptr.RoleId = 8;
   ptr.CreationDate = DateTime.Now();

   db.Permission_To_Role.Add(ptr);
   db.SaveChanges();
}

无论如何 - 在这种情况下 - 我们在映射上没有任何其他属性,因此EF框架避免了

我现在正努力创造一种关系,但没有成功:

using (MyDB db = new MyDB())
{
    //Get ids.
    long permissionId = 2;
    long roleID = 5;

    Permission p = db.Permission.Find(permissionId);
    Role r = db.Role.Find(roleID);

    r.Permissions.Add(p);

    db.SaveChanges();
}

这总是导致异常,我无法弄清楚原因(ID存在且正确)......

db.SaveChanges()的例外情况:

  

类型的例外   发生'System.Data.Entity.Infrastructure.DbUpdateException'   EntityFramework.dll但未在用户代码中处理

     

其他信息:保存实体时发生错误   不要为其关系公开外键属性。该   EntityEntries属性将返回null,因为单个实体不能   被确定为例外的来源。处理例外情况   通过公开外键属性可以使保存更容易   你的实体类型。有关详细信息,请参阅InnerException。

内部例外:

  

更新条目时发生错误。查看内部异常   详情。

内在的例外:

  

您的SQL语法有错误;检查手册   对应于您的MySQL服务器版本,以便使用正确的语法   靠近'(SELECT Permission_to_RolePermissionId,   第{1}行Permission_to_RoleRoleId FROM'

想法?

更新

SHOW CREATE TABLE Permission_to_Role;

输出:

CREATE TABLE `Permission_to_Role` (
  `PermissionId` bigint(19) NOT NULL,
  `RoleId` bigint(19) NOT NULL,
  UNIQUE KEY `Permission_to_Role_unique` (`PermissionId`,`RoleId`),
  KEY `Permission_Mapping_idx` (`PermissionId`),
  KEY `Role_Mapping_idx` (`RoleId`),
  CONSTRAINT `Permission_Mapping` FOREIGN KEY (`PermissionId`) REFERENCES `permission` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION,
  CONSTRAINT `Role_Mapping` FOREIGN KEY (`RoleId`) REFERENCES `role` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8

UPDATE2:

截至目前的评论:我为EF生成的查询启用了输出,并发现了这个魅力 - 这显然是一个格式错误的查询:

INSERT INTO 
  (SELECT 
    Permission_to_Role.PermissionId,
    Permission_to_Role.RoleId  
   FROM 
     Permission_to_Role AS Permission_to_Role
  )
  ( PermissionId, RoleId) VALUES ( 2, 1)

查询实际应为:

INSERT INTO 
  Permission_To_Role
  ( PermissionId, RoleId) VALUES ( 2, 1)

所以,我觉得这看起来像个“虫子”?如上所述:

  

无论如何 - 在这种情况下 - 我们在映射上没有任何其他属性,因此EF框架避免了

没有中间Permission_To_Role实体,因此看起来EF似乎试图用查询替换表名

  SELECT 
    Permission_to_Role.PermissionId,
    Permission_to_Role.RoleId  
   FROM 
     Permission_to_Role AS Permission_to_Role

即使插入......(也许这适用于 MsSQL ,对于 MySQL 连接器来说是一个糟糕的实现?)

2 个答案:

答案 0 :(得分:1)

我真的很想弄清楚"原因"这个问题 - 但是现在,我在该关系表中添加了另一列grantedById(引用user.id)。

enter image description here

因此,这导致在模型中生成中间实体Permission_To_Role,并让我相信它现在正在工作! (原因已经完成了数百次,像这样:)

 using (MyDb db = new MyDB()){
   Permission_To_Role ptr = new Permission_To_Role();
   ptr.PermissionId = 5;
   ptr.RoleId = 8;
   ptr.GrantedById = Session.CurrentUser.Id;

   db.Permission_To_Role.Add(ptr);
   db.SaveChanges();
}

但是,Many-To-Many-Mappings只有2 Foreign-Key-Constraint Columns的数据库插入应该是可能的,不应该吗?

答案 1 :(得分:1)

我现在有点累了,但终于开始工作了。

我不是100%肯定会概述真正的问题,因为我改变了很多东西 - 但是在解决这个问题时,以下“发现”是一个里程碑:

首先,

我试图添加更多列,如上所述,这也没有成功。它导致Mapping-Table作为实体出现,但插入显示了同样的问题。 (插入查询,包含有线“SELECT”语句而不是表名)

其次,

我注意到,生成的映射表(在EM-Designer中)将所有列考虑到了主键,而我设计的表没有任何(复合)主键在MySQL-Designer 中(仅在所有列中设置了唯一的密钥):

enter image description here

第三,

我就像f ** y ** - 并在映射表中添加了代理主键列(在数据库设计器中)......

enter image description here

并取消选中EF-Designer中任何剩余列的所有Primary-Key-Membership:

enter image description here

猜猜是什么?它有效:-)

using (MyDb db = new MyDB()){
    Permission_to_Role ptr = new Permission_to_Role();
    ptr.PermissionId = permissionId;
    ptr.RoleId = r.Id;
    ptr.GrantedById = u.Id;
    ptr.GrantedAt = DateTime.Now;

    db.Permission_to_Role.Add(ptr);
    db.SaveChanges();
}

所以,还有三件事要说:

  • 首先(我注意到这一段时间) - 当你的模型与数据库同步时 - 有些变化没有被提取......你将很难搞清楚它。它可能只是“索引”,但也可能是“外键约束”甚至“主键设置”之类的东西。 (考虑 Hibernate (Java)作为80/20规则的主人:Hibernate做80% - 搜索最终的20%取决于用户!)
  • 第二:这个问题可能是一个特殊的数据库模式的组合,依赖于第三方框架,并期望一切都很好并自动清理......只相信“你自己” !
  • 第三:始终为每个表使用代理主键。它根本没有伤害,但避免了使用native-primary键的所有麻烦。 (无论如何你可以设置为唯一键)