ASP.NET MVC 3:DefaultModelBinder与继承/多态性 [英] ASP.NET MVC 3: DefaultModelBinder with inheritance/polymorphism

查看:233
本文介绍了ASP.NET MVC 3:DefaultModelBinder与继承/多态性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,比较遗憾的是大的职位(我试着做一些研究第一次)和技术对同一个问题的组合(ASP.NET MVC 3,Ninject和MvcContrib)。

First, sorry for the big post (I've tried to do some research first) and for the mix of technologies on the same question (ASP.NET MVC 3, Ninject and MvcContrib).

我发展与ASP.NET MVC 3项目来处理一些客户的订单。

I'm developing a project with ASP.NET MVC 3 to handle some client orders.

简而言之:我有一些物体继承和抽象类订单,我需要分析他们的时候一个POST请求给了我控制器。如何解决正确的类型?我是否需要重写 DefaultModelBinder 类或者有一些其他的方式做到这一点?有人可以提供我一些code或如何做到这一点其他的链接?任何帮助将是巨大的!
如果该信息是混乱的,我可以做任何的改变,使之清楚!

In short: I have some objects inherited from and abstract class Order and I need to parse them when a POST request is made to my controller. How can I resolve the correct type? Do I need to override the DefaultModelBinder class or there are some other way to do this? Can somebody provide me some code or other links on how to do this? Any help would be great! If the post is confusing I can do any change to make it clear!

所以,我对我的订单需要处理以下继承树:

So, I have the following inheritance tree for the orders I need to handle:

public abstract partial class Order {

    public Int32 OrderTypeId {get; set; }

    /* rest of the implementation ommited */
}

public class OrderBottling : Order { /* implementation ommited */ }

public class OrderFinishing : Order { /* implementation ommited */ }

本类都是由实体框架生成的,所以我不会修改他们,因为我需要更新模型(我知道我可以扩展它们)。此外,还会有更多的订单,但都从订单

This classes are all generated by Entity Framework, so I won't modify them because I will need to update the model (I know I can extend them). Also, there will be more orders, but all derived from Order.

我有一个通用视图( Create.aspx ),以创建一个订单,这个观点要求各继承订单的一个强类型的局部视图(以这种情况下, OrderBottling OrderFinishing )。我定义了一个创建于 OrderController 类的GET请求和其他的POST请求()方法。二是这样的:

I have a generic view (Create.aspx) in order to create a order and this view calls a strongly-typed partial view for each of the inherited orders (in this case OrderBottling and OrderFinishing). I defined a Create() method for a GET request and other for a POST request on OrderControllerclass. The second is like the following:

public class OrderController : Controller
{
    /* rest of the implementation ommited */

    [HttpPost]
    public ActionResult Create(Order order) { /* implementation ommited */ }
}

现在的问题:当我收到从表单中的数据POST请求,MVC的默认粘结剂尝试实例化订单对象,这是因为类型确定该方法是。但由于订单是抽象的,它不能被实例化,这是什么是应该做的。

Now the problem: when I receive the POST request with the data from the form, MVC's default binder tries to instantiate an Order object, which is OK since the type of the method is that. But because Order is abstract, it cannot be instantiated, which is what is supposed to do.

的问题:我怎么能发现哪些具体订单类型由视图发送

The question: how can I discover which concrete Order type is sent by the view?

我已经在这里搜索堆栈溢出和Google搜索了很多关于这个(我正在研究这个问题3天左右吧!),并找到了一些方法来解决一些类似的问题,但我无法找到任何东西像我真正的问题。解决这个两个选项:

I've already searched here on Stack Overflow and googled a lot about this (I'm working on this problem for about 3 days now!) and found some ways to solve some similar problems, but I couldn't find anything like my real problem. Two options for solving this:


  • 覆盖ASP.NET MVC DefaultModelBinder ,并使用缸内直喷发现哪些类型是订单;

  • 创建每个订单的方法(不美丽是有问题保持)。

  • override ASP.NET MVC DefaultModelBinder and use Direct Injection to discover which type is the Order;
  • create a method for each order (not beautiful and would be problematic to maintain).

我没有尝试过的第二个选项,因为我不认为这是解决问题的正确方法。对于第一种选择我试过Ninject解决订单的类型和对其进行实例化。我的Ninject模块是这样的:

I haven't tried the second option because I don't think it's the right way to solve the problem. For the first option I've tried Ninject to resolve the type of the order and instantiate it. My Ninject module is like the following:

private class OrdersService : NinjectModule
{
    public override void Load()
    {
        Bind<Order>().To<OrderBottling>();
        Bind<Order>().To<OrderFinishing>();
    }
}

我试图让类型之一throught Ninject的获取LT;&GT;()方法,但它告诉我,该是解决一个以上的方式方式。所以,我理解的模块没有很好地落实。我也试图执行这样两种类型:绑定&LT;排序&gt;()为&lt; OrderBottling&GT;()WithPropertyInject(OrderTypeId,2); 。但它有同样的问题...什么是执行这个模块的正确方法?

I've have tried to get one of the types throught Ninject's Get<>() method, but it tells me that the are more then one way to resolve the type. So, I understand the module is not well implemented. I've also tried to implement like this for both types: Bind<Order>().To<OrderBottling>().WithPropertyInject("OrderTypeId", 2);, but it has the same problem... What would be the right way to implement this module?

