父/子关系的模型绑定

时间:2010-03-27 06:18:09

标签: c# asp.net-mvc nhibernate

我确信之前已经回答了这个问题,但是我花了最后三个小时寻找一个可以接受的解决方案并且找不到任何东西,所以我为我确定重复的事情道歉。

我有两个域对象,播放器和位置。玩家有位置。我的域对象是使用NHibernate绑定到我的数据库的POCO。我有一个带有播放器的Add动作,所以我使用了内置的模型绑定。在我的视图中,我有一个下拉列表,允许用户选择播放器的位置。下拉列表的值是位置的Id。除了我的Position对象未通过验证(ModelState.IsValid)之外,所有内容都被正确填充,因为在模型绑定时它只有一个Id而且没有其它所需的属性。

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

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

  1. 在我的控制器的Add操作中调用ModelState.IsValid之前,根据Id从数据库中获取位置。我无法让模型再次运行验证,因此ModelState.IsValid始终返回false。
  2. 创建一个继承自默认绑定器的自定义ModelBinder,并在调用基本绑定器后从数据库中获取位置。 ModelBinder似乎正在进行验证,所以如果我使用默认绑定器中的任何东西,我就会使用它。这意味着我必须完全滚动自己的绑定器并从表单中获取每个值......对于这样一个常见的用例来说,这似乎是错误和低效的。
  3. 我觉得可能有用的解决方案,我只是想不通怎么做......

    1. 在播放器中使用时关闭Position类的验证。
    2. 编写一个自定义的ModelBinder利用大多数属性绑定的默认绑定器,但是让我在默认绑定器运行验证之前从数据库中获取位置。
    3. 那么,你们其他人如何解决这个问题?

      谢谢,

      P.S。在我看来,仅针对这种情况在Player上使用PositionId并不是一个好的解决方案。必须以更优雅的方式解决。

3 个答案:

答案 0 :(得分:3)

不仅是这个特定问题,而且一般来说,我会创建一个单独的ViewModel,而不是让视图具有域模型。因此,在您的情况下,您不需要将您的域模型过度曝光(以及获取您不需要的内容)到视图中,然而,关闭验证很可能是最糟糕的解决方案

答案 1 :(得分:0)

创建一个可以为您验证此特定情况的自定义ModelBinder。

答案 2 :(得分:0)

我的一个应用程序中遇到了同样的问题。我通过创建一个继承自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