How do I define Foreign Key Optional Relationships in FluentAPI/Data Annotations with the Entity Framework?

前端 未结 4 2046
轻奢々
轻奢々 2020-12-31 00:16

I have a (sample) application with the following code:

public class Posts
{

    [Key]
    [Required]
    public int ID { get; set; }

    [Required]
    pub         


        
相关标签:
4条回答
  • 2020-12-31 00:55

    I would probably try to create the two one-to-one relationships as optional:required because a Poll must have a reference to Posts and a Post also must have a reference to Posts:

    modelBuilder.Entity<Posts>()
        .HasOptional(x => x.Post)
        .WithRequired();
    
    modelBuilder.Entity<Posts>()
        .HasOptional(x => x.Poll)
        .WithRequired();
    

    This makes Posts automatically the principal in the relationship and Post or Poll the dependent. The principal has the primary key in the relationship, the dependent the foreign key which is also the primary key at the same time in Post/Poll table because it is a one-to-one relationship. Only in a one-to-many relationship you would have a separate column for the foreign key. For a one-to-one relationship you also have to remove the foreign key columns PostId and PollId because Posts refers through its primary key to the Post and Poll.

    An alternative approach which seems to be appropriate in your model is inheritance mapping. Then the model would look like this:

    public abstract class BasePost  // your former Posts class
    {
        public int ID { get; set; }
        public string UserName { get; set; }
    }
    
    public class Post : BasePost
    {
        public string Text { get; set; }
        // other properties of the Post class
    }
    
    public class Poll : BasePost
    {
        // properties of the Poll class
    }
    

    You don't need the TypeOfPost then anymore because you can filter the two concrete types using the OfType LINQ operator, for example:

    var x = context.BasePosts.OfType<Post>()
        .Where(p => p.UserName == "Jim")
        .ToList();
    

    This would select all posts of a particular user but not the polls.

    You have to decide then which kind of inheritance mapping you want to use - TPH, TPT or TPC.

    Edit

    To get a one-to-many relationship you can specify the following mapping in Fluent API:

    modelBuilder.Entity<Posts>()
        .HasOptional(x => x.Post)
        .WithMany()
        .HasForeignKey(x => x.PostID);
    
    modelBuilder.Entity<Posts>()
        .HasOptional(x => x.Poll)
        .WithMany()
        .HasForeignKey(x => x.PollID);
    

    The foreign key properties must be nullable (int?) for this as you already found. Because the naming of your foreign key properties follows the naming convention EF uses for mapping you can omit the Fluent mapping altogether. It would only be required if you had unconventional names (like PostFK or something). You could then also use data annotations ([ForeignKey(...)] attribute) instead of Fluent API.

    0 讨论(0)
  • 2020-12-31 01:00

    The reason it wasn't allowing nulls because the following:

    public int PollID { get; set; }
    public virtual Poll Poll { get; set; }
    
    public int PostID { get; set; }
    public virtual Post Post { get; set; }
    

    should have been

    public int? PollID { get; set; }
    public virtual Poll Poll { get; set; }
    
    public int? PostID { get; set; }
    public virtual Post Post { get; set; }
    
    0 讨论(0)
  • Something else that might help. Setting Foreign Key attributes (annotations) with the [Required] attribute will also enforce EF required navigation properties EVEN WHEN the FK property is nullable. I have a special case with legacy data where a FK property is required but may or may not reference a record in the relationship. Makes sense, but I didn't think EF was this 'smart'.

        [Required] <-- even if the FK is nullable, OrgUnit will be Required
        [StringLength(10)]
        [ForeignKey("OrgUnit"), Column(Order = 1)]
        public string OrgCode
        {
            get;
            set;
        }
    
        ... other FK, Column order 0   
    
        public virtual OrgUnit OrgUnit
        {
            get;
            set;
        }
    
    0 讨论(0)
  • 2020-12-31 01:13

    The ForeignKey just has to be Nullable to make it optional - virtual is separate, and only required for Lazy Loading.

    A required relationship in declarative EF Code First:

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

    An optional relationship in declarative EF Code First:

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

    You'll see it when you run update-database -verbose -f:

    ALTER TABLE [dbo].[MyTable] ALTER COLUMN [UserId] [int] NULL
    
    0 讨论(0)
提交回复
热议问题