1

Magazine と Author (MM リレーションシップ) の 2 つのオブジェクトがあります。

public partial class MAGAZINE
    {
        public MAGAZINE()
        {
            this.AUTHORs = new HashSet<AUTHOR>();
        }

        public long REF_ID { get; set; }
        public string NOTES { get; set; }
        public string TITLE { get; set; }

        public virtual REFERENCE REFERENCE { get; set; }
        public virtual ICollection<AUTHOR> AUTHORs { get; set; }
    }

public partial class AUTHOR
{
    public AUTHOR()
    {  
         this.MAGAZINEs = new HashSet<MAGAZINE>();
    }

            public long AUTHOR_ID { get; set; }
            public string FULL_NAME { get; set; }

            public virtual ICollection<MAGAZINE> MAGAZINEs { get; set; }
        }
}

私の問題は、雑誌に対して著者の数を更新できないように見えることです。たとえば、「Smith, P.」という著者が 1 人いる場合などです。既に雑誌に対して保存されている場合、"Jones, D." という名前の別の名前を追加できますが、Edit コントローラーにポストバックした後も、著者の数は 1 と表示されます。つまり、"Smith, PH" です。

著者の数を親エンティティ(雑誌)にモデルバインドすることに成功したことに注意してください。カスタムモデルバインダーを使用して著者を取得し、マガジンにバインドします(私は思います)が、まだ更新されていないようですちゃんと。

モデルを更新するための私のコードは単純明快で、前後の変数値を示しています。

public ActionResult Edit(long id)
    {
        MAGAZINE magazine = db.MAGAZINEs.Find(id);
        return View(magazine);
    }

そして、ここに編集前/更新中の変数があります -

ここに画像の説明を入力

[HttpPost]
public ActionResult Edit(MAGAZINE magazine)
   {
       if (ModelState.IsValid)
       {
           db.Entry(magazine).State = EntityState.Modified;
           db.SaveChanges();
           return RedirectToAction("Index");
       }

       return View(magazine);
   }

...そして、新しい作成者が追加された後の変数は次のとおりです...

私は、著者エンティティがどの雑誌にも拘束されていないことをポスト編集で示しているのではないかと疑っています。これが、雑誌エンティティに更新されていない理由だと推測しています。同じ雑誌エンティティ - 著者のカスタム モデル バインダーと関係があるのではないかと思います。

誰でもこの問題について助けることができますか?

完全を期すために - AuthorModelBinder クラスも含めました -

public class AuthorModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (values != null)
            {
                // We have specified asterisk (*) as a token delimiter. So
                // the ids will be separated by *. For example "2*3*5"
                var ids = values.AttemptedValue.Split('*');

                List<int> validIds = new List<int>();
                foreach (string id in ids)
                {
                    int successInt;
                    if (int.TryParse(id, out successInt))
                    {
                        validIds.Add(successInt);
                    }
                    else
                    {
                        //Make a new author
                        AUTHOR author = new AUTHOR();
                        author.FULL_NAME = id.Replace("\'", "").Trim();
                        using (RefmanEntities db = new RefmanEntities())
                        {
                            db.AUTHORs.Add(author);
                            db.SaveChanges();
                            validIds.Add((int)author.AUTHOR_ID);
                        }
                    }
                }

                 //Now that we have the selected ids we could fetch the corresponding
                 //authors from our datasource
                var authors = AuthorController.GetAllAuthors().Where(x => validIds.Contains((int)x.Key)).Select(x => new AUTHOR
                {
                    AUTHOR_ID = x.Key,
                    FULL_NAME = x.Value
                }).ToList();
                return authors;
            }
            return Enumerable.Empty<AUTHOR>();
        }
    }

ここに画像の説明を入力

4

2 に答える 2

2

MVC/Nhibernate を使用してブログを開発したときに、非常によく似たシナリオに直面しました。エンティティはPostTagです。

