ASP.NET MVC模型绑定外键关系 [英] ASP.NET MVC model binding foreign key relationship

查看:155
本文介绍了ASP.NET MVC模型绑定外键关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Is it possible to bind a foreign key relationship on my model to a form input?

说我有制造商之间存在一个一对多的关系。我想有更新,其中包括设置制造商选择输入的表单。我希望能够做到这一点使用内置的模型约束力,但我开始想我要去做自己。

Say I have a one-to-many relationship between Car and Manufacturer. I want to have a form for updating Car that includes a select input for setting Manufacturer. I was hoping to be able to do this using the built-in model binding, but I'm starting to think I'll have to do it myself.

我的操作方法的签名是这样的:

My action method signature looks like this:

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

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

The form posts the values Name, Description and Manufacturer, where Manufacturer is a primary key of type int. Name and Description get set properly, but not Manufacturer, which makes sense since the model binder has no idea what the PK field is. Does that mean I would have to write a custom IModelBinder that it aware of this? I'm not sure how that would work since my data access repositories are loaded through an IoC container on each Controller constructor.

推荐答案

下面是我的起飞 - 这是一个自定义的模型绑定,当问GetPropertyValue,查找是否该属性是从我的模型装配一个对象,并具有IRepository<>在我的NInject的iKernel注册。如果可以从Ninject的IRepository,它使用检索外键对象。

Here's my take - this is a custom model binder that when asked to GetPropertyValue, looks to see if the property is an object from my model assembly, and has a IRepository<> registered in my NInject IKernel. If it can get the IRepository from Ninject, it uses that to retrieve the foreign key object.

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容器和你自己的资源库类型。

you could obviously substitute Ninject for your DI container and your own repository type.

这篇关于ASP.NET MVC模型绑定外键关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