是否有可能在我的模型外键关系绑定到一个表单输入?

说我有CarManufacturer之间的一对多的关系。我希望有更新Car包括设置Manufacturer选择输入方式。我希望能够做到这一点使用内置的模型约束力,但我开始想我得做我自己。

我的动作方法签名看起来像这样:

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

形式的帖子的值名称,描述和制造商,制造商那里是类型int的主键。名称和描述得到正确设置,而不是制造商,这是有道理的,因为该模型粘合剂根本不知道PK场是什么。这是否意味着我将不得不编写自定义IModelBinder它意识到这一点?我不知道怎么会工作,因为我的数据访问库通过在每个Controller构造IoC容器装载。

有帮助吗?

解决方案

下面是我采取的 - 这是一个自定义的模型绑定,当问GetPropertyValue,查看是否该属性是从我的模型装配的对象,并且有一个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容器和你自己的资源库类型。

其他提示

当然每辆汽车只有一个制造商。如果是这样的话,那么你应该把那可以绑定的选择价值的ManufacturerID场。也就是说,你的选择应该有制造商的名称,因为它的文字和标识作为值。在您的保存值,绑定ManufacturerID而不是制造商。

<%= 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)

也许这是一个迟到一个,但你可以使用自定义的模型绑定来实现这一目标。一般情况下我做的方式是相同的@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;
    }
}

然后我不得不添加以下在Global.asax:

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

和在视图中的用法:

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

我希望这可以帮助你。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top