Entity Framework 6 CF: Remove One-to-Many - c#

My classes are something like:
public class Contact
{
public string Name { get; set; }
public virtual ICollection<Person> Persons { get; set; }
[Key]
public int Id { get; set; }
}
public class Person
{
public string Name { get; set; }
public virtual Contact Contact { get; set; }
public int? ContactId { get; set; }
[Key]
public int Id { get; set; }
}
When I try to remove a Contact that has in it's collection a Person, I get this error:
The primary key value cannot be deleted because references to this key
still exist. [ Foreign key constraint name =
FK_dbo.Persons_dbo.Contacts_ContactsId ]
I set the ContactId to int? so it could be nullable, and the database says it's a nullable FK, and it works fine in general. Just when I try to delete the entity with the collection that I get this error.
I want to be able to delete a Contact, but not the Persons inside it, what should I do?

The default behaviour of EF is to null foreign keys when their principal relationship is deleted. However, no database rule will be setup. In order for EF to issue update statements on the foreign keys of the dependents, they will need to be loaded into the context.
So make sure when you delete Contact all it's dependent Person entities have been loaded.

Related

entity has two relationships one-to-one and one-to-many at the same time

I have two entities Employee, Branch
public class Branch
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int ManagerId { get; set; }
public Employee Manager { get; set; }
public List<Employee> Employees { get; set; }
public List<Department> Departments { get; set; }
}
public class Employee
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ManagerId { get; set; }
public Employee Manager { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
public int BranchId { get; set; }
public Branch Branch { get; set; }
}
each branch has many departments, each department has many employees, each employee belong to one department and one branch
the database scheme should look like this :
Branch : Id , Name , ManagerId
Department : Id , Name , ManagerId
Employee : Id , Name , DepartmentId , BranchId, ManagerId #(department manager)
I want to have OneToOne relationship for Branch with Employee (Manager), and OneToMany relationship for Branch with Employee (Employees Of Branch)
right now when I try to add migrations, it fails with
Unable to determine the relationship represented by navigation
property 'Branch.Manager' of type 'Employee'
I understand the issue, but not able to solve it.
This design is too de-normalized which leads to a risk of invalid associations, and more explicit mapping needed for relationships.
For instance, if a Branch has a Manager, and a collection of employees, then an employee can get their manager via the Branch. I.e.
employee.Branch.Manager
The issue with having Manager on both Employee and Branch is there is no way to ensure that employee.Manager and employee.Branch.Manager are always pointing at the same person. (where they should always be the same person)
A Department may have a manager as well, but this would be expected to be a higher level manager where each branch has a manager that reports to a department manager.
EF will sometimes get confused trying to work out associations between entities by convention if you don't specify the FK relationship between the FK property and navigation property. From memory, EF convention uses the Property type, not the property name as the default expected key name.
So for instance given a property
public virtual Employee Manager { get; set; }
You might expect EF to work out to expect a FK to be "ManagerId" but instead it would attempt to use "EmployeeId" by default based on the type Employee not the property name. This is why you would need to explicitly point it at "ManagerId" as the FK using the ForeignKey attribute or better, using .Map(x => x.MapKey("ManagerId")) or EF Core Shadow Properties to avoid declaring FK properties in your entities alongside the navigation properties.

How to set up Navigation Properties to enforce deleting related records

