是否可以将我的模型上的外键关系绑定到表单输入?
说我Car
和之间有一对多的关系Manufacturer
.我想要一个更新表单,Car
其中包括用于设置的选择输入Manufacturer
.我希望能够使用内置的模型绑定来做到这一点,但我开始认为我必须自己做.
我的动作方法签名如下所示:
public JsonResult Save(int id, [Bind(Include="Name, Description, Manufacturer")]Car car)
表单发布值Name,Description和Manufacturer,其中Manufacturer是类型的主键int
.名称和描述设置正确,但不是制造商,这是有道理的,因为模型绑定器不知道PK字段是什么.这是否意味着我必须写一个IModelBinder
它知道这个的自定义?我不确定这是如何工作的,因为我的数据访问存储库是通过每个Controller
构造函数上的IoC容器加载的.
这是我的看法 - 这是一个自定义模型绑定器,当被问到GetPropertyValue时,查看该属性是否是我的模型程序集中的对象,并在我的NInject IKernel中注册了一个IRepository <>.如果它可以从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; } ////// 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. /// 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; } ////// perform simple check to see if we should even bother looking for a repository /// private bool CheckRepository( Type propertyType ) { return propertyType.HasInterface(); } }
您显然可以将Ninject替换为您的DI容器和您自己的存储库类型.