实体框架流畅API关系映射HasRequired(). withrequired()在没有Map()的情况下不能正常工作

本文关键字:Map 情况下 不能 工作 常工作 withrequired 框架 API 关系 实体 HasRequired | 更新日期: 2023-09-27 17:50:49

Blog Model

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; }
    }
}

Post模型
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()。我希望博客是主要的和帖子是依赖的。请参阅博客配置。

HasRequired(p => p.Post).WithRequiredPrincipal(p => p.Blog).WillCascadeOnDelete(false); 允许null Post 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).

我想知道这是一个真正的错误还是我在这里错过了什么

实体框架流畅API关系映射HasRequired(). withrequired()在没有Map()的情况下不能正常工作

Required≠Required

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是快乐的。

我们不能插入没有PrincipleDependent。这是因为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"); 
    });

这还创建了一个带有"Dependent"的数据模型。(这里是DepartmentDetails),它共享"原则"的PK,并通过FK引用它。现在我们不能在没有DepartmentDetails的情况下创建Department,因为我们只能创建Department对象,DepartmentDetails不是类,并且数据库中总是有两条记录。

当然,这与类模型中有两个实体是完全不同的。这两种模型的用法完全不同。例如,当您查询Department时,DepartmentDetails将始终加入。所以这是真正的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; }
}

在没有任何映射的情况下,这将创建一个包含PersonAddress所有字段的表(EF推断Address是一个复杂类型,因为它没有定义键)。现在我们有两个类,没有地址(和反向)就不能创建Person。但现在我们无法从数据库中获取地址。他们有点太依赖了。Address不是一个实体,因为它没有身份。它是一个值类型。这是另一种完全不同的用法