【问题标题】:How to add a parent record with its children records in EF Core如何在 EF Core 中添加父记录及其子记录
【发布时间】:2017-10-07 08:13:18
【问题描述】:

下面的示例 1 曾经可以工作,但它在 EF Core 中不工作。 问题:如何使下面的示例 2 工作?

示例 1

child c1 = new child();
child c2 = new child();

parent p=new parent();
p.child.Add(c1);
p.child.Add(c2);

using (var db = new DbContext())
{
   db.parent.Add(p);
   db.SaveChanges();
}

实体父 P 具有一对一关系的子 C1 和 C2。尝试插入父项及其子项记录。但是在下面的代码中VS2017 的编辑器intellisensecust.child.Add(c1); 行无法识别.child。也许,EF Core 有更好的方法来插入父/子记录。我正在使用 ASP.NET MVC Core 1.1.1 和 EF Core 1.1.1。

示例 2

...
Parent p = new Parent { Name = "some name" , FY = SelectedYear, ... };

Child c1 = new Child { ItemName = "Abc"};
Child c2 = new Child { ItemName = "Rst"};

p.child.Add(c1);
p..child.Add(c2);

_context.Add(p);
_context.SaveChanges();

更新

根据@GlennSills 的请求,以下是著名的 Blogging Db 示例(取自this ASP.NET 教程):

public class BloggingContext : DbContext
{
    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options)
    { }

    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

在下面的Create(...) 方法中,在blog.Posts.Add(c); 行出现错误:Object reference not set to an instance of an object.

public class BlogsController : Controller
{
    private readonly BloggingContext _context;

    public BlogsController(BloggingContext context)
    {
        _context = context;    
    }
....
....
// POST: Blogs/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("BlogId,Url")] Blog blog)
{
    if (ModelState.IsValid)
    {
        Post c = new Post();
        blog.Posts.Add(c);

        _context.Add(blog);
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    return View(blog);
}
}

【问题讨论】:

  • 你试过在 ef core 中使用事务吗?
  • @H.Herzl 不,我没有。那会是什么样子?
  • 你在使用身份吗?
  • 您需要提供这两个实体(Parent 和 Child)的代码,才能正确回答此问题。如果您已正确设置这两个类之间的关系,则示例 1 有效。
  • @GlennSills 我倾向于同意你的观点。我添加了一个 UPDATE 部分来提供一个真实的例子。如果我在实际场景中使用示例 1,则会收到 UPDATE 部分中显示的错误。示例 1 似乎需要稍作调整。

标签: asp.net-core entity-framework-core


【解决方案1】:

将子对象与其父对象一起保存绝对有效。关键是您需要以 EF 了解所有关系的方式对实体进行建模。这是一个非常简单的应用程序,您可以查看一下。 Simple app on github

我有一个看起来像的父母

using System.Collections.Generic;

namespace EFParentChild
{
    public class Parent
    {
        public int ParentId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public List<Child> Children { get; set; } = new List<Child>();
    }
}

孩子来了

namespace EFParentChild
{
    public class Child
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public Parent Parent { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

这里是上下文:

使用 Microsoft.EntityFrameworkCore;

namespace EFParentChild
{
  class DbParentChild : DbContext
  {
    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Children { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ParentChildDb;Trusted_Connection=True;");
    }

  }
}

这是保存数据的程序

static void Main(string[] args)
    {
        using (var dbContext = new DbParentChild())
        {
            var parent = new Parent
            {
                FirstName = "Joe",
                LastName = "Smith"
            };

            parent.Children.Add(
                new Child
                {
                    FirstName = "LittleJoe",
                    LastName = "Smith"
                });
            parent.Children.Add(
                new Child
                {
                    FirstName = "Anne",
                    LastName = "Smith"
                });

            dbContext.Add(parent);
            dbContext.SaveChanges();
        }
    }

这里的关键是我使用约定告诉 EF 存在父子关系。注意两个实体之间使用的 id 名称是如何匹配的。子项的 ParentId 与其父项中的 ParentId 匹配。还注意到孩子的外键约束。

我使用实体框架工具创建了表格。您可以自己采用这种方法,也可以根据需要手动创建它们。父 DDL 是:

CREATE TABLE [dbo].[Parents] (
[ParentId]  INT            IDENTITY (1, 1) NOT NULL,
[FirstName] NVARCHAR (MAX) NULL,
[LastName]  NVARCHAR (MAX) NULL,
CONSTRAINT [PK_Parents] PRIMARY KEY CLUSTERED ([ParentId] ASC)
);

子 DDL 是:

CREATE TABLE [dbo].[Children] (
[Id]        INT            IDENTITY (1, 1) NOT NULL,
[FirstName] NVARCHAR (MAX) NULL,
[LastName]  NVARCHAR (MAX) NULL,
[ParentId]  INT            NOT NULL,
CONSTRAINT [PK_Children] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Children_Parents_ParentId] FOREIGN KEY ([ParentId]) 
REFERENCES [dbo].[Parents] ([ParentId]) ON DELETE CASCADE
);


GO
CREATE NONCLUSTERED INDEX [IX_Children_ParentId]
  ON [dbo].[Children]([ParentId] ASC);

【讨论】:

    【解决方案2】:

    我假设您为父实体自动生成了 id,在这种情况下,您需要将父实体添加到 DbSet 并保存更改,以便 EF Core 设置生成的属性值。

    试试这个代码,你会保存信息:

    // Create instance for parent class
    var p = new Parent { Name = "some name", FY = SelectedYear, ... };
    
    // Add entity instance to db context
    _context.Parent.Add(p);
    
    // Save changes, this action sets the generated id value to entity's property
    _context.SaveChanges();
    
    // Create instances for child entities
    var c1 = new Child { ItemName = "Abc", ParentID = p.ParentID };
    var c2 = new Child { ItemName = "Rst", ParentID = p.ParentID };
    
    // Add child to db context
    _context.Child.Add(c1);
    _context.Child.Add(c2);
    
    // Save changes
    _context.SaveChanges();
    

    似乎保存失败,因为每个子实例都没有 ParentID 属性的值,并且该列不允许空值。

    如果有用请告诉我

    【讨论】:

    • 父母和孩子都有PK作为身份。并且子表中的 FK 可以为空。在这种情况下,您建议的解决方案是否可行?
    • 是的,因为每次保存更改调用都会生成标识值
    • 我在_context.Parents.Add(p); 行收到错误MyProjContext does not contain a definition for Parents。 MyProjContext 只是一个普通的类public class MyProjContext : DbContext { public MyProjContext (DbContextOptions&lt;MyProjContext &gt; options) : base(options) { } public DbSet&lt;Parent&gt; Parent{ get; set; } public DbSet&lt;Child&gt; Child{ get; set; }
    • 在这种情况下,您必须将 dbset 属性的名称从 Parent 更改为 Parent
    猜你喜欢
    • 1970-01-01
    • 2014-07-14
    • 1970-01-01
    • 2021-03-28
    • 1970-01-01
    • 2016-04-16
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    相关资源
    最近更新 更多