11

要するに:編集ビュー内のモデルのすべてのフィールドを含める必要なく、DB エントリを正常に編集するにはどうすればよいですか?

UPDATE
したがって、DBにアイテム(記事)があります。記事を編集したい。私が編集する記事には、多くのプロパティ (Id、CreatedBy、DateCreated、Title、Body) があります。これらのプロパティの一部 (Id、CreatedBy、DateCreated など) は変更する必要はありません。したがって、編集ビューでは、変更可能なフィールド (タイトル、本文など) の入力フィールドのみが必要です。このような編集ビューを実装すると、モデル バインディングが失敗します。入力を提供しなかったフィールドはすべて、「デフォルト」値に設定されます (DateCreated が 01/01/0001 12:00:00am に設定されるなど)。もし私がするならすべてのフィールドに入力を提供すると、すべてが正常に機能し、記事が期待どおりに編集されます。「編集ビューで入力フィールドが指定されていない場合、システムはフィールドに誤ったデータを入力する」など、必ずしも「モデル バインディングが失敗する」と言うのが正しいかどうかはわかりません。

コントローラーの Edit メソッドが呼び出されたときに、DateCreated などのフィールドが正しく入力され、設定されないように、編集できる/必要なフィールドの入力フィールドのみを提供する必要があるような方法で編集ビューを作成するにはどうすればよいですか?デフォルトの間違った値に?現在の編集方法は次のとおりです。

    [HttpPost]
    public ActionResult Edit(Article article)
    {
        // Get a list of categories for dropdownlist
        ViewBag.Categories = GetDropDownList();


        if (article.CreatedBy == (string)CurrentSession.SamAccountName || (bool)CurrentSession.IsAdmin)
        {                
            if (ModelState.IsValid)
            {
                article.LastUpdatedBy = MyHelpers.SessionBag.Current.SamAccountName;
                article.LastUpdated = DateTime.Now;
                article.Body = Sanitizer.GetSafeHtmlFragment(article.Body);

                _db.Entry(article).State = EntityState.Modified;
                _db.SaveChanges();
                return RedirectToAction("Index", "Home");
            }
            return View(article);
        }

        // User not allowed to edit
        return RedirectToAction("Index", "Home");   
    }

そして、それが役立つ場合は編集ビュー:

. . .
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)

<fieldset>
    <legend>Article</legend>

    <p>
        <input type="submit" value="Save" /> | @Html.ActionLink("Back to List", "Index")
    </p>

    @Html.Action("Details", "Article", new { id = Model.Id })

    @Html.HiddenFor(model => model.CreatedBy)
    @Html.HiddenFor(model => model.DateCreated)

    <div class="editor-field">
        <span>
            @Html.LabelFor(model => model.Type)
            @Html.DropDownListFor(model => model.Type, (SelectList)ViewBag.Categories)
            @Html.ValidationMessageFor(model => model.Type)
        </span>
        <span>
            @Html.LabelFor(model => model.Active)
            @Html.CheckBoxFor(model => model.Active)
            @Html.ValidationMessageFor(model => model.Active)
        </span>
        <span>
            @Html.LabelFor(model => model.Stickied)
            @Html.CheckBoxFor(model => model.Stickied)
            @Html.ValidationMessageFor(model => model.Stickied)
        </span>            
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.Title)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Title)
        @Html.ValidationMessageFor(model => model.Title)
    </div>
    <div class="editor-label">
        @Html.LabelFor(model => model.Body)
    </div>
    <div class="editor-field">
        @* We set the id of the TextArea to 'CKeditor' for the CKeditor script to change the TextArea into a WYSIWYG editor. *@
        @Html.TextAreaFor(model => model.Body, new { id = "CKeditor", @class = "text-editor" })
        @Html.ValidationMessageFor(model => model.Body)
    </div>
</fieldset>
. . .

これら 2 つの入力を省略した場合:

@Html.HiddenFor(model => model.CreatedBy)
@Html.HiddenFor(model => model.DateCreated)

