Unidirectional One to One relationship entity framework, cascade delete doesn't work

c# entity-framework-6

Question

I want to implement a unidirectional one to one relationship; however on cascade delete doesn't work.

I have the following classes:

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public int Id { get; set; }
    public string Street { get; set; }
    //I don't want the StudentId foreign key or the property of Student class here 
}

In my Context class, I'm mapping the relationship like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Student>()
        .HasRequired(s => s.Address)
        .WithOptional()
        .Map(m => m.MapKey("Address_Id"))
        .WillCascadeOnDelete();
}

For some reason, it's not deleting the address when, student object is deleted.

Moreover, I also want to add the foreign key property (i.e. AddressId) in the Student class like this:

[ForeignKey("Address")]
[Column("Address_Id")]
public string AddressId { get; set; }

However, I get this error when I try to add a new migration:

Address_Id: Name: Each property name in a type must be unique. Property name 'Address_Id' is already defined.

I do believe I'm mixing things up (with MapKey and the attributes of AddressId). However, I don't know how to fix this.


I went through this SO question and this article; however, no luck so far.

Link to DotNetFiddle. It won't work cause there is no database.

1
7
5/23/2017 11:50:41 AM

Accepted Answer

For some reason, it's not deleting the address when, student object is deleted.

That's the normal behavior for the relationship you have defined. It's not a matter of data annotations or fluent configuration. If you have different expectations, you'd better revisit your model.

Every relationship has a one side called principal and another side called dependent. The principal side (a.k.a. master, primary) is the one being referenced. The dependent side (a.k.a. detail, secondary) is the one that is referencing the principal. The foreign key is put on the dependent side and must always point to an existing principal or null when the relationship is optional. The cascade delete works by deleing all the dependent records when the principal record is deleted.

As explained in the How Code First Determines the Principal and Dependent Ends in an Association? section of the article mentioned by you, EF always uses the required side as principal and allows you to chose the one only when both are required.

With all that being said, let see what you have.

Address is required, Student is optional. Also you want to put FK in Student, i.e. Student references Address.

All that means that in your relationship, Address is the principal and Student is the dependent. Which means the Address may exists w/o Student referencing it. If cascade delete is turned on (as you did), deleting the Address will delete the Student, not the opposite.

I think all that should explain why it's is working the way it is now, and that no attributes or configuration can help to achieve what are you asking for. If you want it differently, the same article (and related from the same series) explains how to configure the relationship to either use Shared Primary Key Association or Foreign Key Association at the Address side. Whether it is unidirectional or bidirectional absolutely has nothing in common with the problem - see Should We Make the Associations Bidirectional? section in the article.

3
4/8/2016 2:02:11 PM

Popular Answer

You foreign key should be like this :

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    [ForeignKey("AddressId")]
    public Address Address { get; set; }        
    [Column("Address_Id")]
    public int AddressId { get; set; }
}

In your fluent mapping you just need:

modelBuilder.Entity<Student>()
    .HasRequired(s => s.Address)
    .WillCascadeOnDelete(true);

Or you can force cascade delete with an annotation:

[Required]
[ForeignKey("AddressId")]
public Address Address { get; set; }

Now update your database and your mapping should be correct and the delete should cascade.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow