【问题标题】:Entity framework single query insert child entity实体框架单查询插入子实体
【发布时间】:2015-08-19 15:18:51
【问题描述】:

我正在学习实体框架 (EF),首先在版本 6+ 上使用代码和 C# POCO。考虑以下示例:

public class DatabaseContext : DbContext
{
    public virtual DbSet<TextElement> Textuals { get; set; }
}

public class TextElement
{
    [Key]
    public int                              Number   { get; set; }
    public string                           Content  { get; set; }
    public virtual ICollection<TextElement> Children { get; set; }
}

我想要做的是将一个新的孩子添加到现有的父母。在伪 SQL 中,我可以执行以下操作:

INSERT INTO Textuals (Content, ParentId) VALUES ("Lorem Ipsum", 42)

其中 42 是父记录的 ID。

过去一天我一直在网上搜索让 EF 在 C# 代码中为我做同样的事情。我能找到的最好的是:

using (var context = new  DatabaseContext())
{
    var parent = new TextElement { Number = 42, Children = new List<TextElement>() };
    var child  = new TextElement { Content = "I iz kiddo" };
    context.Textuals.Attach(parent);
    parent.Children.Add(child);
    context.SaveChanges();
}

不过,Attach 调用似乎也在运行查询。同样的问题也适用于按 id 删除实体,它也需要 Attach,运行不必要的查询。

是否有更好的替代方法来做我想做的事:将子实体添加到现有父实体?

更新

根据 cvverb 的回答,我得到以下代码做我想做的事:

public class TextElement
{
    [Key]
    public int                              Number   { get; set; }
    public string                           Content  { get; set; }
    public virtual ICollection<TextElement> Children { get; set; }
    // Added custom parent id reference, MUST be nullable to allow 
    // for entries without parent
    public int?                             ParentId { get; set; }
}

现在,因为这是一个打破惯例的名称(惯例规定 TextElementId 尽可能含糊不清),我需要用一点“流利的 api”代码来更改上下文。这明确定义了类中属性之间的关系。

public class DatabaseContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<TextElement>()
            .HasMany((e) => e.Children)        // Indicate the many relation
            .WithOptional()                    // Indicate that the many
                                               // relation is optional.
                                               // Don't require a nav prop
            .HasForeignKey((e) => e.ParentId); // The FK to use with the
                                               // relation.
    }

    public virtual DbSet<TextElement> Textuals { get; set; }
}

这也使我能够成功应用迁移。

【问题讨论】:

  • 您的 TextElement 实体中有 ParentIdproperty 吗?
  • 仅间接使用 Children 属性。我显然喜欢 EF 来处理实现细节 =)。但如果可能的话,没有性能开销。

标签: c# entity-framework-6


【解决方案1】:

您应该在您的TextElement 实体上显式添加ParentId

public int? ParentId { get; set; }

如果您不知道该怎么做,可以在“了解一对多关系的约定”部分下的 this page 上找到一些很好的信息。

然后您可以使用以下代码添加新实体。

using (var context = new  DatabaseContext())
{
    var child  = new TextElement { Content = "I iz kiddo", ParentId = 42 };
    context.SaveChanges();
}

无需将子级附加或添加到父级的Children-list,因为实体框架会自行获取更改。

【讨论】:

  • 感谢您的提示和参考。我会试一试的!不幸的是,实现细节也需要在模型中,但我想我必须凑合着做。如果符合要求,我会接受!
【解决方案2】:

您使用的方式是正确的方式...在这里,EF 上下文为您完成所有工作:启动事务执行所需的插入、编辑和删除,最后提交更改。

【讨论】:

  • 所以你基本上是说在这种情况下我应该接受 100% 的查询开销?我只需要一个查询来插入一条新记录,但我无法在不运行其他查询的情况下与父节点建立关联?
【解决方案3】:

顺着这条路

我想做的是将一个新的孩子添加到现有的父母中

还有你使用的这个 sql 脚本

插入文本(内容,ParentId)值(“Lorem Ipsum”,42)

这应该适合你。

using (var context = new  DatabaseContext())
{
    var newTextual  = new Textuals { Number = 42, Content = "I iz kiddo" };
    context.Textuals.Attach(newTextual);
    context.SaveChanges();
}

【讨论】:

  • 请阅读我的问题。这只会用孩子的内容覆盖现有的父母的内容。请注意,(至少)有三列:数字、内容和“父 ID”。后者是 EF 为该关联生成的列。在数据库中,它实际上看起来像:TextElement_Number。那会让你解决:var newTextual = new Textuals { TextElement_Number= 42, Content = "I iz kiddo" }; 并不是我不能直接访问TextElement_Number
猜你喜欢
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多