65591


EntityFrameworkCoreにてSQLiteを使用したWPFアプリケーションを作成しています。  
ソースを下記GitHubにアップしましたので、ローカルにて実行して頂くと  
現象が分かりやすいかと思います。

<https://github.com/Nao05215/OpeTimeCalendar>

まず、各日付ごとにプロダクトがあり、その中にワークと作業時間があります。  
そこで下記の手順を実行します。

①任意の日付にプロダクトを追加します。（画面右上「Add Product」を選択して任意のコード、名前を入力）  
②追加されたプロダクト内にワークを追加します。（Productにマウスオーバーで現れる「Add Work」を選択）  
③プルダウンから任意の大項目を選択。（ワーク内一番左）  
④プルダウンから任意の小項目を選択。（ワーク内中央）  
⑤Ctrl+Sで保存  
⑥アプリケーションを一度終了して再起動  
⑦手順②③④を再度すべて同じ項目で入力する  
⑧Ctrl+Sで保存

ここで以下のエラーが発生します。  
発生個所は「40_DataBase\DataBase.cs」内の89行目「db.Add(monthInfo);」部分です。

[code]

    The instance of entity type 'Work' cannot be tracked because another instance with the same key value for {'Index'} is already being tracked.
    When attaching existing entities, ensure that only one entity instance with a given key value is attached.
    Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
    
[/code]

しかし、手順⑥の再起動を行わなかった場合、同じワークを追加してもエラーは発生しません。  
多対多テーブルの対象はProduct, Workで結合テーブル「ProductWork」を用いています。

アプリ再起動を行った場合、行わなかった場合で振る舞いが変わってしまう原因が  
全く理解できず、エラー発生の理由が掴めない為、ご教示お願いします。


CC BY-SA 4.0
2020-04-12T01:35:36.977
65548
2020-04-13T11:14:25.080
2020-04-12T06:39:14.303
3060
38100
question
c# wpf sqlite
EF Core 多対多テーブルへのデータ追加でトラッキングエラー


データベースやEntityFrameworkに触れたことは無いので、トンデモな内容かもしれませんが、何かのヒントになれば。

元の処理的には、同じ内容のものがあるか検索して、有ったら削除してから、今回のものを追加する、と言う風になっていると見ました。

ここで今回は、追加する時点の`db.Add()`で例外になっている訳ですが、上記の処理を行うなら`db.Update()`に変えても良いのでは？と考えました。  
実験したらそれでも例外が発生したので、何か類似の代替手段は無いかとIntellisenseで眺めていたら`Attach()`と言うのがあったので例外時にはそちらに切り替えてみたら、それらしく動作するようになりました。

以下のような感じですね。  
元々の`var sel = db.MonthInfos`から`db.Add(monthInfo);`までの処理は削除しています。

[code]

    public static void SaveMonthInfo(MonthInfo monthInfo, bool isNotify = true)
    {
        try
        {
            using (var db = new Context())
            {
                try
                {
                    db.Update(monthInfo);
                }
                catch
                {
                    db.Attach(monthInfo);
                }
                foreach (var item in db.Works)
                {
                    db.Entry(item).State = EntityState.Detached;
                }
                db.SaveChanges();
            }
            if (isNotify) Notification.Notify($"{monthInfo.Year}年{monthInfo.Month}月のデータを保存しました");
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.TargetSite.Name);
            Debug.WriteLine(e.Message);
            if (isNotify) Notification.Notify("データの保存に失敗しました");
        }
    }
    
[/code]

知識や理論は何も無く、変えてみたら動くようになったレベルですが、興味を引いたら調べてみてください。


CC BY-SA 4.0
2020-04-13T11:14:25.080
65591
2020-04-13T11:14:25.080
26370
65548
answer