使用非主键字段设置实体框架一对一映射

时间:2014-02-10 10:43:56

标签: c# entity-framework mapping

我有以下两个类:

public class Blog
{
    public int Id { get; set; }

    public BlogDetail BlogDetail { get; set; }
}

public class BlogDetail
{
    public int Id { get; set; }

    public int BlogId { get; set; }

    public Blog Blog { get; set; }
}

Blog和BlogDetail之间存在一对一的关系,外键是Blog.Id到BlogDetail.BlogId。

BlogDetail.BlogId是强制执行一对一关系的唯一约束。

BlogDetail.Id列是必需的,因为另一个表将BlogDetail称为一对多。

我添加了以下映射:

public BlogMap()
{
    HasKey(t => t.Id);

    Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}

public BlogDetailMap()
{
    HasKey(t => t.Id);

    Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

    HasRequired(t => t.Blog).WithMany().HasForeignKey(e => e.BlogId);
}

我试图让EF 6生成正确的sql查询,使用无主键字段作为无效的关键。它给了我以下sql查询:

select * 
from Blog
left join BlogDetail on Blog.BlogDetail_Id = BlogDetail.Id

实际上我需要的是:

select * 
from Blog
left join BlogDetail on Blog.Id = BlogDetail.BlogId

有人知道如何使用EF中的非主键字段实现一对一映射吗?我已经尽了一切可能无济于事。

如果有帮助,我会在BlogDetail上悬挂另一张桌子:

public class BlogDetailSupplier
{
    public int Id { get; set; }

    public int BlogDetailId { get; set; }

    public BlogDetail BlogDetail { get; set; }
}

这就是为什么BlodDetail必须有一个ID PK列和一个briefId FK列。标准化。

3 个答案:

答案 0 :(得分:0)

如果我理解正确,这就是你想要的:

public BlogMap()
{
    HasOptional(b => b.BlogDetail).WithOptionalPrincipal(bd => bd.Blog);
}

这样BlogDetail将拥有一个BlogId外键,同时保持它自己的(身份)Id字段,您可以在一对多关系中使用它。

更新1

使用以下模型创建了一个新项目,通过应用上述映射,我能够实现您在问题中提出的要求:

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
    public BlogDetail BlogDetail { get; set; }
}

public class BlogDetail
{
    public int Id { get; set; }
    public int PostCount { get; set; }
    public Blog Blog { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<BlogDetail> BlogDetails { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().HasOptional(b => b.BlogDetail).WithOptionalPrincipal(bd => bd.Blog);
    }
}

public class ContextInitializer : DropCreateDatabaseIfModelChanges<MyContext>
{
    protected override void Seed(MyContext db)
    {
        var blog1 =  new Blog { Name = "Blog 1", BlogDetail = new BlogDetail { PostCount = 10 } };
        var blog2 =  new Blog { Name = "Blog 2", BlogDetail = new BlogDetail { PostCount = 28 } };

        db.Blogs.AddRange(new List<Blog> { blog1, blog2 });
    }
}

您可以在此处查看查询及其结果:

Screenshot

以下是表格:

Screenshot 2

答案 1 :(得分:0)

如果你在对象上

protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity()entity.HasOne().WithOne().HasForeignKey(x=>x.BlogId); }

非常重要!! 查询时,_context.Blog.Include(inc=>inc.BlogDetail)

不要忘记 INCLUDE 否则对象将为空!!

答案 2 :(得分:-1)

使用这些地图:

public BlogMap()
{
    HasKey(t => t.Id);
}

public BlogDetailMap()
{
    HasKey(t => t.Id);

    HasRequired(t => t.Blog)
        .WithMany()
        .HasForeignKey(x => x.BlogId)
        .WillCascadeOnDelete(false);
}

这几乎产生了你的结果:

MyContext.BlogDetails.Includes(x => x.Blogs);

选择在BlogDetails上,并使用您之后的ID来加入博客。

SELECT 
    [Extent1].[id] AS [id], 
    [Extent1].[title] AS [title], 
    [Extent1].[BlogId] AS [BlogId], 
    [Extent2].[id] AS [id1], 
    [Extent2].[title] AS [title1], 
    [Extent2].[BlogDetail_id] AS [BlogDetail_id]
    FROM  [dbo].[BlogDetails] AS [Extent1]
    INNER JOIN [dbo].[Blogs] AS [Extent2] ON [Extent1].[BlogId] = [Extent2].[id]
相关问题