私もこのような編集アクションを持っていましたが、

public ActionResult Edit(Post post)
{
  if (ModelState.IsValid)
  {
       repo.EditPost(post);
       ...
  }
  ...
}

しかし、あなたとは異なり、私はPostnot用のカスタム モデル バインダーを作成しましたTag。カスタムでは、PostModelBinder私はあなたがそこでやっていることとほとんど同じことをしています (しかし、私はTagあなたが s に対して行っているように新しい s を作成していませんAuthor)。基本的にPost、POSTed フォームからすべてのプロパティを取り込みTag、データベースから ID のすべての を取得する新しいインスタンスを作成しました。Tag.ではなくデータベースから sのみをフェッチしたことに注意してくださいPost

ModelBinderを作成して確認することをお勧めしますMagazine。また、コントローラーから直接呼び出すのではなく、リポジトリ パターンを使用することをお勧めします。

アップデート:

Postモデル バインダーの完全なソース コードは次のとおりです。

namespace PrideParrot.Web.Controllers.ModelBinders
{
  [ValidateInput(false)]
  public class PostBinder : IModelBinder
  {
    private IRepository repo;

    public PostBinder(IRepository repo)
    {
      this.repo = repo;
    }

    #region IModelBinder Members

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      HttpRequestBase request = controllerContext.HttpContext.Request;

      // retrieving the posted values.
      string oper = request.Form.Get("oper"),
               idStr = request.Form.Get("Id"),
               heading = request.Form.Get("Heading"),
               description = request.Form.Get("Description"),
               tagsStr = request.Form.Get("Tags"),
               postTypeIdStr = request.Form.Get("PostType"),
               postedDateStr = request.Form.Get("PostedDate"),
               isPublishedStr = request.Form.Get("Published"),
               fileName = request.Form.Get("FileName"),
               serialNoStr = request.Form.Get("SerialNo"),
               metaTags = request.Form.Get("MetaTags"),
               metaDescription = request.Form.Get("MetaDescription"),
               themeIdStr = request.Form.Get("Theme");

      // initializing to default values.
      int id = 0, serialNo = 0;
      DateTime postedDate = DateTime.UtcNow;
      DateTime? modifiedDate = DateTime.UtcNow;
      postedDate.AddMilliseconds(-postedDate.Millisecond);
      modifiedDate.Value.AddMilliseconds(-modifiedDate.Value.Millisecond);

      /*if operation is not specified throw exception. 
        operation should be either'add' or 'edit'*/
      if (string.IsNullOrEmpty(oper))
        throw new Exception("Operation not specified");

      // if there is no 'id' in edit operation add error to model.
      if (string.IsNullOrEmpty(idStr) || idStr.Equals("_empty"))
      {
        if (oper.Equals("edit"))
          bindingContext.ModelState.AddModelError("Id", "Id is empty");
      }
      else
        id = int.Parse(idStr);

      // check if heading is not empty.
      if (string.IsNullOrEmpty(heading))
        bindingContext.ModelState.AddModelError("Heading", "Heading: Field is required");
      else if (heading.Length > 500)
        bindingContext.ModelState.AddModelError("HeadingLength", "Heading: Length should not be greater than 500 characters");

      // check if description is not empty.
      if (string.IsNullOrEmpty(description))
        bindingContext.ModelState.AddModelError("Description", "Description: Field is required");

      // check if tags is not empty.
      if (string.IsNullOrEmpty(metaTags))
        bindingContext.ModelState.AddModelError("Tags", "Tags: Field is required");
      else if (metaTags.Length > 500)
        bindingContext.ModelState.AddModelError("TagsLength", "Tags: Length should not be greater than 500 characters");

      // check if metadescription is not empty.
      if (string.IsNullOrEmpty(metaTags))
        bindingContext.ModelState.AddModelError("MetaDescription", "Meta Description: Field is required");
      else if (metaTags.Length > 500)
        bindingContext.ModelState.AddModelError("MetaDescription", "Meta Description: Length should not be greater than 500 characters");

      // check if file name is not empty.
      if (string.IsNullOrEmpty(fileName))
        bindingContext.ModelState.AddModelError("FileName", "File Name: Field is required");
      else if (fileName.Length > 50)
        bindingContext.ModelState.AddModelError("FileNameLength", "FileName: Length should not be greater than 50 characters");

      bool isPublished = !string.IsNullOrEmpty(isPublishedStr) ? Convert.ToBoolean(isPublishedStr.ToString()) : false;

      //** TAGS
      var tags = new List<PostTag>();
      var tagIds = tagsStr.Split(',');
      foreach (var tagId in tagIds)
      {
        tags.Add(repo.PostTag(int.Parse(tagId)));
      }
      if(tags.Count == 0)
        bindingContext.ModelState.AddModelError("Tags", "Tags: The Post should have atleast one tag");

      // retrieving the post type from repository.
      int postTypeId = !string.IsNullOrEmpty(postTypeIdStr) ? int.Parse(postTypeIdStr) : 0;
      var postType = repo.PostType(postTypeId);
      if (postType == null)
        bindingContext.ModelState.AddModelError("PostType", "Post Type is null");

      Theme theme = null;
      if (!string.IsNullOrEmpty(themeIdStr))
        theme = repo.Theme(int.Parse(themeIdStr));

      // serial no
      if (oper.Equals("edit"))
      {
        if (string.IsNullOrEmpty(serialNoStr))
          bindingContext.ModelState.AddModelError("SerialNo", "Serial No is empty");
        else
          serialNo = int.Parse(serialNoStr);
      }
      else
      {
        serialNo = repo.TotalPosts(false) + 1;
      }

      // check if commented date is not empty in edit.
      if (string.IsNullOrEmpty(postedDateStr))
      {
        if (oper.Equals("edit"))
          bindingContext.ModelState.AddModelError("PostedDate", "Posted Date is empty");
      }
      else
        postedDate = Convert.ToDateTime(postedDateStr.ToString());

      // CREATE NEW POST INSTANCE
      return new Post
      {
        Id = id,
        Heading = heading,
        Description = description,
        MetaTags = metaTags,
        MetaDescription = metaDescription,
        Tags = tags,
        PostType = postType,
        PostedDate = postedDate,
        ModifiedDate = oper.Equals("edit") ? modifiedDate : null,
        Published = isPublished,
        FileName = fileName,
        SerialNo = serialNo,
        Theme = theme
      };
    }

    #endregion
  }
}
于 2012-10-03T15:57:37.530 に答える
2

この行db.Entry(magazine).State = EntityState.Modified;は、マガジン エンティティが変更されたことを EF に通知するだけです。関係については何も言いません。Attachオブジェクト グラフ内のすべてのエンティティを呼び出すと、Unchanged状態がアタッチされ、それぞれを個別に処理する必要があります。多対多のリレーションの場合はさらに悪いことに、リレーション自体も処理する必要があります( DbContext APIでリレーションの状態を変更することはできません)。

私はこの問題と切断されたアプリでの設計に多くの時間を費やしました。そして、次の 3 つの一般的なアプローチがあります。

  • 変更されたものと削除されたものを見つけるために、エンティティに追加情報を送信します (はい、削除されたアイテムまたは関係も追跡する必要があります)。次に、オブジェクト グラフ内のすべてのエンティティと関係の状態を手動で設定します。
  • 現時点で持っているデータを使用するだけですが、それらをコンテキストに添付する代わりに、現在の雑誌と必要なすべての著者をロードし、ロードされたエンティティでそれらの変更を再構築します。
  • これをまったく行わず、代わりに軽量の AJAX 呼び出しを使用して、すべての作成者を追加または削除します。これは、多くの複雑な UI に共通することがわかりました。
于 2012-09-28T16:38:28.763 に答える