博客模型
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();
}
}
}
我有模型Blog
和Post
。这里讨论的关系是HasRequired()。WithRequired()。 我希望博客成为校长和发布作为依赖。请参阅博客配置。
HasRequired(p => p.Post).WithRequiredPrincipal(p => p.Blog).WillCascadeOnDelete(false);
允许使用 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()的配置按预期工作,当我们尝试插入空帖子时会抛出错误。
不是HasRequired()的全部目的。WithRequired()是为了确保即使没有使用Map(),两端都有一个值。目前没有Map()它就像HasOptional(博客).WithRequired(Post)。
我想明白这是一个真正的错误还是我在这里遗漏了一些东西。
答案 0 :(得分:1)
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很高兴。
我们无法在没有Dependent
的情况下插入Principle
。那是因为Dependent
的主键是从Principle
复制的,而且它同时是一个外键。但那是1:0..1,而不是1:1。如果我们将关联映射为...
modelBuilder.Entity<Principal>()
.HasOptional(pr => pr.Dependent)
.WithRequired(dep => dep.Principal);
......我们得到相同的数据模型。
获得真正需要的 1:1关联的一种方法是 实体拆分 。 common example是:
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
)的数据模型,该模型共享“Principle”的PK并通过FK引用它。现在我们无法创建Department
没有DepartmentDetails
,因为我们只能创建一个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; }
}
如果没有任何映射,则会创建一个包含Person
和Address
中所有字段的表(EF推断Address
作为复杂类型,因为它没有定义键)。现在我们有两个类,我们不能创建没有地址(和反向)的Person
。但现在我们无法从数据库中获取地址。他们有点太依赖了。并且Address
不是实体,因为它没有身份。它是值类型。所以,这是另一种选择,但却完全不同。