I am using MVC 4 and VS2010 with Entity Framework 6.1.x. I am working with a Code First fairly easy database but there is a slightly more complex part of it. First of all, two tables Person and Recording have relationship 1 to many.
public class Person {
public int PersonID { get; set; }
public int GenderID { get; set; }
// Navigation property
public virtual List<Recording> Recordings { get; set; }
}
public class Recording {
public int RecordingID { get; set; }
// ...
public int PersonID { get; set; }
// Navigation properties
public virtual Person Person { get; set; }
public virtual List<Junction> Junctions { get; set; }
}
By default, when I delete a Person, Entity Framework deletes all recordings related to Person. That's what I expect. But the records from Recording table are also placed in different set of tables. Let's say I have table called Applicant, Application and Junction. When I delete any recording either by deleting Person or Recording, I would like all records in Junction which are related to records in Recording to be deleted, as well. In my project it wouldn't make sense if there are zero number of recordings associated with particular application.
There is a combined Primary Key, that is ApplicantID, ApplicationID, and RecordID in Junction make the complex Key.
Is it possible to enforce constraints via Entity Framework or I'd rather have to provide my own custom function?
How to set up Navigation properties in corresponding tables: Recording and Junction so that related recordings are deleted?
public class Applicant
{
public int ApplicantID { get; set; }
// Navigation property
public virtual List<Junction> Junctions { get; set; }
}
public class Application {
public int ApplicationID { get; set; }
// Navigation property
public virtual List<Junction> Junctions { get; set; }
}
public class Junction
{
public int ApplicationID { get; set; }
public int ApplicantID { get; set; }
public int RecordingID { get; set; }
public virtual Application Application { get; set; }
public virtual Applicant Applicant { get; set; }
public virtual Recording Recording { get; set; }
}
Thanks for any help.
EDIT
#Chris. From what I have understood, if a Foreign Key is not nullable, records in the secondary table are deleted if a record in the Primary table with the corresponding PrimaryID are deleted. On the other hand, if the Foreign Key is nullable, records in the secondary table, would become null but not deleted.
I made this project and two tables and I filled the database. In the class Student, if I leave DepartmentID not nullable,
public int DepartmentID { get; set; }
records are deleted when a record with primary DepartmentID is deleted. This is what I expect but if I make DepartmentID nullable
public int? DepartmentID { get; set; }
then I've got this error:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Student_dbo.Department_DepartmentID". The conflict occurred in database "TestDB", table "dbo.Student", column 'DepartmentID'.
The statement has been terminated.
In test project I made two tables with relationship 1 to many.
public class Department
{
public int DepartmentID { get; set; }
public string DepartmentName { get; set; }
public virtual List<Student> Students { get; set; }
}
and
public partial class Student : IDisposable
{
public int StudentID { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
}
Is this what I should expect?
The delete cascade is not intended to just delete items on a whim. It exists out of necessity. In your example above, Recording has a non-nullable foreign key to Person. If that Person is deleted, the foreign key would either have to be set to null (which can't happen) or all related Recordings must be deleted to preserve referential integrity.
In the second example with Junction, there's a non-nullable foreign key to Record. So if you delete a Person all related Records and all Junctions related to those records should be deleted. However, if you delete a Junction no further action is taken. There is nothing inherently dependent on a Junction so its deletion goes unnoticed. Any Records it once referenced are still valid. Remember this is all about maintaining referential integrity. As long as integrity is intact, nothing will ever be deleted.

Two foreign keys with same Navigation Property?

I am new to Entity Framework so I don't know much about it. Currently I am working on My College Project, in that Project I came across a problem where I have two foreign keys refers to the Same column in another table. how can I handle this situation.
Is it necessary to create Navigation Property for Every Foreign key. And if I create another Navigaton property for ContactId then it is necessary to create another Navigation Property in User class like:
public virtual ICollection<BlockedUser> SomePropertyName { get; set; }
please tell me the best way to overcome this problem. I am using Entity Framework 6.
Here are My Model Classes:
public class BlockedUser
{
// User Foreign Key
public int UserId { get; set; } // Composite Primary Key
// User Foreign key
public int ContactId { get; set; } // Composite Primary Key
// User Navigation Property
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; } // Primary key
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
// BlockedUser Navigation Property
public virtual ICollection<BlockedUser> BlockedUsers { get; set; }
}
Is it necessary to create Navigation Property for Every Foreign key?
Yes, or more precisely: You need at least one navigation property for every relationship. "At least one" means that you can decide which of the two entities you want to add the navigation property to. It normally depends on the most common use cases in your application if you often want to navigate from entity A to entity B or the other way around. If you want, you can add the navigation properties to both entities but you don't need to.
In your model you apparently have two (one-to-many) relationships. If you want to expose navigation properties in both entities you would need four navigation property and - important! - you have to define which navigation properties form a pair for a relationship (see the [InverseProperty] attribute in the following code snippet).
With data annotations it would like this:
public class BlockedUser
{
[Key, ForeignKey("User"), Column(Order = 1)]
public int UserId { get; set; }
[Key, ForeignKey("Contact"), Column(Order = 2)]
public int ContactId { get; set; }
[InverseProperty("BlockedUsers")]
public virtual User User { get; set; }
[InverseProperty("BlockedContacts")]
public virtual User Contact { get; set; }
}
public class User
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public virtual ICollection<BlockedUser> BlockedUsers { get; set; }
public virtual ICollection<BlockedUser> BlockedContacts { get; set; }
}
If you don't want the BlockedContacts collection you can probably just remove it and the [InverseProperty("BlockedContacts")] attribute from the Contact navigation property as well.
You could use attribute ForeignKey to solve your problem. ForeignKey is used to pair navigation property and foreign key property.There is no difference between FK data annotation with Foreign Key property and FK with Navigation Properties. However, the following code will create two foreign keys with different name.
public class BlockedUser
{
// User Foreign Key
[ForeignKey("UserId")]
public int UserId { get; set; } // Composite Primary Key
// User Foreign key
[ForeignKey("BlockedUser_User")]
public int ContactId { get; set; } // Composite Primary Key
// User Navigation Property
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; } // Primary key
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
// BlockedUser Navigation Property
public virtual ICollection<BlockedUser> BlockedUsers { get; set; }
}

