EF 6 关系:在模型生成期间检测到一个或多个验证错误
EF 6 Relationship: One or more validation errors were detected during model generation
我一直在绞尽脑汁想找出我认为相对简单的关系映射(一对多)。
让我们回顾一下模型中的内容:
ContentExternalLink
public class ContentExternalLink
{
public ContentExternalLink()
{
ContentTagAssignments = new List<ContentTagAssignment>();
}
[Key]
public string LinkId { get; set; }
public string LinkTypeId { get; set; }
public string LinkTitle { get; set; }
public string LinkUrl { get; set; }
public string LinkSource { get; set; }
public string LinkPhoneNumber { get; set; }
public DateTime LinkDate { get; set; }
public DateTime LinkCreatedDate { get; set; }
public DateTime LinkModifiedDate { get; set; }
[ScriptIgnore]
public virtual ICollection<ContentTagAssignment> ContentTagAssignments { get; set; }
}
ContentTagAssignmnent
public class ContentTagAssignment
{
public ContentTagAssignment()
{
this.ContentExternalLink = new ContentExternalLink();
}
[Key]
public string TagId { get; set; }
[Key]
public string ArticleId { get; set; }
public bool IsPrimary { get; set; }
public DateTime CreatedDate { get; set; }
[ScriptIgnore]
public virtual ContentExternalLink ContentExternalLink { get; set; }
}
现在进入映射:
ContentExternalLinkMap
public class ContentExternalLinkMap : EntityTypeConfiguration<ContentExternalLink>
{
public ContentExternalLinkMap()
{
this.ToTable("content_external_link", "dbo");
this.HasKey(c => c.LinkId);
this.Property(c => c.LinkId).HasColumnName("link_id");
this.Property(c => c.LinkTypeId).HasColumnName("link_type_id");
this.Property(c => c.LinkTitle).HasColumnName("link_title");
this.Property(c => c.LinkUrl).HasColumnName("link_url");
this.Property(c => c.LinkSource).HasColumnName("link_source");
this.Property(c => c.LinkPhoneNumber).HasColumnName("link_phone_number");
this.Property(c => c.LinkDate).HasColumnName("link_date");
this.Property(c => c.LinkCreatedDate).HasColumnName("link_created_date");
this.Property(c => c.LinkModifiedDate).HasColumnName("link_modified_date");
}
}
ContentTagAssignmnetMap
public class ContentTagAssignmentMap : EntityTypeConfiguration<ContentTagAssignment>
{
public ContentTagAssignmentMap()
{
this.ToTable("content_tag_assignment", "dbo");
this.HasKey(t => new {t.TagId, t.ArticleId});
this.Property(t => t.TagId).HasColumnName("tag_id");
this.Property(t => t.ArticleId).HasColumnName("article_id");
this.Property(t => t.IsPrimary).HasColumnName("is_primary_tag");
this.Property(t => t.CreatedDate).HasColumnName("created_date");
this.HasOptional(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => new {t.TagId, t.ArticleId});
}
}
这种关系肯定很奇怪,因为 LinkId 会与 ContentTagAssignment 中的 ArticleId 匹配。
我在 ContentTagAssignmentMap 中尝试了以下方法:
//this.HasOptional(t => t.ContentExternalLink)
// .WithMany(t => t.ContentTagAssignments)
// .HasForeignKey(t => t.ContentExternalLink);
this.HasOptional(x => x.ContentExternalLink)
.WithMany(x=>x.ContentTagAssignments)
.Map(x => x.MapKey("LinkId").HasColumnAnnotation("LinkId","ArticleId",null));
//this.Map(m =>
// {
// m.Properties(x => x.ArticleId);
// m.ToTable("content_tag_assignment");
// })
// .Map(p =>
// {
// p.Properties(x => x.ContentExternalLink.LinkId);
// p.ToTable("ContentExternalLink");
// });
评论的关系不成立。一个有效的(因为它不会在初始页面加载时失败)。但是,当我尝试从 ContentExternalLink 访问列表时,出现以下错误:{"Invalid column name 'LinkId'.\r\nInvalid column name 'LinkId'."}
所以我更迷茫了...
问题
基本上,并不是每个 ContentExternalLink 都会有一个标签分配给它。但是,如果 ContentExternalLink 有 ContentTagAssignments,我应该能够得到它们的列表。此外,TagId 和 ArticleId 是字符串(guid)。
有人有什么建议吗?
谢谢!
您的实体有两个问题 model/implementation。
让我们从更简单的开始(在 中提到)。根据经验,您永远不应在构造函数(或内部任何其他位置)内初始化单个导航 属性,因为这会破坏 EF 延迟加载行为。
很快,只需删除
this.ContentExternalLink = new ContentExternalLink();
来自 ContentTagAssignment
class 构造函数的行。
现在是主要问题。 EF6 仅支持引用主体端的 PK(主键)的 FK(外键)关系。这意味着您不能在 ContentTagAssignment
实体中使用复合键 (TagId, ArticleId
) 来引用 ContentExternalLink
实体的单个 PK (LinkId
),因为您正在尝试设置通过 HasForeignKey(t => new {t.TagId, t.ArticleId})
调用。这两列构成依赖端 (ContentTagAssignment
) 的 PK 这一事实与关系无关。没有其他选择 - 您必须选择两个字段之一。根据 LinkId 将与 ContentTagAssignment 中的 ArticleId 匹配,那应该是 ArticleId
字段。
如果我的猜测是正确的,只需更换
this.HasOptional(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => new {t.TagId, t.ArticleId});
与
this.HasRequired(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => t.ArticleId);
那(连同构造函数修改)应该可以解决问题。
请注意,我还将 HasOptional
更改为 HasRequired
,因为 ArticleId
字段是 PK 的一部分,因此无论如何都不允许 null
值。
我认为问题在于您试图在没有指示正确连接的情况下使用外键。
您需要做的是向 ContentTagAssignment 添加一个 LinkId 字段,这是您应该使用的实际外键并加入该字段:
this.HasOptional(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => t.LinkId);
原因是您要让它去查找 ContentExternalLink 中不存在的密钥(tagId、ArticleId)。
在 ivan stoev 的评论之后,我看到这里唯一的错误是当外键也是主键时设置可选 (not nullable
)
所以映射将是:
public class ContentTagAssignmentMap : EntityTypeConfiguration<ContentTagAssignment>
{
public ContentTagAssignmentMap()
{
this.ToTable("content_tag_assignment", "dbo");
this.HasKey(t => new { t.TagId, t.ArticleId });
this.Property(t => t.TagId).HasColumnName("tag_id");
this.Property(t => t.ArticleId).HasColumnName("article_id");
this.Property(t => t.IsPrimary).HasColumnName("is_primary_tag");
this.Property(t => t.CreatedDate).HasColumnName("created_date");
this.HasRequired(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => t.ArticleId)
.WillCascadeOnDelete(false);
}
}
以下是您的模型的工作示例:
class Program
{
static void Main(string[] args)
{
var ctx = new Context();
ctx.Database.Delete();
ctx.Database.CreateIfNotExists();
Console.ReadKey();
}
}
public class Context : DbContext
{
public Context():base ("Teste")
{
}
public DbSet<ContentExternalLink> ContentExternalLinks { get; set; }
public DbSet<ContentTagAssignment> ContentTagAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ContentExternalLinkMap());
modelBuilder.Configurations.Add(new ContentTagAssignmentMap());
base.OnModelCreating(modelBuilder);
}
}
public class ContentExternalLink
{
public ContentExternalLink()
{
ContentTagAssignments = new List<ContentTagAssignment>();
}
[Key]
public string LinkId { get; set; }
public string LinkTypeId { get; set; }
public string LinkTitle { get; set; }
public string LinkUrl { get; set; }
public string LinkSource { get; set; }
public string LinkPhoneNumber { get; set; }
public DateTime LinkDate { get; set; }
public DateTime LinkCreatedDate { get; set; }
public DateTime LinkModifiedDate { get; set; }
public virtual ICollection<ContentTagAssignment> ContentTagAssignments { get; set; }
}
public class ContentTagAssignment
{
public ContentTagAssignment()
{
this.ContentExternalLink = new ContentExternalLink();
}
[Key]
public string TagId { get; set; }
[Key]
public string ArticleId { get; set; }
public bool IsPrimary { get; set; }
public DateTime CreatedDate { get; set; }
public virtual ContentExternalLink ContentExternalLink { get; set; }
}
public class ContentExternalLinkMap : EntityTypeConfiguration<ContentExternalLink>
{
public ContentExternalLinkMap()
{
this.ToTable("content_external_link", "dbo");
this.HasKey(c => c.LinkId);
this.Property(c => c.LinkId).HasColumnName("link_id");
this.Property(c => c.LinkTypeId).HasColumnName("link_type_id");
this.Property(c => c.LinkTitle).HasColumnName("link_title");
this.Property(c => c.LinkUrl).HasColumnName("link_url");
this.Property(c => c.LinkSource).HasColumnName("link_source");
this.Property(c => c.LinkPhoneNumber).HasColumnName("link_phone_number");
this.Property(c => c.LinkDate).HasColumnName("link_date");
this.Property(c => c.LinkCreatedDate).HasColumnName("link_created_date");
this.Property(c => c.LinkModifiedDate).HasColumnName("link_modified_date");
}
}
public class ContentTagAssignmentMap : EntityTypeConfiguration<ContentTagAssignment>
{
public ContentTagAssignmentMap()
{
this.ToTable("content_tag_assignment", "dbo");
this.HasKey(t => new { t.TagId, t.ArticleId });
this.Property(t => t.TagId).HasColumnName("tag_id");
this.Property(t => t.ArticleId).HasColumnName("article_id");
this.Property(t => t.IsPrimary).HasColumnName("is_primary_tag");
this.Property(t => t.CreatedDate).HasColumnName("created_date");
this.HasRequired(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => t.ArticleId)
.WillCascadeOnDelete(false);
}
}
以上代码的结果如下:
我一直在绞尽脑汁想找出我认为相对简单的关系映射(一对多)。
让我们回顾一下模型中的内容:
ContentExternalLink
public class ContentExternalLink
{
public ContentExternalLink()
{
ContentTagAssignments = new List<ContentTagAssignment>();
}
[Key]
public string LinkId { get; set; }
public string LinkTypeId { get; set; }
public string LinkTitle { get; set; }
public string LinkUrl { get; set; }
public string LinkSource { get; set; }
public string LinkPhoneNumber { get; set; }
public DateTime LinkDate { get; set; }
public DateTime LinkCreatedDate { get; set; }
public DateTime LinkModifiedDate { get; set; }
[ScriptIgnore]
public virtual ICollection<ContentTagAssignment> ContentTagAssignments { get; set; }
}
ContentTagAssignmnent
public class ContentTagAssignment
{
public ContentTagAssignment()
{
this.ContentExternalLink = new ContentExternalLink();
}
[Key]
public string TagId { get; set; }
[Key]
public string ArticleId { get; set; }
public bool IsPrimary { get; set; }
public DateTime CreatedDate { get; set; }
[ScriptIgnore]
public virtual ContentExternalLink ContentExternalLink { get; set; }
}
现在进入映射:
ContentExternalLinkMap
public class ContentExternalLinkMap : EntityTypeConfiguration<ContentExternalLink>
{
public ContentExternalLinkMap()
{
this.ToTable("content_external_link", "dbo");
this.HasKey(c => c.LinkId);
this.Property(c => c.LinkId).HasColumnName("link_id");
this.Property(c => c.LinkTypeId).HasColumnName("link_type_id");
this.Property(c => c.LinkTitle).HasColumnName("link_title");
this.Property(c => c.LinkUrl).HasColumnName("link_url");
this.Property(c => c.LinkSource).HasColumnName("link_source");
this.Property(c => c.LinkPhoneNumber).HasColumnName("link_phone_number");
this.Property(c => c.LinkDate).HasColumnName("link_date");
this.Property(c => c.LinkCreatedDate).HasColumnName("link_created_date");
this.Property(c => c.LinkModifiedDate).HasColumnName("link_modified_date");
}
}
ContentTagAssignmnetMap
public class ContentTagAssignmentMap : EntityTypeConfiguration<ContentTagAssignment>
{
public ContentTagAssignmentMap()
{
this.ToTable("content_tag_assignment", "dbo");
this.HasKey(t => new {t.TagId, t.ArticleId});
this.Property(t => t.TagId).HasColumnName("tag_id");
this.Property(t => t.ArticleId).HasColumnName("article_id");
this.Property(t => t.IsPrimary).HasColumnName("is_primary_tag");
this.Property(t => t.CreatedDate).HasColumnName("created_date");
this.HasOptional(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => new {t.TagId, t.ArticleId});
}
}
这种关系肯定很奇怪,因为 LinkId 会与 ContentTagAssignment 中的 ArticleId 匹配。
我在 ContentTagAssignmentMap 中尝试了以下方法:
//this.HasOptional(t => t.ContentExternalLink)
// .WithMany(t => t.ContentTagAssignments)
// .HasForeignKey(t => t.ContentExternalLink);
this.HasOptional(x => x.ContentExternalLink)
.WithMany(x=>x.ContentTagAssignments)
.Map(x => x.MapKey("LinkId").HasColumnAnnotation("LinkId","ArticleId",null));
//this.Map(m =>
// {
// m.Properties(x => x.ArticleId);
// m.ToTable("content_tag_assignment");
// })
// .Map(p =>
// {
// p.Properties(x => x.ContentExternalLink.LinkId);
// p.ToTable("ContentExternalLink");
// });
评论的关系不成立。一个有效的(因为它不会在初始页面加载时失败)。但是,当我尝试从 ContentExternalLink 访问列表时,出现以下错误:{"Invalid column name 'LinkId'.\r\nInvalid column name 'LinkId'."}
所以我更迷茫了...
问题
基本上,并不是每个 ContentExternalLink 都会有一个标签分配给它。但是,如果 ContentExternalLink 有 ContentTagAssignments,我应该能够得到它们的列表。此外,TagId 和 ArticleId 是字符串(guid)。
有人有什么建议吗?
谢谢!
您的实体有两个问题 model/implementation。
让我们从更简单的开始(在
很快,只需删除
this.ContentExternalLink = new ContentExternalLink();
来自 ContentTagAssignment
class 构造函数的行。
现在是主要问题。 EF6 仅支持引用主体端的 PK(主键)的 FK(外键)关系。这意味着您不能在 ContentTagAssignment
实体中使用复合键 (TagId, ArticleId
) 来引用 ContentExternalLink
实体的单个 PK (LinkId
),因为您正在尝试设置通过 HasForeignKey(t => new {t.TagId, t.ArticleId})
调用。这两列构成依赖端 (ContentTagAssignment
) 的 PK 这一事实与关系无关。没有其他选择 - 您必须选择两个字段之一。根据 LinkId 将与 ContentTagAssignment 中的 ArticleId 匹配,那应该是 ArticleId
字段。
如果我的猜测是正确的,只需更换
this.HasOptional(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => new {t.TagId, t.ArticleId});
与
this.HasRequired(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => t.ArticleId);
那(连同构造函数修改)应该可以解决问题。
请注意,我还将 HasOptional
更改为 HasRequired
,因为 ArticleId
字段是 PK 的一部分,因此无论如何都不允许 null
值。
我认为问题在于您试图在没有指示正确连接的情况下使用外键。
您需要做的是向 ContentTagAssignment 添加一个 LinkId 字段,这是您应该使用的实际外键并加入该字段:
this.HasOptional(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => t.LinkId);
原因是您要让它去查找 ContentExternalLink 中不存在的密钥(tagId、ArticleId)。
在 ivan stoev 的评论之后,我看到这里唯一的错误是当外键也是主键时设置可选 (not nullable
)
所以映射将是:
public class ContentTagAssignmentMap : EntityTypeConfiguration<ContentTagAssignment>
{
public ContentTagAssignmentMap()
{
this.ToTable("content_tag_assignment", "dbo");
this.HasKey(t => new { t.TagId, t.ArticleId });
this.Property(t => t.TagId).HasColumnName("tag_id");
this.Property(t => t.ArticleId).HasColumnName("article_id");
this.Property(t => t.IsPrimary).HasColumnName("is_primary_tag");
this.Property(t => t.CreatedDate).HasColumnName("created_date");
this.HasRequired(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => t.ArticleId)
.WillCascadeOnDelete(false);
}
}
以下是您的模型的工作示例:
class Program
{
static void Main(string[] args)
{
var ctx = new Context();
ctx.Database.Delete();
ctx.Database.CreateIfNotExists();
Console.ReadKey();
}
}
public class Context : DbContext
{
public Context():base ("Teste")
{
}
public DbSet<ContentExternalLink> ContentExternalLinks { get; set; }
public DbSet<ContentTagAssignment> ContentTagAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ContentExternalLinkMap());
modelBuilder.Configurations.Add(new ContentTagAssignmentMap());
base.OnModelCreating(modelBuilder);
}
}
public class ContentExternalLink
{
public ContentExternalLink()
{
ContentTagAssignments = new List<ContentTagAssignment>();
}
[Key]
public string LinkId { get; set; }
public string LinkTypeId { get; set; }
public string LinkTitle { get; set; }
public string LinkUrl { get; set; }
public string LinkSource { get; set; }
public string LinkPhoneNumber { get; set; }
public DateTime LinkDate { get; set; }
public DateTime LinkCreatedDate { get; set; }
public DateTime LinkModifiedDate { get; set; }
public virtual ICollection<ContentTagAssignment> ContentTagAssignments { get; set; }
}
public class ContentTagAssignment
{
public ContentTagAssignment()
{
this.ContentExternalLink = new ContentExternalLink();
}
[Key]
public string TagId { get; set; }
[Key]
public string ArticleId { get; set; }
public bool IsPrimary { get; set; }
public DateTime CreatedDate { get; set; }
public virtual ContentExternalLink ContentExternalLink { get; set; }
}
public class ContentExternalLinkMap : EntityTypeConfiguration<ContentExternalLink>
{
public ContentExternalLinkMap()
{
this.ToTable("content_external_link", "dbo");
this.HasKey(c => c.LinkId);
this.Property(c => c.LinkId).HasColumnName("link_id");
this.Property(c => c.LinkTypeId).HasColumnName("link_type_id");
this.Property(c => c.LinkTitle).HasColumnName("link_title");
this.Property(c => c.LinkUrl).HasColumnName("link_url");
this.Property(c => c.LinkSource).HasColumnName("link_source");
this.Property(c => c.LinkPhoneNumber).HasColumnName("link_phone_number");
this.Property(c => c.LinkDate).HasColumnName("link_date");
this.Property(c => c.LinkCreatedDate).HasColumnName("link_created_date");
this.Property(c => c.LinkModifiedDate).HasColumnName("link_modified_date");
}
}
public class ContentTagAssignmentMap : EntityTypeConfiguration<ContentTagAssignment>
{
public ContentTagAssignmentMap()
{
this.ToTable("content_tag_assignment", "dbo");
this.HasKey(t => new { t.TagId, t.ArticleId });
this.Property(t => t.TagId).HasColumnName("tag_id");
this.Property(t => t.ArticleId).HasColumnName("article_id");
this.Property(t => t.IsPrimary).HasColumnName("is_primary_tag");
this.Property(t => t.CreatedDate).HasColumnName("created_date");
this.HasRequired(t => t.ContentExternalLink)
.WithMany(t => t.ContentTagAssignments)
.HasForeignKey(t => t.ArticleId)
.WillCascadeOnDelete(false);
}
}
以上代码的结果如下: