Pergunta

I hit an issue when trying to delete records due to FK constraints. I therefore went back to the drawing board and am trying to specify how the relationship should work.

Here are my code first classes:

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("DeferredDataId")]
    public virtual DeferredData DeferredData { get; set; }
}

public class DeferredData
{

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    //other properties
}

What I am looking to achieve is that the MemberDataSet has zero or one DeferredData. I can access DeferredData from MemberDataSet but DeferredData does not need a navigation property back to MemberDataSet. DeferredData should strictly require a MemberDataSet. In an ideal world deleting MemberDataSet will therefore delete DeferredData if assigned.

What seemed to me to be what I wanted to specify is this:

modelBuilder.Entity<MemberDataSet>().HasOptional(d => d.DeferredData).WithRequired().WillCascadeOnDelete(true);

i.e. MemberDataSet has an option DeferredData but DeferredData has a required MemberDataSet and this relationship should cascade on delete.

However, I then get an error:

The ForeignKeyAttribute on property 'DeferredData' on type 'MemberDataSet' is not valid. The foreign key name 'DeferredDataId' was not found on the dependent type 'DeferredData'. The Name value should be a comma separated list of foreign key property names.

Edit

After feeling happy with Sam's answer below I went ahead and changed a few other ForeignKey attributes. MemberDataSet has another property called SignedOffBy that is a userProfile. This previously looked like this:

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("DeferredDataId")]
    public virtual DeferredData DeferredData { get; set; }

    public int? SignedOffById { get; set; }
    [ForeignKey("SignedOffId")]
    public virtual UserProfile SignedOffBy { get; set; }
}

After discussion below on what ForeignKey attribute is actually doing I changed this to:

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("Id")]
    public virtual DeferredData DeferredData { get; set; }

    public int? SignedOffById { get; set; }
    [ForeignKey("UserId")]
    public virtual UserProfile SignedOffBy { get; set; }
}

However, I now get a very similar error message:

The ForeignKeyAttribute on property 'SignedOffBy' on type 'MemberDataSet' is not valid. The foreign key name 'UserId' was not found on the dependent type 'MemberDataSet'. The Name value should be a comma separated list of foreign key property names.

The difference here is that this relationship is Many to One i.e. 1 user can have several signedoff datasets. Is this what makes the difference? i.e. the UserProfile is now the principal object so the ForeignKey is on the MemberDataSet?

Many thanks again for any and all help.

Foi útil?

Solução

The error

The ForeignKeyAttribute on property 'DeferredData' on type 'MemberDataSet' is not valid. The foreign key name 'DeferredDataId' was not found on the dependent type 'DeferredData'.

is telling you exactly what is wrong.

DeferredData.Id is not DeferredData.DeferredDataId

This is your problem.

Just removing the attribute will solve your problem as Entity Framework figures out foreign keys based on the name of your entities. If you want to keep the attributes, use:

[ForeignKey("Id")]

instead of

[ForeignKey("DeferredDataId")]

So:

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("Id")]
    public virtual DeferredData DeferredData { get; set; }
}

or change the Id of DeferredData to be DeferredDataId and not Id

A few notes about EF:

  1. Properties with names Id are automatically Keys, so no need for the Key attribute
  2. When you define a relationship using code first you don't need to manually decorate things with attributes, EF figures it out based on the structure.

Edit:

For a One-to-many relationship you need an ICollection<T>

public virtual ICollection<MemberDataSet> MemberDataSets { get; set; }

Does UserProfile have a UserId property?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top