EF 4.1 Database First approach.
Say I have this table schema
Users 1---M UserRoles M---1 Roles
Cascade delete is setup in the Foreign Keys
The UserRoles table has additional columns like CreatedDate so I create a model for UserRoles and map accordingly.
I end up with the following Models:
User
----
int Id
string Name
List<UserRoles> UserRoles
UserRoles
---------
int UserId
int RoleId
DateTime CreatedDate
User User
Role Role
Role
----
int Id
string Name
List<UserRoles> UserRoles
If I have my configuration correct, should I be able to delete a user and will the user roles rows be deleted WITHOUT having to clear the UserRoles collection manually?
So can I just do this:
DbContext.Entry(user).State = EntityState.Deleted;
DbContext.SaveChanges();
Or do I HAVE to do this:
user.UserRoles.Clear();
DbContext.Entry(user).State = EntityState.Deleted;
DbContext.SaveChanges();
My testing shows I HAVE to clear the child collection, but I find conflicting information that if I have cascade delete setup correctly it should work by only deleting the User.
When I DON'T clear the UserRoles I receive this error:
The relationship could not be changed because one or more of the
foreign-key properties is non-nullable
Thanks for you help in clarifying this!
You must use
DbContext.Users.Remove(user);
It is not the same thing as setting the state to Deleted. Setting the state won't mark any child objects with cascading delete setup as Deleted but Remove will do.
Setting the state to Deleted should work IF no children are loaded into the context because EF will send only a DELETE statement for the parent to the database and the database will delete the children as well due to the cascading delete in the database.
IF however you have loaded children into the context setting the state on the parent to Deleted won't set the state of the children. EF will throw the exception, it's not the database who complains.
You should be able to specify that deleting a Role or User will in turn delete the child grants. You can use the WillCascadeOnDelete() method on the fluent DbModelBuilder API:
modelBuilder.Entity<UserRoles>
.HasRequired(d => d.User)
.WithMany(p => p.UserRoles)
.HasForeignKey(d => d.UserId)
.WillCascadeOnDelete();
modelBuilder.Entity<Role>
.HasMany(p => p.UserRoles)
.WithRequired(d => d.Role)
.HasForeignKey(d => d.RoleId)
.WillCascadeOnDelete();
With this setup, deleting a User or a Role should also delete all of the UserRoles.
Related
We have a database that is creating some multiple cascade paths errors.
I do understand what that means and what is happening, but I'd like to know the best approach since we have to remove some of the OnDelete Cascade options we have.
This is our table structure:
SCHOOL
- SchoolId
- Name
- ....
STUDENT
- StudentId
- SchoolId
- Name
- ...
COURSE
- CourseId
- SchoolId
- Name
STUDENT-COURSE
- StudentId
- CourseId
- ....
SchoolId FK in Student deletes all the students related in cascade when deleting a school
SchoolId FK in Course deletes all the courses related in cascade when deleting a school
StudentId FK in Student-Course deletes all the student-courses related in cascade when deleting a student
CourseId FK in Student-Course deletes all the student-courses related in cascade when deleting a course
So here we have the cycle, because when deleting a school the entity Student-Course is going to be deleted from 2 different sides, creating the cycle.
I understand what happens here:
HasMany(p => p.Students)
.WithRequired(p => p.School)
.HasForeignKey(p => p.SchoolId)
.WillCascadeOnDelete(true);
And I know I can do:
HasMany(p => p.Students)
.WithRequired(p => p.School)
.HasForeignKey(p => p.SchoolId)
.WillCascadeOnDelete(false);
The question here is what would be the best approach.
Thanks.
I would remove on delete cascade from the School, and leave the others as is, and when deleting a school add some logic to delete related students, courses (but his keeps a consistency for deletes in lower levels).
If you remove on delete cascade from the Students, you will need to manually add deletion logic for them to clean related student courses first, and will be somehow inconsistent with courses delete (hierarchical level is not consistent in terms of deletion logic).
I could go into the EDMX in design view and update the cardinality (1-, 0-, etc.) of associations, but I want to know if it is alright to do that in this scenario.
I have these two tables, let's say:
User
-----
Id
UserProfile
-------------
Id
UserId
Stuff
A single user may have just one profile. Therefore, I have set a UNIQUE constraint no the UserId column in the UserProfile table. Also, UserProfile.Id is defined as a foreign key that references User.Id.
However, in the Entity Framework model, I get the following:
class User
{
ICollection<UserProfile> UserProfiles { get; set; }
}
At first, I had the UserProfile table without any primary key of its own like this:
UserProfile
--------------
UserId
Stuff
But EntityFramework made that read-only as it needed to have a primary key on every table that you want to be made writable. So, I put an Id column in the UserProfile table as well.
UPDATE
I tried setting the multiplicit/cardinality of the association between the User table and the UserProfile table in my EDMX in the designer. However, I get this error suggesting I should turn it back to what it was, i.e. a one-to-many relationship.
Running transformation: Multiplicity is not valid in Role 'UserBasicProfile'
in relationship 'FK_UserBasicProfile_User'. Because the Dependent Role
properties are not the key properties, the upper bound of the multiplicity
of the Dependent Role must be *.
I've found a way. Well, not purely a 1-1 relationship but a 1-0..1 relationship was what I was after, which, for all practical purposes is a one-to-one relationship.
If you ever fall into this trap like I described in my question, here's what you do:
1) Remove the Id field from the dependent table, in this case the UserProfile table. Therefore, do not give it a separate primary key.
2) Instead, mark the foreign key in the dependent table as its own primary key. In this case, mark the UserId field itself as the primary key of the UserProfile table.
In addition to these two, I assume, as I outlined in the question that you've got a primary and foreign key relationship between the authority (the User table in this case) and the dependent table (the UserProfile table in this example).
So, you new table structure should look like this:
User
------
Id
UserProfile
-------------
UserId (foreign key, and also the primary key of this UserProfile table)
Stuff
Entity Framework will then recognize this 1-0..1 relationship.
I'm trying to use migrations to add a parent table to an existing child table. For eg. I currently have User table, now I want to add a Department table that has a 1 to many relationship: Department has many User.
My questions, in automatic update, can I somehow seed the parent table before adding the FK so I can update all the children to this default seeded Department? If automatic update cannot do this, how do I accomplish this in code?
What I currently did: Made the FK nullable, created the Parent and seeded it, then update all child User FK to the parent. But now I cant change the FK not nullable because throws this error: Automatic migration was not applied because it would result in data loss.
Switching from nullable to non-nullable is considered data loss because after the migration, there is no way to tell which rows (if any) were null. If you are ok with this, you can call Update-Database with the -Force flag.
Another option would be to add a code-based migration that would:
Add the Departments table
Insert a default department
Add the required FK column to User with a default value of the inserted department
If a user has multiple locations
And if a user has a single role.
My table design will be like
TableUser
Userid (pk)
Username
RoleId (fk)
TableRole
RoleId (pk)
RoleName
TableLocation
LocationId (pk)
LocationName
UserLocation
LocationId (fk) (part of composite pk)
Userid (fk)(part of composite pk)
I have entity framework 4.0.
When I fetch a user entity the user entity does not have a Role object with it.
But it does have a Location object.
Why is this like this?
Is it the default behavior of entity framework?
Does it support only one-to-many relations by default?
Yes, EF doesn't support one-to-one relation modeled this way. To enforce this relation in the database you must mark RoleId in the database with unique index / constraint. EF doesn't support unique keys so it cannot model this relation as one-to-one. The only way to model one-to-one relation in EF is on top of primary keys:
Role - PK RoleId
User - PK, FK UserId
That means that instead of User.RoleId you will use User.UserId as FK to Role.
If you want to keep your current structure you must make it one-to-many relation (one role can have many users), don't expose Users navigation property on the Role and place unique key to the database. It will still allow you assigning multiple users to single role in the application but database will fire exception if you try to save it.
I have the concept of user and roles where User has many roles and Role has many users. If a user is in a role (using a link table generated by EF), and I try to delete it, it does not cascade, then it throws an error of : The primary key value cannot be deleted because references to this key still exist. [ Foreign key constraint name = Role_Users_Target ].
This is the code I use to delete the user:
var user = new User() { UserId = userId };
db.Users.Attach(user);
db.Users.Remove(user);
db.SaveChanges();
How do I fix this?