私は、ASP.NET MVC3環境のEntity Framework 5内でレコードを編集/更新するさまざまな方法を模索してきましたが、これまでのところ私が必要とするすべてのボックスにチェックを入れるものはありません。その理由を説明します。
長所と短所について述べる3つの方法があります。
方法1 - 元のレコードを読み込み、各プロパティを更新する
var original = db.Users.Find(updatedUser.UserId);
if (original != null)
{
original.BusinessEntityId = updatedUser.BusinessEntityId;
original.Email = updatedUser.Email;
original.EmployeeId = updatedUser.EmployeeId;
original.Forename = updatedUser.Forename;
original.Surname = updatedUser.Surname;
original.Telephone = updatedUser.Telephone;
original.Title = updatedUser.Title;
original.Fax = updatedUser.Fax;
original.ASPNetUserId = updatedUser.ASPNetUserId;
db.SaveChanges();
}
長所
短所
方法2 - 元のレコードを読み込み、変更した値を設定する
var original = db.Users.Find(updatedUser.UserId);
if (original != null)
{
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();
}
長所
短所
方法3 - 更新されたレコードを添付し、EntityState.Modifiedに状態を設定する
db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();
長所
短所
質問
あなたへの私の質問。この一連の目標を達成するための明確な方法はありますか。
私はこれが指摘するには非常にマイナーなことを理解していますが、私はこれに対する簡単な解決策を見逃すかもしれません。そうでなければ方法1が優先されます;-)
あなたは探している:
db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();
私は本当に受け入れられた答えが好きです。私もこれに取り組むためのさらに別の方法があると思います。ビューに含めたくないプロパティの非常に短いリストがあるとしましょう。そのため、エンティティを更新するときにはそれらは省略されます。これら2つのフィールドは、パスワードとSSNです。
db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.State = EntityState.Modified;
entry.Property(e => e.Password).IsModified = false;
entry.Property(e => e.SSN).IsModified = false;
db.SaveChanges();
この例では、UsersテーブルとViewに新しいフィールドを追加した後、ビジネスロジックを本質的にそのままにしておくことができます。
foreach(PropertyInfo propertyInfo in original.GetType().GetProperties()) {
if (propertyInfo.GetValue(updatedUser, null) == null)
propertyInfo.SetValue(updatedUser, propertyInfo.GetValue(original, null), null);
}
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();
私は、Scaffoldingによって生成されたupdateメソッドに似た追加のupdateメソッドを私のリポジトリの基本クラスに追加しました。オブジェクト全体を「変更」に設定する代わりに、個々のプロパティのセットを設定します。 (Tはクラス総称パラメーターです。)
public void Update(T obj, params Expression<Func<T, object>>[] propertiesToUpdate)
{
Context.Set<T>().Attach(obj);
foreach (var p in propertiesToUpdate)
{
Context.Entry(obj).Property(p).IsModified = true;
}
}
そして電話をかけるために、例えば:
public void UpdatePasswordAndEmail(long userId, string password, string email)
{
var user = new User {UserId = userId, Password = password, Email = email};
Update(user, u => u.Password, u => u.Email);
Save();
}
私はデータベースへの1回の旅行が好きです。ただし、プロパティのセットが繰り返されないようにするには、ビューモデルでこれを実行することをお勧めします。私のビューモデルバリデータのバリデーションメッセージを自分のドメインプロジェクトに入れないようにする方法がわからないので、私はまだそれをしていません。
public interface IRepository
{
void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class;
}
public class Repository : DbContext, IRepository
{
public void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class
{
Set<T>().Attach(obj);
propertiesToUpdate.ToList().ForEach(p => Entry(obj).Property(p).IsModified = true);
SaveChanges();
}
}
オプションのリストに追加するだけです。データベースからオブジェクトを取得し、 Auto Mapper のような自動マッピングツールを使用して、変更したいレコードの部分を更新することもできます。
ユースケースに応じて、上記のすべての解決策が適用されます。これは私が通常それをする方法です:
サーバーサイドコード(バッチプロセスなど)の場合、通常エンティティを読み込み、動的プロキシを操作します。通常バッチプロセスでは、サービスの実行時にとにかくデータをロードする必要があります。 findメソッドを使用して時間を節約する代わりに、データをバッチロードしようとしています。私が楽観的または悲観的な同時実行制御を使用するプロセスに応じて(私はプレーンSQLステートメントでいくつかのレコードをロックする必要がある並列実行シナリオを除いて常に楽観的を使用しますが、これはまれです)コードとシナリオによっては、影響をほぼゼロに抑えることができます。
クライアント側のシナリオでは、いくつかの選択肢があります。
ビューモデルを使用してください。モデルには、プロパティUpdateStatus(未修正、挿入、更新、削除)が必要です。ユーザーの操作に応じてこの列に正しい値を設定するのはクライアントの責任です(insert-update-delete)。サーバーはデータベースに元の値を問い合わせるか、クライアントは変更された行と共に元の値をサーバーに送信する必要があります。サーバーは元の値を添付し、各行のUpdateStatus列を使用して新しい値を処理する方法を決定します。このシナリオでは、私は常に楽観的同時実行を使用します。これはinsert-update-deleteステートメントのみを実行し、selectは実行しませんが、グラフをたどってエンティティを更新するには賢いコードが必要になるかもしれません(あなたのシナリオ - アプリケーションによって異なります)。マッパーは役に立ちますがCRUDロジックを処理しません
(1で説明したように)この複雑さの大部分を隠すbreeze.jsのようなライブラリを使用し、それをあなたのユースケースに適合させるようにしてください。
それが役に立てば幸い