質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

4620閲覧

EntityFrameworkCore(postgres)でのUpdateについて

zanarukando

総合スコア3

Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2021/06/01 12:45

前提・実現したいこと

EntityFrameworkCoreで特定のレコードをUpdateしたい。

発生している問題・エラーメッセージ

EntityFrameworkCoreのUpdateメソッドを使用すると、addしていないのにも関わらず、更新用のデータでレコード追加されてしまう。旧データと新データの2レコードになってしまう。

公式のリファレンスを読んでも理解できずで困っております。。
下記のようにすれば更新はできるのですが

var entity = _dataBaseService.Books.Where(p => p.id== targetID).FirstOrDefault(); entity.amountUsed = 2000;

できるかぎり、Entity(BooksData)のsetterは排除したいと考えています。

どなたか、ご教授願います。不明点あれば申しつけ下さい。

該当のソースコード

c#

1 public interface IDataBaseService 2 { 3 DbSet<BooksData> Books { get; set; } 4 } 5 6public class BooksData 7 { 8 public BooksData(int id, string m_no, int amountUsed, DateTime intime, DateTime registDate) 9 { 10 this.id = id; 11 this.m_no = m_no; 12 this.amountUsed = amountUsed; 13 this.intime = intime; 14 this.registDate = registDate; 15 } 16 17 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 18 public int id { get; } 19 20 public string m_no { get; } 21 22 public DateTime registDate { get; } 23 24 public int amountUsed { get; } 25 26 public DateTime intime { get; } 27 28 public DateTime utime { get; } 29 } 30 31public class Main{ 32 //省略 33    var dataModel = new BooksData(id, m_no, amountUsed, DateTime.Now, registDate); 34 35 //該当箇所 36 _dataBaseService.Books.Update(dataModel); 37 38 _dataBaseService.SaveChanges(); 39}

補足情報(FW/ツールのバージョンなど)

EntityFrameworkCore 3.00
EntityFrameworkCore.PostgresSQL 3.00
Postgres11

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

_dataBaseService.Update(dataModel); _dataBaseService.SaveChanges();

もしくは、

_dataBaseService.Entry(dataModel).State = EntityState.Modified; _dataBaseService.SaveChanges();

としたらどうなりますか?

更新するには、上のコードの後者の例のようにエンティティをコンテキストにアタッチして、その State を Modified に設定してから SaveChanges メソッドを適用するか(以下の記事参照)、上のコードの前者の例のように Update メソッドを使うということになると思います。

Attaching an existing but modified entity to the context
https://docs.microsoft.com/en-us/ef/ef6/saving/change-tracking/entity-state#attaching-an-existing-but-modified-entity-to-the-context

お試しください。postgres は違うかもしれませんが、もし違ったらすみません。

【追記】

下のコメント欄の 2021/06/02 11:10 の私のコメントで「質問者さんのエラーを再現してみました。後で回答欄にその画像を貼っておきます。」と書いた件です。

先にコメント欄に書いたことも話を分かりやすくするためにまとめて書いておきます。

おそらく_dataBaseService内にすでにidが存在しているのが原因のようですが更新時にユニークな値が必ず必要だという認識なのですよね

そうではなくて、質問に書いてない上の回答のコードの前にエンティティの取得とかしていて、当該エンティティが追跡中になっているからだと思います。

自分の環境 .NET 5.0, EF Core 5.0, SQL Server でも以下のようにして再現できます。

イメージ説明

以下の Microsoft のドキュメントを見てください。

追跡なしのクエリ
https://docs.microsoft.com/ja-jp/aspnet/core/data/ef-mvc/crud?view=aspnetcore-5.0#no-tracking-queries

質問者さんの "The instance of entity type 'BooksData' cannot be tracked because another instance with the same key value for {'id'} is already being tracked." というエラーメッセージは、上記の記事に書いてあるように:

"エンティティを更新するためにエンティティをアタッチしたいが、それより前に別の目的で同じエンティティを取得してある場合。 エンティティはデータベース コンテキストによって既に追跡されているため、変更するエンティティをアタッチできません"

・・・ということであろうと思われます。解決方法は、これも記事に書いてありますが以下の 2 つが考えられます。

(1) ASP.NET Core Web アプリケーションのように、エンティティを読み取るコンテキストをエンティティが再び使われる前に破棄

(2) 前のクエリで AsNoTracking を呼び出す

上の画像の例外は、例えば上記 (2) の解決方法の AsNoTracking を呼び出すことで回避できます。以下の画像を見てください。

イメージ説明

ただ、この問題が解決できたとしても、tamoto さんの回答で言われている setter なしプロパティのエンティティクラスがホントに使えるかは疑問ですが。とにかく試してみてください。

投稿2021/06/01 13:31

編集2021/06/02 04:27
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zanarukando

2021/06/01 14:15

SurferOnWwwさん お世話になっております、ご回答ありがとうございます。 ご指摘いただいたように実装行うと下記エラーとなりました。 The instance of entity type 'BooksData' cannot be tracked because another instance with the same key value for {'id'} 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. おそらく_dataBaseService内にすでにidが存在しているのが原因のようですが 更新時にユニークな値が必ず必要だという認識なのですよねー。。うーん とりいそぎ共有させていただきます。
退会済みユーザー

退会済みユーザー

2021/06/02 00:09 編集

> おそらく_dataBaseService内にすでにidが存在しているのが原因のようですが更新時にユニークな値が必ず必要だという認識なのですよね そうではなくて、質問に書いてない上の回答のコードの前にエンティティの取得とかしていて、当該エンティティが追跡中になっているからだと思います。その辺りをチェックしてください。 今スマホしか使えないので、後で PC が使えるようになったらもう少し詳しく書きます。
退会済みユーザー

退会済みユーザー

2021/06/02 01:35

以下の Microsoft のドキュメントを見てください。 追跡なしのクエリ https://docs.microsoft.com/ja-jp/aspnet/core/data/ef-mvc/crud?view=aspnetcore-5.0#no-tracking-queries "The instance of entity type 'BooksData' cannot be tracked because another instance with the same key value for {'id'} is already being tracked." というエラーメッセージは、上記の記事に書いてあるように: "エンティティを更新するためにエンティティをアタッチしたいが、それより前に別の目的で同じエンティティを取得してある場合。 エンティティはデータベース コンテキストによって既に追跡されているため、変更するエンティティをアタッチできません" ・・・ということであろうと思われます。解決方法は、これも記事に書いてありますが以下の 2 つが考えられます。 (1) ASP.NET Core Web アプリケーションのように、エンティティを読み取るコンテキストをエンティティが再び使われる前に破棄 (2) 前のクエリで AsNoTracking を呼び出す ただ、この問題が解決できたとしても、tamoto さんの回答で言われている setter なしプロパティのエンティティクラスがホントに使えるかは疑問ですが。とにかく試してみてください。
退会済みユーザー

退会済みユーザー

2021/06/02 02:10

質問者さんのエラーを再現してみました。後で回答欄にその画像を貼っておきます。
zanarukando

2021/06/02 12:25

SurferOnWwwさん ご丁寧にありがとうございます。無事に解決しました。ありがとうございます!! こちらご指摘の通り、Findした際にエンティティがトラッキングされていたのが原因でした。 AsNoTracking で無事に成功しました! またご指摘があったSetterの件ですが、getterのみで問題なさそうです。 例えばDBからデータマッピングしてきた際に、戻り値がエンティティクラスになるので、setterが存在する場合どっかでentity.id = 123445; のように途中で値変えれてしまうことが嫌だった。という理由でした。 DTOに詰め替えるにしろ、それまではmutable の状態になってしまうと思いました。 ORMはあまり使用したことなかったので色々と助かりました。 ありがとうございました。
guest

0

こんにちは。
質問への直接の回答ではありませんが。

できるかぎり、Entity(BooksData)のsetterは排除したいと考えています。

まず、これがなぜなのかが分かりません。
DB の値を「更新」する用途があるなら、そのモデルも当然 mutable であるべきです。
EF Core の Model とは、C# で書かれていますがアプリケーション側ではなく DB 側に所属しているのです。

UI など --> ビジネスロジック --> (DB: BooksData(C#) --> postgres(SQL))

あなたの作った DB の「モデル」が更新を許していないのに、更新を行いたいという要件がそもそもおかしいのです。
Model が setter を持たないというのは、そのレコードの更新を許可しないというデータベース設計における宣言に他ならないのです。

setter を排除したいというのが、もし、ビジネスロジック側でデータを扱う際に書き換えを許可したくないというだけの話なら、
mutable な DB Model とは別に、immutable な Data class を作って詰め替えてしまえば良いのです。
その方が、プロパティ名なども C# の流儀に寄せられるのでより扱いやすくなるでしょう。

投稿2021/06/02 00:35

tamoto

総合スコア4110

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問