2

概要: Code First と EF 5.0 を使用して MVC ASP.Net アプリに取り組んでいます。Scripts と ScriptItems の 2 つのテーブルがあります。スクリプトは、複数の ScriptItem を持つことができます。ScriptItem も階層的です。ScriptItems はオプションで互いに属することができますが、この関係はありがたいことに 1 レベルの深さしかありません。この関係は、ScriptItem.ParentId によって示されます。

問題: ScriptItems を使用して新しいスクリプト エントリを作成すると、問題なく動作します。ScriptItems を既存のスクリプトに追加しようとすると、問題が発生します。ParentId を持たない ScriptItems を追加しようとすると、すべて正常に動作します。ParentId を持つ ScriptItem を追加しようとするとすぐに、FK 違反の例外が発生します。

詳細:

スクリプト クラス:

public class Script
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Type { get; set; }
    [ForeignKey("ProcessorId")]
    public Processor Processor { get; set; }
    public int ProcessorId { get; set; }
    public string Owner { get; set; }
    public DateTime Created { get; set; }
    public bool Public { get; set; }

    public List<ScriptItem> Items { get; set; }
    public List<ScriptRun> Runs { get; set; }

    public Script()
    {
        Items = new List<ScriptItem>();
        Created = DateTime.Now;
    }
}

ScriptItem クラス: (簡潔にするために切り詰められています)

 public class ScriptItem
{
    [Key]
    public int Id { get; set; }

    [ForeignKey("ParentId")]
    public ScriptItem Parent { get; set; }
    public int? ParentId { get; set; }

    public Script Script { get; set; }
    [ForeignKey("Script")]
    public int ScriptId { get; set; }

スクリプト項目を追加する関数:

private void addToScript(ScriptModel model, List<int> ids)
    {
        Script script = scriptRepository.GetScriptWithItems(model.ScriptId);
        List<History> historyItems = historyRespository.History.Where(h => ids.Contains(h.Id)).ToList();

        ScriptItem lastScriptItem = script.Items.OrderByDescending(item => item.SortIndex).FirstOrDefault();
        int topSortIndex = lastScriptItem == null ? 0 : lastScriptItem.SortIndex;

        if (script != null)
        {
            List<ScriptItem> newItems = new List<ScriptItem>();
            Mapper.CreateMap<History, ScriptItem>();
            foreach (History h in historyItems)
            {
                ScriptItem scriptItem = new ScriptItem();
                Mapper.Map(h, scriptItem); //Populate new ScriptItem from History entry
                scriptItem.SortIndex = ++topSortIndex;
                scriptItem.ScriptId = model.ScriptId;
                scriptItem.Script = script;

                //Only add an entry if it is NOT the parent of another entry. Otherwise, EF will duplicate the Parent entries
                if (!historyItems.Any(his => his.ParentId == h.Id))
                    newItems.Add(scriptItem);    
            }
            scriptRepository.AddScriptItems(newItems);
        }
    }

最後に、scriptRepository.AddScripItems():

public void AddScriptItems(List<ScriptItem> items)
    {
        items.ForEach(item => context.Entry(item).State = System.Data.EntityState.Added);
        context.SaveChanges();
    }

2 つの ScriptItem A と B を既存のスクリプトに追加するシナリオを考えてみましょう。A は B の親です。SQL Server トレースを実行すると、親レコード A を挿入しようとしましたが、ScriptId が 0 であるため、FK 違反例外が発生しました。ScriptId が 0 である理由がわかりません。ScriptId は ScriptItems で適切に設定されています。これをデバッガで確認しました。

上記の addToScript 関数と非常によく似ているため、新しいスクリプトと項目を挿入する関数は含めませんでした。そして、それはうまくいきます。でも、誰かが見たいなら、私もそれを追加できます。

私より頭のいい人にアイデアはありますか?ありがとう!

4

2 に答える 2

3

何が原因かわかりません。しかし、所有者スクリプトを設定する代わりにnewScriptItemを追加すると役立つと思います。script.Items

scriptItem.ScriptId = model.ScriptId;
scriptItem.Script = script;

script.Items.Add(scriptItem);

もう 1 つの利点は、状態を手動で変更する必要がないことです。変更トラッカーは、追跡対象のコレクションに新しいアイテムが追加されたときに十分に認識します。スクリプトでこれを行う必要があるかどうかさえ疑問に思います。設定Scriptも十分なはずだったからです。

おそらく、状態の設定script 変更 が EF 自身のロジックに干渉しすぎて、軌道から外れてしまったのでしょう。ScriptId

于 2012-10-14T20:55:22.877 に答える
0

私を正しい方向に向けてくれたGertArnoldに感謝します。この問題は、最終的に、AutoMapperを介してScriptItemを構築する方法に起因していました。正確な問題が何であるかを説明することはできませんが、誰かがそれを有用だと思った場合に備えて、ここに私の改訂された作業コードがあります。

private void addToScript(ScriptModel model, List<int> ids)
    {
        Script script = scriptRepository.GetScriptWithItems(model.ScriptId);
        List<History> historyItems = historyRespository.History.Where(h => ids.Contains(h.Id)).ToList();

        ScriptItem lastScriptItem = script.Items.OrderByDescending(item => item.SortIndex).FirstOrDefault();
        int topSortIndex = lastScriptItem == null ? 0 : lastScriptItem.SortIndex;

        if (script != null)
        {
            Mapper.CreateMap<History, ScriptItem>();
            List<ScriptItem> Parents = new List<ScriptItem>();
            List<History> SourceParents = historyItems
                .Where(h => historyItems.Any(h2 => h2.ParentId == h.Id)).ToList();

            SourceParents.Each(h =>
            {
                ScriptItem parent = new ScriptItem();
                Mapper.Map(h, parent);
                parent.Script = script;
                Parents.Add(parent);
            });

            historyItems.Except(SourceParents).Each(h =>
            {
                ScriptItem child = new ScriptItem();
                Mapper.Map(h, child);
                child.Script = script;
                if (child.ParentId.HasValue)
                    child.Parent = Parents.SingleOrDefault(p => p.Id == child.ParentId);
                script.Items.Add(child);
            });

            //Todo: Get sortIndex "sorted" out

            scriptRepository.SaveScript(script);
        }
    }

scriptRepository.SaveScriptは、スクリプトの状態を変更済みに設定し、SaveChanges()を呼び出すだけです。

于 2012-10-15T00:50:04.740 に答える