Edit メソッドが呼び出されると、既定値に設定されます。CreatedBy はNullに設定され、Created は01/01/0001 12:00:00am に設定されます

DB で現在設定されている値に設定されていないのはなぜですか?

4

5 に答える 5

11

さらに調査を重ねた結果、ViewModel プロセスを支援するいくつかのツールに出会いました。1 つは AutoMapper で、もう 1 つは InjectValues です。私が InjectValues を使用した主な理由は、オブジェクトを「平坦化」する (オブジェクト a -> b をマップする) だけでなく、それらを「非平坦化」する (オブジェクト b -> a をマップする) こともできるためです。 box - DB 内の値を更新するために必要なこと。

ここで、Article モデルとそのすべてのプロパティをビューに送信する代わりに、次のプロパティのみを含む ArticleViewModel を作成しました。

public class ArticleViewModel
{
    public int Id { get; set; }

    [MaxLength(15)]
    public string Type { get; set; }

    public bool Active { get; set; }
    public bool Stickied { get; set; }

    [Required]
    [MaxLength(200)]
    public string Title { get; set; }

    [Required]
    [AllowHtml]
    public string Body { get; set; }
}

記事を作成するとき、Article オブジェクト (すべてのプロパティを含む) を送信する代わりに、View に「より単純な」モデル (私の ArticleViewModel) を送信します。

//
// GET: /Article/Create

public ActionResult Create()
{
    return View(new ArticleViewModel());
}

POST メソッドでは、View に送信した ViewModel を取得し、そのデータを使用して DB に新しい記事を作成します。これを行うには、ViewModel を Article オブジェクトに「非フラット化」します。

//
// POST: /Article/Create
public ActionResult Create(ArticleViewModel articleViewModel)
{
    Article article = new Article();              // Create new Article object
    article.InjectFrom(articleViewModel);         // unflatten data from ViewModel into article 

    // Fill in the missing pieces
    article.CreatedBy = CurrentSession.SamAccountName;   // Get current logged-in user
    article.DateCreated = DateTime.Now;

    if (ModelState.IsValid)
    {            
        _db.Articles.Add(article);
        _db.SaveChanges();
        return RedirectToAction("Index", "Home");
    }

    ViewBag.Categories = GetDropDownList();
    return View(articleViewModel);            
}

埋められた「不足している部分」は、ビューで設定したくない記事のプロパティであり、編集ビューで更新する必要もありません (さらに言えば、まったく更新する必要もありません)。

Edit メソッドはほとんど同じですが、新しい ViewModel を View に送信する代わりに、DB からのデータが事前に入力された ViewModel を送信します。これを行うには、DB から Article を取得し、データを ViewModel にフラット化します。まず、GET メソッド:

    //
    // GET: /Article/Edit/5
    public ActionResult Edit(int id)
    {
        var article = _db.Articles.Single(r => r.Id == id);     // Retrieve the Article to edit
        ArticleViewModel viewModel = new ArticleViewModel();    // Create new ArticleViewModel to send to the view
        viewModel.InjectFrom(article);                          // Inject ArticleViewModel with data from DB for the Article to be edited.

        return View(viewModel);
    }

POST メソッドでは、View から送信されたデータを取得し、DB に保存されている Article を更新します。これを行うには、Create メソッドの POST バージョンで行ったのと同じように、ViewModel を Article オブジェクトに「非フラット化」することにより、フラット化プロセスを逆にします。

    //
    // POST: /Article/Edit/5
    [HttpPost]
    public ActionResult Edit(ArticleViewModel viewModel)
    {
        var article = _db.Articles.Single(r => r.Id == viewModel.Id);   // Grab the Article from the DB to update

        article.InjectFrom(viewModel);      // Inject updated values from the viewModel into the Article stored in the DB

        // Fill in missing pieces
        article.LastUpdatedBy = MyHelpers.SessionBag.Current.SamAccountName;
        article.LastUpdated = DateTime.Now;

        if (ModelState.IsValid)
        {
            _db.Entry(article).State = EntityState.Modified;
            _db.SaveChanges();
            return RedirectToAction("Index", "Home");
        }

        return View(viewModel);    // Something went wrong
    }

