簡體   English   中英

實體框架Fluent API關系映射HasRequired()。WithRequired()在沒有Map()的情況下無法正確運行

[英]Entity Framework Fluent API relationship mapping HasRequired().WithRequired() not behaving correctly without Map()

博客模型

using System.Collections.Generic;

namespace DataLayer
{
    public class Blog
    {
        public int BlogKey { get; set; }
        public string Title { get; set; }
        public string BloggerName { get; set; }
        public virtual Post Post { get; set; }
    }
}

郵政模型

using System;
using System.Collections.Generic;

namespace DataLayer
{
    public class Post
    {
        public int PostKey { get; set; }
        public string Title { get; set; }
        public DateTime? DateCreated { get; set; }
        public string Content { get; set; }
        public virtual Blog Blog { get; set; }
    }
}

模型配置

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;

namespace DataLayer
{
    public class BlogConfiguration : EntityTypeConfiguration<Blog>
    {
        public BlogConfiguration()
        {
            ToTable("Blog", "dbo");
            HasKey(k => k.BlogKey).Property(p=>p.BlogKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

// This will allow having null Post for the Blog
            //HasRequired(p => p.Post).WithRequiredPrincipal(p => p.Blog).WillCascadeOnDelete(false);

// This will NOT allow having no Post for the Blog
            HasRequired(p => p.Post).WithRequiredPrincipal(p => p.Blog).Map(m=>m.MapKey("OtherBlogKeyColumn")).WillCascadeOnDelete(false);
        }
    }

    public class PostConfiguration : EntityTypeConfiguration<Post>
    {
        public PostConfiguration()
        {
            ToTable("Post", "dbo");
            HasKey(k => k.PostKey).Property(p=>p.PostKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        }
    }
}

客戶

using DataLayer;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            MyDbContext c = new MyDbContext();

            //Works when dependent's foreign key column is mapped to the primary key column(this is by default when Map() is not provided). 
            //Doesn't work when foreign key column is mapped to some other column(which is provided by Map())
            Blog blog = new Blog { Title = "world", Post = null, BloggerName = "suyash" };


            //Blog required, Post required
            //Blog blog = new Blog { Title = "work", Post = new Post { Title = "new world post" }, BloggerName = "suyash" };

            c.Blogs.Add(blog);

            c.SaveChanges();

        }
    }
}

我有模型BlogPost 這里討論的關系是HasRequired()。WithRequired()。 我希望Blog成為負責人,Post成為依賴人 請參閱博客配置。

HasRequired(p => p.Post).WithRequiredPrincipal(p => p.Blog).WillCascadeOnDelete(false); 允許 Blog blog = new Blog { Title = "world", Post = null, BloggerName = "suyash" }; 為空的 Blog blog = new Blog { Title = "world", Post = null, BloggerName = "suyash" };

但是, HasRequired(p => p.Post).WithRequiredPrincipal(p => p.Blog).Map(m=>m.MapKey("OtherBlogKeyColumn")).WillCascadeOnDelete(false); 沒有。

Map()的配置按預期工作,當我們嘗試插入空Post時,它將引發錯誤。

這不是HasRequired()。WithRequired()的全部目的,即使沒有使用Map(),也要確保兩端都有一個值。 當前沒有Map(), 它的工作方式與HasOptional(Blog).WithRequired(Post)相同。

我想了解這是一個真正的錯誤,還是我在這里錯過了一些東西。

必需≠必需

HasRequired - WithRequired組合比實際承諾的更多。 它使您可以存儲唯一的原則,而無需附帶依賴項。

在關系數據庫中(至少在我所知道的實現中),沒有辦法“同時”將兩行存儲在不同的表中(即作為原子操作)。 因此,沒有辦法強制執行相互要求的1:1關聯。 必須先插入主體實體,然后是從屬實體。

但這是否應該阻止EF要求建立關聯? 我認為像其他驗證一樣,在保存更改之前可以驗證需求。 我認為他們可以強制執行HasRequired - WithRequired是真正需要的。 但是他們沒有。

如果我們有這兩個簡單的類...

public class Principal
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Dependent Dependent { get; set; }
}

public class Dependent
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Principal Principal { get; set; }
}

...映射為...

modelBuilder.Entity<Principal>()
            .HasRequired(pr => pr.Dependent)
            .WithRequiredPrincipal(dep => dep.Principal);

...我們可以做的...

var principle1 = new Principal { Name = "Principal1" };
context.Principals.Add(principle1);
context.SaveChanges();

...而EF很高興。

沒有Principle我們不能插入Dependent 這是因為Dependent的主鍵是從Principle的復制而來的,並且同時是外鍵。 但這是1:0..1,而不是1:1。 如果我們將關聯映射為...

modelBuilder.Entity<Principal>()
            .HasOptional(pr => pr.Dependent)
            .WithRequired(dep => dep.Principal);

...我們得到相同的數據模型。

備擇方案?

獲得真正需要的 1:1關聯的一種方法是實體拆分 一個常見的例子是:

modelBuilder.Entity<Department>() 
    .Map(m => 
    { 
        m.Properties(t => new { t.DepartmentID, t.Name }); 
        m.ToTable("Department"); 
    }) 
    .Map(m => 
    { 
        m.Properties(t => new { t.DepartmentID, t.Administrator, t.StartDate, t.Budget }); 
        m.ToTable("DepartmentDetails"); 
    });

這還將創建一個具有“從屬”(此處為DepartmentDetails )的數據模型,該數據模型共享“原理”的PK並通過FK進行引用。 現在,如果沒有Department就無法創建DepartmentDetails因為我們只能創建Department對象, DepartmentDetails不是類,並且數據庫中始終有兩個記錄。

但是,這當然與在類模型中具有兩個實體完全不同。 兩種型號的用法完全不同。 例如,當您查詢DepartmentDepartmentDetails始終被加入。因此,這是真正的1:1(在數據模型中),但不能替代1:1作為類。

另一種方法是映射復雜類型 一個非常基本的示例是:

public class Person
{
    public int Id { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public int Number { get; set; }
    public string Town { get; set; }
}

沒有任何映射,這將創建一個包含“ Person和“ Address中所有字段的表(EF推斷“ Address為復雜類型,因為未定義鍵)。 現在,我們有兩個類,並且如果沒有地址,我們將無法創建Person (並且反向)。 但是現在我們無法從數據庫中獲取地址。 他們有點太依賴了。 Address不是實體,因為它沒有身份。 這是一個值類型 同樣,這是另一種替代方法,但用途完全不同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM