3

エンティティのコピーを作成し、ユーザーの入力に基づいて変更を加え、データベースに再挿入することをおすすめしますか?EntityFrameworkコア - エンティティをコピーしてデータベースに戻す

他のいくつかのStackoverflowスレッドでは、データベースに同じプライマリキーが存在していても、EFが新しいオブジェクトの挿入を処理すると述べていますが、EFコアがそれをどのように扱っているかはわかりません。私が試してみて、オブジェクトをコピーするたびに、私は基本的に私は、オブジェクトをコピーし、ユーザー入力に基づいて、それにはいくつかの変更を加え、再びデータベースにそのコピーを挿入するためのクリーンな方法が必要です

Cannot insert explicit value for identity column in table when IDENTITY_INSERT is set to OFF 

のエラーが出ますIdを自動的にインクリメントするようにしてください。手動でプロパティをnullまたは空に設定しなくても、これを行うベストプラクティスまたは簡単な方法はありますか? (いくつかのフィールドがあるRowVersionタイムスタンプのように自動生成されるように)オブジェクトを取得した後

public Incident GetIncidentByIdForCloning(int id) 
    { 
     try 
     { 
      return _context.Incident.Single(i => i.IncidentId == id); 
     } 
     catch 
     { 
      return null; 
     } 
    } 

コード:

EDIT:データベースからオブジェクトを取得するためのコード例

public IActionResult Clone([FromBody]Incident Incident) 
    { 
     var incidentToCopy = _incidentService.IncidentRepository.GetIncidentByIdForCloning(Incident.IncidentId); 
     incidentToCopy.IncidentTrackingRefId = _incidentService.IncidentRepository.GetNextIdForIncidentCategoryAndType(
      Incident.IncidentCategoryLookupTableId, Incident.IncidentTypeLookupTableId).GetValueOrDefault(0); 
     incidentToCopy.RowVersion = null; 
     incidentToCopy.IncidentId = 0; //This will fail with or without this line, this was more of a test to see if manually setting would default the insert operation, such as creating a brand new object would normally do. 
     incidentToCopy.IncidentCategoryLookupTableId = Incident.IncidentCategoryLookupTableId; 
     incidentToCopy.IncidentTypeLookupTableId = Incident.IncidentTypeLookupTableId; 
     var newIncident = _incidentService.IncidentRepository.CreateIncident(incidentToCopy); 
... 

完全に新しいオブジェクトを作成して左利きのコピーを作成することはできますが、それはひどく非効率的で、EFコアがより良いソリューションを提供しているかどうかを知りたいと思います。

+0

IDENTITY \ _INSERTがOFFに設定されている場合、[テーブルのID列に明示的な値を挿入できません](http://stackoverflow.com/questions/1334012/cannot-insert-explicit-value-for Identity-Columns-in-ID) – Curiousdev

+0

エンティティを取得し、変更後にdbaに挿入しようとするコードを共有できますか?基本的にはエラーに基づいて、idプロパティをデフォルトのゼロまたはnullに設定してからdbに再挿入することをお勧めします。 –

+0

私はそれを0に設定しようとしましたが、エラーを挿入しようとすると、まだスローされ、ローカルでそれを検査する-2147483647を与えます(データベースからエラーが発生したと仮定します)。 OPのコード情報を一秒で編集します。好奇心が強い開発者にとっては、IDENTITY_INSERTをこれまで以上に設定しない方が良いでしょう。これは実稼働環境で終了するため、何か問題が起きた場合には本当に大きなワームの可能性があります。 –

答えて

3

だから私は「可能な重複が」私が最初にこれを作成する前に、それにつまずいた、と私は基本的にそれを見落とし、それほど高度ではないupvoted解決策があったときに私がやったよりも少しスレッドを経てデータベースからオブジェクトを取得するときに一度にすべての値を取得し、プロセス内のそのオブジェクトへの参照を取得しません。私のコードは次のようになります。

try 
{ 
    var incidentToCopy = _context.Incident.Single(i => i.IncidentId == id); 
    return (Incident) _context.Entry(incidentToCopy).CurrentValues.ToObject(); 
} 
2

IncidentRepositoryクラスでは、AsNoTrackingを使用してIncidentを取得してください。追加すると、新しいエンティティとして追跡されるはずです。

public void Clone(int id) 
{ 
    // Prevent tracking changes to the object. 
    var incident = _context.AsNoTracking().SingleOrDefault(i => i.Id == id); 

    // Setting back to 0 should treat the object Id as unset. 
    incident.Id = 0; 

    // Add the Incident while it is untracked will treat it as a new entity. 
    _context.Incidents.Add(incident); 
    _context.SaveChanges(); 
} 
+0

私は、これが追跡されていなかったことを知っていて、それを非常に気に入らなかったので、これをデータベースに挿入しようとしたときに問題が発生したと思います。私は先に進み、検索メソッドでオブジェクトのCurrentValuesをすべて取得しましたが、今は動作しているようです(詳細はこのスレッドへの私の回答を参照してください)。 –

+0

@RobertMcCoy DbContextは追跡されていないエンティティについては認識しません。 Addを呼び出すと、エンティティを追跡してデータベースに追加するためにキューに入れることができます。コード実行時にどのような例外が発生しましたか? – dkmann

0

私は、次のされ、ここで何が起こっているのかと考えている:

あなたは、データベースから値を取得する場合、それは

context.ChangeTracker.Entries<Incident> 
のようなものに格納されます

追跡中のインシデントエントリの集合です。取得したインシデントオブジェクトのidプロパティを変更すると、効率の名前でChangeTrackerを悪用することになります。 ChangeTrackerは、あなたが新しいオブジェクトを作成したとは信じていません。あなたは、ChangeTrackerのエントリを見つけるようなものを試して、その状態をdetachedに設定すると、idを0に設定した後にオブジェクトをcontext.DbSetに戻すことができるかもしれませんが、その時点で、単にオブジェクトをコピーするよりも複雑です。

関連する問題