また、Article の代わりに ArticleViewModel を予期するように、厳密に型指定された Create ビューと Edit ビューを変更する必要があります。

@model ProjectName.ViewModels.ArticleViewModel

以上です!

つまり、ViewModel を実装して、モデルの一部だけをビューに渡すことができます。次に、それらの部分だけを更新し、ViewModel を Controller に戻し、ViewModel 内の更新された情報を使用して実際のモデルを更新できます。

于 2012-06-11T17:31:40.110 に答える
2

モデルの例を見る:

public class ArticleViewModel {
    [Required]
    public string Title { get; set; }

    public string Content { get; set; }
}

バインディングの例

public ActionResult Edit(int id, ArticleViewModel article) {
    var existingArticle = db.Articles.Where(a => a.Id == id).First();
    existingArticle.Title = article.Title;
    existingArticle.Content = article.Content;
    db.SaveChanges();
}

これは単純な例ですが、ModelStateを調べて、モデルにエラーがないかどうかを確認し、承認を確認して、このコードをコントローラーからサービスクラスに移動する必要がありますが、これは別のレッスンです。

これは修正された編集方法です。

[HttpPost]
public ActionResult Edit(Article article)
{
    // Get a list of categories for dropdownlist
    ViewBag.Categories = GetDropDownList();


    if (article.CreatedBy == (string)CurrentSession.SamAccountName || (bool)CurrentSession.IsAdmin)
    {                
        if (ModelState.IsValid)
        {
            var existingArticle = _db.Articles.First(a => a.Id = article.Id);
            existingArticle.LastUpdatedBy = MyHelpers.SessionBag.Current.SamAccountName;
            existingArticle.LastUpdated = DateTime.Now;
            existingArticle.Body = Sanitizer.GetSafeHtmlFragment(article.Body);
            existingArticle.Stickied = article.Stickied;

            _db.SaveChanges();
            return RedirectToAction("Index", "Home");
        }
        return View(article);
    }

    // User not allowed to edit
    return RedirectToAction("Index", "Home");   
}
于 2012-06-07T17:39:46.687 に答える
2

ビューモデルを使わない別の良い方法

// POST: /Article/Edit/5
[HttpPost]
public ActionResult Edit(Article article0)
{
    var article = _db.Articles.Single(r => r.Id == viewModel.Id);   // Grab the Article from the DB to update

   article.Stickied = article0.Stickied;

    // Fill in missing pieces
    article.LastUpdatedBy = MyHelpers.SessionBag.Current.SamAccountName;
    article.LastUpdated = DateTime.Now;

    if (ModelState.IsValid)
    {
       _db.Entry(article0).State = EntityState.Unchanged;
        _db.Entry(article).State = EntityState.Modified;
        _db.SaveChanges();
        return RedirectToAction("Index", "Home");
    }

    return View(article0);    // Something went wrong
}
于 2015-08-02T06:59:17.173 に答える
0

答えに加えて、AutoMapper を使用して平坦化を解除することもできます。 AutoMapper を使用して DTO をフラット化解除する

于 2015-08-28T10:19:13.907 に答える
0

ViewModelを使用します。

この問題の解決策を見つけるための継続的な調査を通じて、「ViewModels」と呼ばれるものを使用することが道だと思います。Jimmy Bogard の投稿で説明されているように、ViewModel は「単一のエンティティからの情報のスライスを表示する」方法です。

asp.net-mvc-view-model-patternsは、私を正しい軌道に乗せました。ViewModel の概念をさらに理解するために、著者が投稿した外部リソースのいくつかをまだチェックしています (Jimmy によるブログ投稿はその 1 つです)。

于 2012-06-08T14:47:43.520 に答える