1

我有这两个对象 - 杂志和作者(MM 关系):

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; }
        }
}

我的问题是我似乎无法根据杂志更新作者的数量,例如,如果我有 1 位作者名为“Smith, P”。已经存储在一本杂志上,我可以添加另一个名为“Jones, D.”的文章,但在发回编辑控制器后,作者的数量仍然显示为 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

此行db.Entry(magazine).State = EntityState.Modified;仅告诉 EF 杂志实体已更改。它没有提到关系。如果您调用Attach对象图中的所有实体都附加在Unchanged状态中,您必须分别处理它们中的每一个。更糟糕的是,在多对多关系的情况下,您还必须处理关系本身(并且无法更改 DbContext API 中的关系状态)。

我花了很多时间来解决这个问题并在断开连接的应用程序中进行设计。并且有三种通用方法:

  • 您将向您的实体发送附加信息,以查找已更改和已删除的内容(是的,您还需要跟踪已删除的项目或关系)。然后,您将手动设置对象图中每个实体和关系的状态。
  • 您将只使用您目前拥有的数据,而不是将它们附加到上下文中,您将加载当前杂志和您需要的每个作者,并在这些加载的实体上重建这些更改。
  • 您根本不会这样做,而是使用轻量级 AJAX 调用来添加或删除每个作者。我发现这在许多复杂的 UI 中很常见。
于 2012-09-28T16:38:28.763 回答
2

当我使用 MVC/Nhibernate 开发博客并且实体是PostTag.

我也有类似这样的编辑操作,

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

但与您不同的是,我为Postnot创建了一个自定义模型绑定器Tag。在自定义中PostModelBinder,我正在做与您在那里所做的几乎相同的事情(但我并没有Tag像您为 s 所做的那样创建新的Authors)。基本上,我创建了一个新Post实例,从 POST 表单中填充它的所有属性,并Tag从数据库中获取 id 的所有 s。请注意,我只Tag从数据库中获取 s 而不是Post.

我可能会建议您创建一个ModelBinderMagazine检查一下。此外,最好使用存储库模式而不是直接从控制器进行调用。

更新:

这是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 回答