1

We are using the "DB-First" approach for an application, cause the Database is shared between various applications, so it should be the "master". (MySQL)

We have 3 simple tables, responsible for the Role-To-Permission Assignment, like this:

enter image description here

The Visual-Studio Model Designer (after building the model from the database) perfectly fine recocnices this as a "Many-To-Many" relation, not even generating a "Role_to_permission"-Entity, as there are no further attributes on the assignment)

enter image description here

Until now, we created those entries in the database, which lead to the expected outcome within the application. (Accessing mappings)

Currently we are working on an Interface, allowing to assign "Permissions" to "Roles". And here, I'm a little stuck:

  • If such a relation has another attribute (such as required or a date), the EMF creates an own entity for the relation - let's assume Permission_To_Role.

then, I can "easily" create a relation, with the following code:

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();
}

Whatsoever - in this case - we don't have any additional attribute on the mapping, so the additional class is avoided by the EF Framework.

Im now trying hard to create a relation, but without success:

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();
}

This always results in an Exception, and I can't figure out why (IDs are existing and correct) ...

Exception of db.SaveChanges():

An exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll but was not handled in user code

Additional information: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

Inner Exception:

An error occurred while updating the entries. See the inner exception for details.

Inner Inner Exception:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(SELECT Permission_to_Role.PermissionId, Permission_to_Role.RoleId FROM' at line 1

Ideas?


Update:

SHOW CREATE TABLE Permission_to_Role;

output:

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:

As of the current comments: I enabled the output for queries generated by the EF, and found this charm - which is obviously a malformated Query:

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

The query actual should be:

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

So, I think this looks like a "bug"? As mentioned above:

Whatsoever - in this case - we don't have any additional attribute on the mapping, so the additional class is avoided by the EF Framework.

there is no intermediate Permission_To_Role Entity, hence it seems like EF is trying to replace this table name with the query

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

EVEN upon inserts... (Maybe this works for MsSQL and is a bad implementation for the MySQL Connector?)

CodeNotFound
  • 22,153
  • 10
  • 68
  • 69
dognose
  • 20,360
  • 9
  • 61
  • 107
  • @AussieJoe Got a quick hint how to that? – dognose May 02 '18 at 20:18
  • what's the generated SQL that is being executed by Entity Framework? Have you inspected the failing SQL? – AussieJoe May 02 '18 at 20:18
  • 1
    yes https://stackoverflow.com/questions/1412863/how-do-i-view-the-sql-generated-by-the-entity-framework – AussieJoe May 02 '18 at 20:19
  • 1
    @AussieJoe Well, that only works with access to the `IQueryable` - howerver found this one, when using `context`: `context.Database.Log = Console.WriteLine;` (I'll be right back with an update) https://stackoverflow.com/questions/16880687/how-can-i-log-the-generated-sql-from-dbcontext-savechanges-in-my-program?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa – dognose May 02 '18 at 20:22
  • yes, there are several approaches I am afraid. – AussieJoe May 02 '18 at 20:25
  • @AussieJoe, well that doesn't help at all... It shows an Invalid query, yes - but why would EMF generate this? `-- Failed in 1 ms with error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(SELECT Permission_to_Role.PermissionId, Permission_to_Role.RoleId FROM' at line 1` - Its' missing the table name it seems... but why? – dognose May 02 '18 at 20:29
  • 1
    Heres the keyPoint I guess: That looks like none-sence to me (Or is higher skilled SQL :P ) `INSERT INTO (SELECT Permission_to_Role.PermissionId, Permission_to_Role.RoleId FROM Permission_to_Role AS Permission_to_Role)( PermissionId, RoleId) VALUES ( 2, 1)` – dognose May 02 '18 at 20:33
  • Have you tried setting a key on 'Permission_to_Role' entity object (in the designer)? We always have to explicitly give it a foreign key, just to make compiler happy. It just needs an index? – AussieJoe May 02 '18 at 20:33
  • But it makes a little sence... Because there is no `Permission_To_Role` ENTITY, EMF replaces this expression with `(SELECT Permission_to_Role.PermissionId, Permission_to_Role.RoleId FROM Permission_to_Role AS Permission_to_Role)` - Therefore, the insert looks like above, while it should be `INSERT INTO Permission_To_Role ( PermissionId, RoleId) VALUES ( 2, 1)` – dognose May 02 '18 at 20:43

2 Answers2

1

I'd really like to figure out the "cause" of this issue - but for now, I added another column grantedById (referencing user.id) to that relation table.

enter image description here

So, this caused the intermediate entity Permission_To_Role to be generated in the Model, and made me believe it's going to work now! (Cause done that hundreds of times, like this: )

 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();
}

But It should be possible to do Database-Inserts for Many-To-Many-Mappings with 2 Foreign-Key-Constraint Columns ONLY as well, shouldn't it?

dognose
  • 20,360
  • 9
  • 61
  • 107
  • I, too, would love to know and understand this answer :) and I think we face similar issue in our application, and we use MSSQL. – AussieJoe May 02 '18 at 21:41
  • 1
    @AussieJoe I'll keep you updated... I've now updated the application as described - but quess what? Getting the same error-prone query for "3 Foreign-Key-Columns".... Now trying it with an 4th nullable "throwAway Column" :-) (I'm Updating relations about "Everywhere" - works... There has to be something different causing this...) – dognose May 02 '18 at 21:46
  • @AussieJoe See my second reply. I Hope this helps you as well. – dognose May 02 '18 at 23:37
1

I'm a little tired right now, but finally got it to work.

I'm not 100% sure to outline the real problem, cause I changed a lot of things - but the following "findings" are kind of milestones while resolving this problem:

First,

I tried to add more columns as mentioned above, which did nor work out. It caused the Mapping-Table to be present as entity, but the insert showed the same problem. (Insert Query, containing wired "SELECT" Statement instead of table name)

Second,

I noted, that the generated Mapping-Table (In EM-Designer) took ALL columns into account for the primary key, while I designed the table without any (composite) Primary Key in the MySQL-Designer (only a unique key was setup accross all columns):

enter image description here

Third,

I was like f*** y** - and added a surrogate Primary Key-Column to the mapping-table (In the database designer)...

enter image description here

and unchecked any Primary-Key-Membership in the EF-Designer for any remaining column:

enter image description here

And guess what? It works :-)

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();
}

So, there are three things left to say:

  • First (I noted this quite some time) - When synchronsizing your Model with the database - there are changes that aren't picked up... And you'll have a hard time figuring it out. It might only be "Indexes", but it also can be stuff like "Foreign Key Constraints" or even "Primary-Key-Settings". (Consider Hibernate (Java) as the master of the 80/20 rule: Hibernate does 80% - Searching the final 20% is up to the user!)
  • Second: This problem might be a combination of a special database-schema, relying on third-party-frameworks and expecting about everything to be nice and clean automatically... Only trust "yourself"!
  • Third: Always use a surrogate Primary Key for every table. It doesn't hurt at all, but avoids all the hassle with native-primary keys. (Which you can setup as unique keys anyway)
dognose
  • 20,360
  • 9
  • 61
  • 107
  • Yes, we have to setup primary key on tables we don't have any type of keys with. Sorry, I had told you foreign key above. For whatever reason, Entity Framework requires an index for lookup. – AussieJoe May 03 '18 at 15:11