18

複数の子を持つエンティティを一度に追加しようとすると、EF が挿入を並べ替えるのに問題があります。それぞれの間に 1 対多の関係を持つ 3 レベルの構造があります ( 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
    }
}

リレーショナル制約を満たすために挿入を並べ替える必要があるかもしれないと述べているAlex James によるこの回答を認識していますが、それはここでは問題ではありません。彼の答えは、リストなどの順序を維持する構造ではアイテムの順序を追跡できないことにも言及しています。

私が知りたいのは、これらのインサートを注文する方法です。挿入したアイテムを PK 以外のフィールドでソートすることもできますが、PK 順序に頼ることができれば、はるかに効率的です。これを達成するために複数の SaveChanges 呼び出しを使用する必要はありません。

私は EF5 RC を使用していますが、他の未回答の質問から判断すると、これはしばらく前から存在しています。

4

7 に答える 7

10

私が知りたいのは、これらのインサートを注文する方法です。

それはいけません。データベースコマンドの順序は、EFの内部動作です。コマンドの順序を制御したい場合は、低レベルのデータベースの相互作用から抽象化するツールを使用しないでください。SQLを直接使用してください。

コメントに基づいて編集:

はい、それは低レベルの相互作用です。なぜなら、制御下にない抽象化を操作するときにSQLコマンドの順序に期待を置いているからです。高レベルでは、その抽象化では機能しない期待値を使用しているため、何か違うものが得られます。SQLコマンドの順序を制御したい場合は、項目を1つずつ保存してEFを強制するか(=>複数SaveChangesおよびTransactionScope)、SQLを自分で作成する必要があります。それ以外の場合は、注文に別の列を使用してください。

ところで。EFは、表示されているエンティティを保存しません。アタッチされているすべてのインスタンスへの参照を保持する独自の変更トラッカーがあります。参照は複数のDictionaryインスタンスで保持され、ディクショナリは挿入順序を保持しません。これらのコレクションがSQLコマンドの生成に使用されている場合(そして私はそれらが使用されていると思います)、順序は保証されません。

于 2012-07-17T11:50:32.383 に答える
2

データベース内のテーブルはセットです。つまり、順序は保証されません。あなたの例では、「番号」で並べ替えられた結果が必要であると想定しています。それがあなたの望むものなら、その番号が変更され、データベース内の順序が反映されなくなったらどうしますか?

行を特定の順序で挿入したい場合は、複数の SaveChanges が最善の策です。

誰も SaveChanges を複数回呼び出したくない理由は、これがまさにそのように感じられるからです: 汚いハックです。

主キーは技術的な概念であるため、このキーで結果を並べ替えても機能的な意味はありません。結果を特定のフィールドで並べ替え、データベース インデックスを使用できます。おそらく速度の違いはわかりません。

順序を明示的にすることには、他の利点もあります。順序を維持する必要がある人にとって、理解しやすいということです。それ以外の場合は、主キーの順序が重要であり、正しい結果が得られることをその人が知っている必要があります。これは、アプリケーションの他の (完全に) 無関係なセクションで、誤って数値フィールドと同じ順序になっているためです。

于 2012-07-17T11:18:04.157 に答える