我也尝试使用MvcContrib模型绑定。我已经做到了这一点:

I've also tried use MvcContrib Model Binder. I've done this:

[DerivedTypeBinderAware(typeof(OrderBottling))]
[DerivedTypeBinderAware(typeof(OrderFinishing))]
public abstract partial class Order { }

的Global.asax.cs 我已经做到了这一点:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);

    ModelBinders.Binders.Add(typeof(Order), new DerivedTypeModelBinder());
}

但是,这将引发异常: system.missingMethodException而:无法创建抽象类的。所以,我presume粘合剂是不会或不能解析到正确的类型。

But this throws an exception: System.MissingMethodException: Cannot create an abstract class. So, I presume the binder isn't or can't resolve to the correct type.

许多许多在此先感谢!

编辑:首先,谢谢马丁和Jason你的答案和抱歉耽误了!我想这两种方法都和工作!我标志着马丁的答案正确的,因为它更灵活,能满足一些需求,为我的项目。具体地,对于每个请求的ID被存储在数据库中,如果我只在一个地方(数据库或在类)更改ID把它们的类可以打破软件。马丁的做法是在这一点上是非常灵活的。

first of all, thank you Martin and Jason for your answers and sorry for the delay! I tried both approaches and both worked! I marked Martin's answer as correct because it is more flexible and meets some of the needs for my project. Specifically, the IDs for each request are stored on a database and putting them on the class can break the software if I change the ID only in one place (database or on the class). Martin's approach is very flexible in that point.

@马丁:我的code我改了行

@Martin: on my code I changed the line

var concreteType = Assembly.GetExecutingAssembly().GetType(concreteTypeValue.AttemptedValue);

var concreteType = Assembly.GetAssembly(typeof(Order)).GetType(concreteTypeValue.AttemptedValue);

由于我的班,其中的另一个项目(等,在不同的部件)。我分享这一点,因为它似乎喜欢比只得到执行的程序集无法解析外部组件类型更加灵活。在我的情况下,所有的秩序类是在相同的生产。这不是最好,也不是一个神奇的公式,但我觉得有趣的是,分享这个;)

because my classes where on another project (and so, on a different assembly). I'm sharing this because it's seems a like more flexible than getting only the executing assembly that cannot resolve types on external assemblies. In my case all the order classes are on the same assembly. It's not better nor a magic formula, but I think is interesting to share this ;)

推荐答案

我已经尝试过做类似的事情,我得出的结论是没有什么内置的,这将处理这个问题。

I've tried to do something similar before and I came to the conclusion that there's nothing built in which will handle this.

我去的选择是创建自己的模型绑定(虽然从默认继承所以它不是太多code)。它找了一个回值与所谓xxxConcreteType类型,其中xxx是另一种类型的它被绑定到的名称。这意味着一个字段必须调回与你试图绑定类型的值;在任一OrderBottling或OrderFinishing。

The option I went with was to create my own model binder (though inherited from the default so its not too much code). It looked for a post back value with the name of the type called xxxConcreteType where xxx was another type it was binding to. This means that a field must be posted back with the value of the type you're trying to bind; in this case OrderConcreteType with a value of either OrderBottling or OrderFinishing.

您另一种方法是使用的UpdateModel或TryUpdateModel和你的方法ommit参数。您将需要确定哪一种你叫这(通过一个参数或其他方式)之前,要更新模式,并事先实例化类,那么你可以使用两种方法来这样就把它

Your other alternative is to use UpdateModel or TryUpdateModel and ommit the parameter from your method. You will need to determine which kind of model you're updating before calling this (either by a parameter or otherwise) and instantiate the class beforehand, then you can use either method to popuplate it

编辑:

下面是code ..

Here is the code..

public class AbstractBindAttribute : CustomModelBinderAttribute
{
    public string ConcreteTypeParameter { get; set; }

    public override IModelBinder GetBinder()
    {
        return new AbstractModelBinder(ConcreteTypeParameter);
    }

    private class AbstractModelBinder : DefaultModelBinder
    {
        private readonly string concreteTypeParameterName;

        public AbstractModelBinder(string concreteTypeParameterName)
        {
            this.concreteTypeParameterName = concreteTypeParameterName;
        }

        protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {
            var concreteTypeValue = bindingContext.ValueProvider.GetValue(concreteTypeParameterName);

            if (concreteTypeValue == null)
                throw new Exception("Concrete type value not specified for abstract class binding");

            var concreteType = Assembly.GetExecutingAssembly().GetType(concreteTypeValue.AttemptedValue);

            if (concreteType == null)
                throw new Exception("Cannot create abstract model");

            if (!concreteType.IsSubclassOf(modelType))
                throw new Exception("Incorrect model type specified");

            var concreteInstance = Activator.CreateInstance(concreteType);

            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => concreteInstance, concreteType);

            return concreteInstance;
        }
    }
}

改变你的操作方法,看起来像这样:

Change your action method to look like this:

public ActionResult Create([AbstractBind(ConcreteTypeParameter = "orderType")] Order order) { /* implementation ommited */ }

您需要把以下内容在您的视图:

You would need to put the following in your view:

@Html.Hidden("orderType, "Namespace.xxx.OrderBottling")

这篇关于ASP.NET MVC 3:DefaultModelBinder与继承/多态性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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