【问题标题】:Model Binding with Parent/Child Relationship与父/子关系的模型绑定
【发布时间】:2011-08-07 00:59:38
【问题描述】:

我确定之前已经回答过这个问题,但我在过去三个小时里一直在寻找可接受的解决方案,但一直找不到任何东西,所以对于我确定是重复的问题,我深表歉意。

我有两个域对象,Player 和 Position。玩家有一个位置。我的域对象是使用 NHibernate 绑定到我的数据库的 POCO。我有一个需要播放器的添加操作,所以我使用的是内置模型绑定。在我看来,我有一个下拉列表,可让用户选择玩家的位置。下拉列表的值是职位的 Id。除了我的 Position 对象验证失败 (ModelState.IsValid) 之外,一切都被正确填充,因为在模型绑定时它只有一个 Id,没有其他必需的属性。

使用 ASP.NET MVC 2 解决此问题的首选解决方案是什么?

我尝试过的解决方案...

  1. 在我的控制器的 Add 操作中调用 ModelState.IsValid 之前,根据 Id 从数据库中获取位置。我无法让模型再次运行验证,因此 ModelState.IsValid 总是返回 false。
  2. 创建一个继承自默认绑定器的自定义 ModelBinder,并在调用基本绑定器后从数据库中获取位置。 ModelBinder 似乎正在做验证,所以如果我使用默认活页夹中的任何东西,我就会被冲洗掉。这意味着我必须完全滚动自己的活页夹并从表单中获取每个值......对于这样一个常见的用例来说,这似乎真的是错误且效率低下的。

我认为可能可行的解决方案,但我不知道该怎么做...

  1. 在 Player 中使用 Position 类时关闭验证。
  2. 编写自定义 ModelBinder 为大多数属性绑定利用默认绑定器,但让我在默认绑定器运行验证之前从数据库中获取位置。

那么,你们其他人是如何解决这个问题的呢?

谢谢,

附:在我看来,仅针对这种情况在 Player 上设置 PositionId 并不是一个好的解决方案。必须以更优雅的方式解决。

【问题讨论】:

    标签: c# asp.net-mvc nhibernate


    【解决方案1】:

    不仅针对这个特定问题,而且总的来说,我会创建一个单独的 ViewModel,而不是让视图拥有域模型。因此,在您的情况下,您不需要将域模型(并获取您不需要的东西)过度暴露给视图,但是,关闭验证很可能是最糟糕的解决方案

    【讨论】:

    • 所以,基本上我必须为我的所有观点制作 DTO。对我来说似乎效率很低。其他人有解决方案吗?
    • 我第一次听说时也有同样的想法。但是测试和关注点分离的积极方面似乎总是获胜。对我来说,创建另一个模型通常比调试特定领域的东西或创建自定义模型绑定器更快。
    【解决方案2】:

    创建一个自定义 ModelBinder,它可以为您验证这个特殊情况。

    【讨论】:

    • Xeb,我无法找到有关自定义 ModelBinders 的有用信息,您认为您可以直接指出我的正确位置吗?
    【解决方案3】:

    我在我的一个应用程序中遇到了同样的问题。我通过创建一个继承自 DefaultModelBinder 的自定义 IModelBinder 并使用我正在绑定的特定类型注册它来解决它。

    诀窍是使用session.Load<>,它只创建对实体的类型化引用而不查询数据库。 (Read more about session.Load here in Ayende's blog)

    /// <summary>
        /// Base for binding references. Usually displayed in dropdowns
        /// </summary>
        public class ReferenceBinder<T> : DefaultModelBinder
            where T : class
        {
    
            private readonly ISession session;
    
            public ReferenceBinder(ISession session)
            {
                this.session = session;
            }
    
    
            public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
    
                var idName = CreateSubPropertyName(bindingContext.ModelName, "ID");
    
                ValueProviderResult result = bindingContext.ValueProvider.GetValue(idName);
    
                int value;
                return (int.TryParse(result.AttemptedValue, out value)) ?  this.session.Load<T>(value) : null;
    
            }
    
    
        }
    

    在您的示例中,您将在 Global.asax 中执行类似的操作:

    ModelBinders.Binders.Add(typeof(Position), new ReferenceBinder<Position>(<pass your current session implementation or use DI/IoC>));
    

    (我正在使用 Castle Windsor 创建实际的活页夹并在我的实现中填充会话)

    假设你的领域模型是:

            public class Player {
                public virtual Position Position {get;set;}
            }
    
            public class Position {
                public virtual int ID {get;private set;}
            }
    

    你的回发看起来像这样:

    "Position.ID" = <id from dropdown>
    

    编辑:您可能应该使用具有专用视图模型的 DTO 方法。我后来才知道这一点。 Have a look here for a quick start.

    【讨论】:

      猜你喜欢
      • 2015-01-09
      • 2018-01-28
      • 1970-01-01
      • 2013-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多