代码之家  ›  专栏  ›  技术社区  ›  roryf

ASP.NET MVC模型绑定外键关系

  •  7
  • roryf  · 技术社区  · 15 年前

    是否可以将模型上的外键关系绑定到表单输入?

    比如说,我和他们之间有一对多的关系 Car Manufacturer . 我想要一张更新的表格 汽车 包括用于设置的选择输入 制造商

    我的操作方法签名如下所示:

    public JsonResult Save(int id, [Bind(Include="Name, Description, Manufacturer")]Car car)
    

    int . 正确设置名称和描述,但不设置制造商,这是有意义的,因为model binder不知道PK字段是什么。那是不是意味着我必须写一个习惯 IModelBinder Controller 构造器。

    3 回复  |  直到 15 年前
        1
  •  6
  •   Dave Thieben    14 年前

    这是我的观点-这是一个自定义模型绑定器,当被要求获取PropertyValue时,它会查看该属性是否是我的模型程序集中的对象,并具有IRepository<&燃气轮机;在我的NInject IKernel注册。如果它可以从Ninject获取IRepository,它将使用它来检索外键对象。

    public class ForeignKeyModelBinder : System.Web.Mvc.DefaultModelBinder
    {
        private IKernel serviceLocator;
    
        public ForeignKeyModelBinder( IKernel serviceLocator )
        {
            Check.Require( serviceLocator, "IKernel is required" );
            this.serviceLocator = serviceLocator;
        }
    
        /// <summary>
        /// if the property type being asked for has a IRepository registered in the service locator,
        /// use that to retrieve the instance.  if not, use the default behavior.
        /// </summary>
        protected override object GetPropertyValue( ControllerContext controllerContext, ModelBindingContext bindingContext,
            PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder )
        {
            var submittedValue = bindingContext.ValueProvider.GetValue( bindingContext.ModelName );
            if ( submittedValue == null )
            {
                string fullPropertyKey = CreateSubPropertyName( bindingContext.ModelName, "Id" );
                submittedValue = bindingContext.ValueProvider.GetValue( fullPropertyKey );
            }
    
            if ( submittedValue != null )
            {
                var value = TryGetFromRepository( submittedValue.AttemptedValue, propertyDescriptor.PropertyType );
    
                if ( value != null )
                    return value;
            }
    
            return base.GetPropertyValue( controllerContext, bindingContext, propertyDescriptor, propertyBinder );
        }
    
        protected override object CreateModel( ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType )
        {
            string fullPropertyKey = CreateSubPropertyName( bindingContext.ModelName, "Id" );
            var submittedValue = bindingContext.ValueProvider.GetValue( fullPropertyKey );
            if ( submittedValue != null )
            {
                var value = TryGetFromRepository( submittedValue.AttemptedValue, modelType );
    
                if ( value != null )
                    return value;
            }
    
            return base.CreateModel( controllerContext, bindingContext, modelType );
        }
    
        private object TryGetFromRepository( string key, Type propertyType )
        {
            if ( CheckRepository( propertyType ) && !string.IsNullOrEmpty( key ) )
            {
                Type genericRepositoryType = typeof( IRepository<> );
                Type specificRepositoryType = genericRepositoryType.MakeGenericType( propertyType );
    
                var repository = serviceLocator.TryGet( specificRepositoryType );
                int id = 0;
    #if DEBUG
                Check.Require( repository, "{0} is not available for use in binding".FormatWith( specificRepositoryType.FullName ) );
    #endif
                if ( repository != null && Int32.TryParse( key, out id ) )
                {
                    return repository.InvokeMethod( "GetById", id );
                }
            }
    
            return null;
        }
    
        /// <summary>
        /// perform simple check to see if we should even bother looking for a repository
        /// </summary>
        private bool CheckRepository( Type propertyType )
        {
            return propertyType.HasInterface<IModelObject>();
        }
    
    }
    

    显然,您可以用Ninject替换DI容器和您自己的存储库类型。

        2
  •  3
  •   tvanfosson    15 年前

    <%= Html.DropDownList( "ManufacturerID",
            (IEnumerable<SelectListItem>)ViewData["Manufacturers"] ) %>
    

    具有

    ViewData["Manufacturers"] = db.Manufacturers
                                  .Select( m => new SelectListItem
                                                {
                                                   Text = m.Name,
                                                   Value = m.ManufacturerID
                                                } )
                                   .ToList();
    

    public JsonResult Save(int id,
                           [Bind(Include="Name, Description, ManufacturerID")]Car car)
    
        3
  •  2
  •   Maksymilian Majer    14 年前

    也许这是一个比较晚的版本,但您可以使用自定义模型绑定器来实现这一点。通常我会用与@tvanofosson相同的方法来做,但我有一个案例,我将UserDetails添加到AspNetMembershipProvider表中。因为我也只使用POCO(并从EntityFramework映射它),所以我不想使用id,因为从业务角度来看它是不合理的,所以我创建了一个模型,只用于添加/注册用户。此模型具有用户的所有属性以及角色属性。我想将角色的文本名称绑定到它的RoleModel表示。基本上我就是这么做的:

    public class RoleModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            string roleName = controllerContext.HttpContext.Request["Role"];
    
            var model = new RoleModel
                              {
                                  RoleName = roleName
                              };
    
            return model;
        }
    }
    

    ModelBinders.Binders.Add(typeof(RoleModel), new RoleModelBinder());
    

    <%= Html.DropDownListFor(model => model.Role, new SelectList(Model.Roles, "RoleName", "RoleName", Model.Role))%>
    

    我希望这对你有帮助。