Code First Cascade Delete Not Using Fluent API

I have these two entities
class AUT
{
public Guid ID { get; set; }
public string Name { get; set; }
public Engineer Engineer { get; set; }
}
class InstallationSetup
{
public virtual AUT ApplicationUnderTesting { get; set; }
public Guid ID { get; set; }
// Loads of properties etc
}
class Engineer
{
public Guid ID { get; set; }
public string Name { get; set; }
}
Using code first and some data annotations these entities create a database. I'm using EF 5, When I delete an Application, it should only delete itself and any InstallationSetup that has been referenced to it. It shouldn't delete the Engineer. However when I do try and delete it, I get the error:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.InstallationSetups_dbo.AUTs_ApplicationUnderTesting_ID". The conflict occurred in database "UXLab", table "dbo.InstallationSetups", column 'ApplicationUnderTesting_ID'.
The statement has been terminated.
So, I'm guessing that because there is another table with an entry relying on the AUT to be there, by deleting the AUT you will leave InstallationSetup with null foreign key thus a broken row.
I should be able to (preferably not using Fluent API) tell entity framework that any thing that has a reference to AUT should also be deleted? This is what I want to achieve.
you just have to add a column that is similar as your generated Foreign key column, when entity framework generates this FK column it set cascading delete to disabled.
class AUT
{
public Guid ID { get; set; }
public string Name { get; set; }
public Engineer Engineer { get; set; }
}
class InstallationSetup
{
public virtual AUT ApplicationUnderTesting { get; set; }
public int ApplicationUnderTestingId {get; set;} <--- Add this.
public Guid ID { get; set; }
// Loads of properties etc
}
class Engineer
{
public Guid ID { get; set; }
public string Name { get; set; }
}
If you generate your database again, you see that some things are changed. The automatically generated column AUTs_ApplicationUnderTesting_ID is no longer there and the ApplicationUnderTestingId column is now used for your foreign key relationship.
EF will enable cascading delete automatically now.

Code first driving me crazy with foreign key exception

These are my model classes:
public class Organization
{
public Organization()
{
}
[DisplayName("Organization Id")]
public int OrganizationId { get; set; }
[StringLength(128)]
[DisplayName("Organization Name")]
public string Name { get; set; }
}
public class User
{
public User()
{
Roles = new List<Role>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid UserGuid { get; set; }
[StringLength(25)]
public string FirstName { get; set; }
[StringLength(25)]
public string LastName { get; set; }
public virtual List<Role> Roles { get; set; }
public int OrganizationId { get; set; }
[ForeignKey("OrganizationId")]
public virtual Organization Organization { get; set; }
}
This is my code:
Organization organization = new Organization { Name = "Test", };
context.Organizations.Add(organization);
And I get this:
The INSERT statement conflicted with the FOREIGN KEY constraint
\"Organization_Users\". The conflict occurred in database
\"SampleDB\", table \"dbo.Organizations\", column
'OrganizationId'.\r\nThe statement has been terminated.
Isn't this weird? I am just adding an organization. What problem can it possibly have in this?
P.S: My user table does have OrganizationId that is foreign key and pointing to Organization table. So far so good but why is the exception thrown? I am adding a master Organization record. How does that violate foreign key constraint?
I would expect the database id to be generated for the organisation as well:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[DisplayName("Organization Id")]
public int OrganizationId { get; set; }
Either that, or you need toset it to a valid, unique value yourself before saving
Alternative guess:
The name Organization_Users suggests that there is (or was) a relation from the Organization table to th Users (perhaps via a relation table). Did you show the full code? Code there be remnants of this old relationship in the database? It is not enough to remove such relations just from the C# code (because the datatabase will continue checking the constraints, until the unused fields/relations are dropped from the actual database schema).
It appears that EF has created the relationship in the wrong direction.
The User class should have an Organization not an OrganizationID.
The Organization class should have a list of users.

Categories

Resources