实体框架中多个记录的插入顺序

问题描述 投票:12回答:4

当我尝试一次添加具有多个子元素的实体时,EF在重新排序插入内容时遇到麻烦。我有一个三级结构,每个结构之间都有一对多的关系[Outer 1--* Item 1--* SubItem)。如果我尝试插入带有项目和子项目的新“外部”,则包含子项目的项目最终将被首先插入。

示例代码(.NET 4.5,EF 5.0.0-rc):

public class Outer
{
    public int OuterId { get; set; }
    public virtual IList<Item> Items { get; set; }
}

public class Item
{
    public int OuterId { get; set; }
    [ForeignKey("OuterId")]
    public virtual Outer Outer { get; set; }

    public int ItemId { get; set; }
    public int Number { get; set; }

    public virtual IList<SubItem> SubItems { get; set; }
}

public class SubItem
{
    public int SubItemId { get; set; }

    [ForeignKey("ItemId")]
    public virtual Item Item { get; set; }
    public int ItemId { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<Outer> Outers { get; set; }
    public DbSet<Item> Items { get; set; }
    public DbSet<SubItem> SubItems { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
        MyContext context = new MyContext();

        // Add an Outer object, with 3 Items, the middle one having a subitem
        Outer outer1 = new Outer { Items = new List<Item>() };
        context.Outers.Add(outer1);
        outer1.Items.Add(new Item { Number = 1, SubItems = new List<SubItem>() });
        outer1.Items.Add(new Item { Number = 2, SubItems = new List<SubItem>(new SubItem[] { new SubItem() }) });
        outer1.Items.Add(new Item { Number = 3, SubItems = new List<SubItem>() });

        context.SaveChanges();

        // Print the order these have ended up in
        foreach (Item item in context.Items)
        {
            Console.WriteLine("{0}\t{1}", item.ItemId, item.Number);
        }
        // Produces output:
        // 1       2
        // 2       1
        // 3       3
    }
}

我知道this answer by Alex James,该状态指出可能需要对插入进行重新排序才能满足关系约束,但这不是这里的问题。他的回答还提到他们无法跟踪诸如列表之类的订单保留结构中的项目顺序。

我想知道如何订购这些插入物。虽然我可以依靠除PK以外的其他字段对插入的项目进行排序,但是如果我可以依靠PK顺序,效率会大大提高。我真的不需要使用多个SaveChanges调用来完成此操作。

我正在使用EF5 RC,但是从其他未解决的问题来看,已经存在了一段时间!

entity-framework entity-framework-5
4个回答
7
投票

我想知道如何订购这些插入物。

您不能。数据库命令的顺序是EF的内部行为。如果要控制命令的顺序,请不要使用从底层数据库交互中抽象出来的工具-直接使用SQL。

根据评论编辑:

是的,它是低级交互,因为在进行不受您控制的抽象时,您期望SQL命令的顺序。在较高的层次上,您会得到一些不同的结果,因为您使用的期望与该抽象不兼容。如果要控制SQL命令的顺序,则必须通过一一保存(=>多个SaveChangesTransactionScope)来强制执行EF,或者自己编写SQL。否则,请使用单独的列进行订购。

顺便说一句。 EF不会像您看到的那样保存实体。它具有自己的变更跟踪器,其中包含对所有附加实例的引用。引用保存在多个Dictionary实例和字典doesn't preserve insertion order中。如果这些集合用于生成SQL命令(我想它们是),则不能保证任何顺序。


1
投票

数据库中的表是集合。这意味着不能保证订单。我假设在您的示例中,您希望结果按“数字”排序。如果那是您想要的,那么如果该数字更改并且不再反映数据库中的顺序,您将怎么办?

如果您确实要按特定顺序插入行,则最好选择多个SaveChanges。

没有人想要多次调用SaveChanges的原因是因为这确实是这样:肮脏的黑客。

由于主键是一个技术概念,因此,无论如何在此键上排序结果都没有任何功能意义。您可以按特定字段对结果进行排序,并为此使用数据库索引。您可能看不到速度差异。

明确显示订购还有其他好处:对于必须维护它的人来说,它更容易理解。否则,该人员必须知道对主键进行排序很重要并给出正确的结果,因为在您的应用程序的另一个(完全)不相关的部分中,它偶然地与数字字段的顺序相同。


0
投票

保存之前的多个Add()或保存之前的AddRange()不会保留顺序。同样,在阅读集合时,也不能保证以最初添加时的顺序返回结果。您需要向实体添加一些属性,并且在查询时使用OrderBy()来确保它们以所需的顺序返回。


0
投票

我已经找到了一种方法。只是以为我会让你知道:

using (var dbContextTransaction = dbContext.Database.BeginTransaction())
{
  dbContext.SomeTables1.Add(object1);
  dbContext.SaveChanges();

  dbContext.SomeTables1.Add(object2);
  dbContext.SaveChanges();

  dbContextTransaction.Commit();
}
© www.soinside.com 2019 - 2024. All rights reserved.