Code First Foreign Keys between two entities

Jason H

OK what am I missing here or is this just able to be done with data annotation?

I have a Document Entity Model which has a Foreign Key to a User that added the document (one-to-one relationship):

[Table("Documents", Schema = "Configuration")]
public class Document : IPrimaryKey {
    [Key]
    public long Id { get; set; }

    [Required]
    public string OrginalName { get; set; }

    [Required]
    public DocumentTypes DocumentType { get; set; }

    [Required]
    public MIMETypes MIMEType { get; set; }

    [Required]
    public byte[] Data { get; set; }

    [DefaultValue(false)]
    public bool IsPublic { get; set; }

    [Required]
    public DateTimeOffset DateTimeAdded { get; set; }

    [Required]
    public long AddedByUser { get; set; }

    [ForeignKey("AddedByUser")]
    public virtual Details Details { get; set; }
}

I then have a User (Details) Entity that can have an image file (which is stored in the document entities model (none|one-to-one relationship):

[Table("Details", Schema = "User")]
public class Details : IPrimaryKey {
    [Key]
    public long Id { get; set; }

    [Required]
    public string UserId { get; set; }

    [ForeignKey("UserId")]
    public AppUser User { get; set; }

    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<Address> Addresses { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<Email> Emails { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<PhoneNumber> PhoneNumbers { get; set; }

    public ICollection<NotificationHistory> NotificationHistory { get; set; }
    public long TimeZoneId { get; set; }

    public long? ImageId { get; set; }

    [ForeignKey("ImageId")]
    public virtual Document Document { get; set; }

    [ForeignKey("TimeZoneId")]
    public virtual TimeZone TimeZone { get; set; }
}

When I try to create a Migration I get this error:

Unable to determine the principal end of an association between the types 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.User.Details' and 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.Configuration.Document'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

UPDATED:

While still researching this I made two changes and was able to get around the error but this created an unexpected result in my database.

In the Document Entity I added:

public virtual ICollection<Details> Details { get; set; }

In the Details (user) Entity I added:

puflic virtual ICollection<Document> Documents { get; set; }

In my DB Tables I now have the foreign key on the field I want but I have a secondary foreign key for each respectively.

I tried just removing the single virtual reference and left ONLY the ICollection Virtual reference, now I have no foreign key at all.

UPDATED (based on Akash Kava Suggestion):

I have made the following changes [Table("Documents", Schema = "Configuration")] public class Document : IPrimaryKey { [Required] public string OrginalName { get; set; }

    [Required]
    public DocumentTypes DocumentType { get; set; }

    [Required]
    public MIMETypes MIMEType { get; set; }

    [Required]
    public byte[] DocumentData { get; set; }

    [DefaultValue(false)]
    public bool IsPublic { get; set; }

    [Required]
    public DateTimeOffset DateTimeAdded { get; set; }

    [Required]
    public long AddedByUser { get; set; }

    [Key]
    public long Id { get; set; }

    [ForeignKey("AddedByUser")]

    [InverseProperty("Image")]

    public virtual Details User { get; set; }
}

[Table("Details", Schema = "User")]
public class Details : IPrimaryKey {
    [Required]
    public string UserId { get; set; }

    [ForeignKey("UserId")]
    public AppUser User { get; set; }

    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<Address> Addresses { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<Email> Emails { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<PhoneNumber> PhoneNumbers { get; set; }

    public ICollection<NotificationHistory> NotificationHistory { get; set; }
    public long TimeZoneId { get; set; }
    public long? ImageId { get; set; }

    [ForeignKey("ImageId")]
    [InverseProperty("User")]
    public Document Image { get; set; } 

    [ForeignKey("TimeZoneId")]
    public virtual TimeZone TimeZone { get; set; }


    [Key]
    public long Id { get; set; }
}

I have commented out the Fluent API Code

Unable to determine the principal end of an association between the types 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.User.Details' and 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.Configuration.Document'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

Akash Kava

You can achieve same with Data Annotation as well, you are missing InverseProperty attribute, which resolves ambiguity in this case. Conceptually, every navigation property has Inverse Navigation property, EF automatically detects and assumes inverse property based on type, but if two entities are related to each other by multiple FK properties, you have to explicitly specify InverseProperty attribute on corresponding navigation properties.

I would recommend putting InverseProperty on every navigation property, which helps reduce startup time for EF as EF does not have to determine and validate the model.

Example,

public class AccountEmail {

    public long AccountID {get;set;}

    // Inverse property inside Account class
    // which corresponds to other end of this
    // relation
    [InverseProperty("AccountEmails")]
    [ForeignKey("AccountID")]
    public Account Account {get;set;}

}

public class Account{

    // Inverse property inside AccountEmail class
    // which corresponds to other end of this
    // relation
    [InverseProperty("Account")]
    public ICollection<AccountEmail> AccountEmails {get;set;}
}

I have written a text template which generates all these navigation properties based on current schema. Download all three files from https://github.com/neurospeech/atoms-mvc.net/tree/master/db-context-tt, you might have to customize this as it adds few more things based on our framework, but it does generate pure code model from your database directly.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Code First migration when entities have cross references with foreign keys

Code First - Two foreign keys as primary keys, unable to add migration

Entity Framework Code First - two Foreign Keys from same table

Entity Framework Core - Code First - Two Foreign Keys - One Table

EF Code First Foreign Keys

EF Code First foreign key with multiple keys

How to save collection of foreign keys in code first

Foreign keys with Code First in a Web API

OR operator between two foreign keys in sequelize

how to define many-to-many and one-to-many relations between two entities in code first?

How to have multiple one-to-many relations between two entities using Entity Framework Code First

Hibernate - Foreign keys instead of Entities

Laravel relationship between two tables with two foreign keys

Using Entity Framework Code First to have two Foreign Keys from same parent table without having to specify the collections on the parent entity

Why are foreign keys in EF Code First marked as virtual?

MVC modelbuilding: multiple foreign keys in table, code first EF

Having foreign keys between two different databases using linked servers?

Multiple FOREIGN KEYs between two SQL Server tables

query to search between two foreign keys in a self join table

Scoping with two foreign keys

How to create relationships between entities with existing database that does not contain foreign keys

LINQ to Entities .Contains for a tuple (2 foreign keys)

Inserting Entities in OData with required Foreign Keys

Mapping entities with composite foreign keys in JPA

How to set a relationship between two entities that don't have an (integer) foreign key?

Two foreign keys as primary key

Two Foreign Keys generated with EF

Two Foreign Keys on one column

Room @Relation with two foreign keys