3

私の基本エンティティであるUserというクラスがあり(DbSetユーザーとしてDbContextで使用します)、Data-Access-Layersのグラウンドレベルとして使用するとします。クラスが次のようになっているとしましょう。

public class User
{
    [Key]
    public int Id { get; set; }
    public bool Active { get; set; }
    public string Description { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public byte[] Photo { get; set; }
    public DateTime Created { get; set; }
}

ここで、ユーザーがアクティブかどうかだけを表示する純粋なビューと、その値を変更できる単純なチェックボックスが必要です。他のエンティティプロパティ、特にプロパティPhotoは非常識なので、ロードしたくありません。次のようなActivateUserModelを作成しました。

public class ActivateUserModel
{
    [Key]
    public int Id { get; set; }
    public bool Active { get; set; }
}

Activateと呼ばれる厳密に型指定されたビューがあります。これはActivateUserModelを取得して表示し(これはチェックボックスであり、Idに対して非表示になっています)、次に[HttpPost] Activateアクションを実行して、ActivateUserModelをキャプチャし、それをUserエンティティに変換して、変更をデータベースに保存します。 。これはPOSTアクションです。

    [HttpPost]
    public ActionResult Activate(ActivateUserModel model)
    {
        if (ModelState.IsValid)
        {
            User user = new User { Id = model.Id, Active = model.Active };
            db.Users.Attach(user);
            db.Entry(user).Property(u => u.Active).IsModified = true;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(model);
    }

これは魅力のように機能します。SQL Serverに対して発行されたクエリを監視しました。ロードするのは、IDとアクティブのペアだけです。更新するのも、IDに基づくアクティブな変更です。

しかし、私はそれがどのように見えるかというコードが好きではありません。50個のプロパティを持つエンティティと25個のビューがあるとします。IsModified=trueと言う25行を書きたくありません。

だから私の質問は:反射ベースの方法を掘り下げることなく、同じことをするためのより効率的な方法はありますか?任意のビューモデルからエンティティにデータを転送してから、それらのプロパティのみを保存したいと思います。

事前に回答ありがとうございます、私は質問を十分に明確にしたことを願っています:)

4

1 に答える 1

6

あなたはこのようにそれを行うことができます:

[HttpPost]
public ActionResult Activate(ActivateUserModel model)
{
    if (ModelState.IsValid)
    {
        User user = db.Users.Single(u => u.Id == model.Id);
        db.Entry(user).CurrentValues.SetValues(model);
        db.SaveChanges();

        return RedirectToAction("Index");
    }
    return View(model);
}

db.Entry(user).CurrentValues.SetValues(model)userのプロパティがに同じ名前で存在するかどうかを確認し、存在するmodel場合は、プロパティ値をからmodelにコピーしますuser。そうでない場合は、プロパティ値はuser変更されません。

これがリフレクションベースではないのではないかと思います。ただし、上記のコードは簡単な方法であり、シナリオを正確にサポートするように設計されています。

編集

上記のコードはuser、プロパティを含むエンティティ全体をロードしPhotoます。IsModified潜在的に大きなバイナリフィールドをロードしたくない場合は、トリック以外の戦略でこの問題を解決することをお勧めします。Entity Frameworkを使用した更新は、変更の追跡に強く依存しているため、エンティティ全体を読み込む必要があります。Modifiedこれを回避し、特定のプロパティのフラグを手動で設定しようとすると、コードが複雑になります。

データベースからエンティティをフェッチするときに、単一のスカラープロパティがロードされるのを除外できないことをおそらくご存知でしょう。プロパティをとプロパティを持つPhoto新しいエンティティに移動し、ナビゲーションプロパティをクラスに配置することをお勧めします。次に、写真を一緒にロードするかどうかを、怠惰な、熱心な、または明示的なロードを介して決定できます。UserPhotoIdPhotoUserPhotoUserUser

とを別のテーブルに格納する場合はUser、との間で1対1のマッピングを作成できます。または、テーブルに列を残して、テーブル分割を介して2つのエンティティをこの同じテーブルにマップすることもできます。UserPhotoUserPhotoPhotoUserUserUserPhoto

編集2

アプローチが「不必要なもの」をロードするというあなたのコメントを参照してください。私は次のことを言及するのを忘れました:

実際、上記のコードでは、データベースからエンティティをロードするためのコストがあります。ただし、EFを使用modelしてロードされたエンティティに適用すると、データベース内の元の値と比較して実際に変更されたプロパティのみがマークされます。生成されたUPDATEステートメントには、これらの列のみが含まれます。したがって、UPDATEステートメントを作成するためのコストは最小限に抑えられます。userSetValues(model)Modified

エンティティをロードしたくない場合は、データベース内の現在の列の値がわからず、実際に何が変更されたかもわかりません。唯一のチャンスは、データベースの行が正しく更新されるように、すべてのプロパティのUPDATEを強制することです。IsModifiedたとえば、ViewModelに含まれる25個のプロパティすべてをに設定する必要がありましたtrue。生成されたSLQUPDATEステートメントには、25列すべてが含まれます。したがって、UPDATEステートメントは潜在的にはるかに高価であり、(あなたの言葉を借りて)不必要なことをします。

于 2012-04-29T12:41:02.047 に答える