3

I have an entity that includes many different documents with unkown-relations to any entities in my database.

public class Document : BaseEntity
{

    public string Filename { get; set; }

    public string MIMEType { get; set; }
    public int? Length { get; set; }

    public byte[] Content { get; set; }

}

and the codefirst-mapping is:

    public DocumentConfiguration()
    {
        Property(x => x.Filename).HasMaxLength(300).IsRequired();
        Property(x => x.MIMEType).HasMaxLength(300).IsRequired();
        Property(x => x.Length).IsOptional();
        Property(x => x.Content).IsOptional().HasColumnType("varbinary(max)");


        ToTable("Document"); 
    }

Now I want an optional relation to document-table in my addressentity like so:

public class Address : BaseEntity
{

    public string Name1 { get; set; }
    public string Name2 { get; set; }
    public string Additional { get; set; }


    public string Street { get; set; }
    public string HousNr { get; set; }
    public string ZipCode { get; set; }
    public string City { get; set; }

    public virtual Document Image { get; set; }

}

with the following mapping:

    public AddressConfiguration()
    {

        Property(x => x.Name1).IsRequired().HasMaxLength(250);
        Property(x => x.Name2).HasMaxLength(250);
        Property(x => x.Additional).HasMaxLength(250);


        Property(x => x.Street).HasMaxLength(250);
        Property(x => x.HousNr).HasMaxLength(10);
        Property(x => x.ZipCode).HasMaxLength(10);
        Property(x => x.City).HasMaxLength(100);


        HasOptional(x => x.Image)
            .WithOptionalDependent()
            .Map(map => map.MapKey("ImageId")).WillCascadeOnDelete();


        ToTable("Address");

    }

But it deletes the related address when I delete an image in the document-table.

I would like a OneWay-Deletation from address to document, but not from document to address...?

How can i implement that?

thank you.

Ali Ramezani
  • 63
  • 1
  • 4

2 Answers2

5

The reason it has the cascade from document to address is because you used WithOptionalDependent method. From the documentation:

Configures the relationship to be optional:optional without a navigation property on the other side of the relationship. The entity type being configured will be the dependent and contain a foreign key to the principal. The entity type that the relationship targets will be the principal in the relationship.

Consider this line of code in AddressConfiguration method:

HasOptional(x => x.Image)         // The entity type being configured is Address
   .WithOptionalDependent()...   // The entity type that the relationship targets
                                //  is Document (x.Image)

Which means you effectively specify Address as the dependent and Document as the principal in this association hence the cascade behavior.

But wait, there is more to this story! You can create a one-to-one association in two ways. First being Shared Primary Key Association or One-to-One Foreign Key Association. You can read more about them from here and here. It looks like that you want to map your association through a Foreign Key (One-to-One Foreign Key Association). If so, you have to note that the dependent entity will always carries the foreign key which means in your case, the Document entity will have an AddressId references the AddressId on Address entity (you did the reverse which is incorrect.).

All that being said, your object model and fluent API code should be looked like this:

public class Address 
{
    public int AddressId { get; set; }
    public virtual Document Image { get; set; }
}

public class Document 
{
    public int DocumentId { get; set; }
}

class Context : DbContext
{
    public DbSet<Address> Addresses { get; set; }
    public DbSet<Document> Documents { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Address>()  // The entity being configured is Address
                    .HasOptional(x => x.Image)  
                    .WithOptionalPrincipal()
                    .Map(map => map.MapKey("AddressId"))
                    .WillCascadeOnDelete();
    }
}

Basically the WithOptionalPrincipal is the method you should use:

Configures the relationship to be optional:optional without a navigation property on the other side of the relationship. The entity type being configured will be the principal in the relationship. The entity type that the relationship targets will be the dependent and contain a foreign key to the principal.


As a result the cascade delete is properly switched on from Address to Document.

Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83
  • thank you morteza. document can be referenced from various entities. so I can map the same designated "foreign key" for all my coming entities? for example: "RelatedRecordId" with any type of relation (one-to-one or one-to-many..? optional i can have a document without any relation.. – Ali Ramezani Apr 06 '12 at 09:18
  • No you cannot! The AddressId is for Address and Address only. DB doesn't support having a FK referencing multiple entities and nor does EF. You have to have more FKs on Document which is not necessarily a good design. Maybe you can come back to your original design by having the DocId on Address but then why do you need Cascades on? If Doc is shared, deleting an Address shouldn't delete its related Doc as it might be referenced by some other entities. – Morteza Manavi Apr 06 '12 at 14:05
  • But if i want a one-to-many relation from any comming entity to document-entity? how can i map that? thanks. – Ali Ramezani Apr 10 '12 at 07:17
  • see the new question :-): http://stackoverflow.com/questions/10091453/ef-one-to-many-mapping-code-first – Ali Ramezani Apr 10 '12 at 15:18
  • @MortezaManavi - I attempted to use this but can't quite figure it out. If you get a moment, can you take a look at this? http://stackoverflow.com/questions/17767235/ef-code-first-modelbuilder-how-to-set-cascade-delete – RobVious Jul 20 '13 at 22:47
0

Try to remove OneToManyCascadeDeleteConvention: modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention >();

ADIMO
  • 1,107
  • 12
  • 24