【发布时间】:2011-07-24 12:33:41
【问题描述】:
首先,很抱歉这篇大文章(我已经尝试先做一些研究)以及针对同一问题的技术组合(ASP.NET MVC 3、Ninject 和 MvcContrib)。
我正在使用 ASP.NET MVC 3 开发一个项目来处理一些客户订单。
简而言之:我有一些继承自抽象类 Order 的对象,当向我的控制器发出 POST 请求时,我需要解析它们。如何解决正确的类型?我需要覆盖DefaultModelBinder 类还是有其他方法可以做到这一点?有人可以为我提供一些关于如何执行此操作的代码或其他链接吗?任何帮助都会很棒!
如果帖子令人困惑,我可以做任何更改以使其清楚!
所以,对于我需要处理的订单,我有以下继承树:
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 */ }
这些类都是由实体框架生成的,所以我不会修改它们,因为我需要更新模型(我知道我可以扩展它们)。另外,还会有更多的订单,但都是来自Order。
我有一个通用视图 (Create.aspx) 来创建一个订单,这个视图为每个继承的订单(在本例中为 OrderBottling 和 OrderFinishing)调用一个强类型的部分视图。我为 GET 请求定义了一个 Create() 方法,并为 OrderControllerclass 上的 POST 请求定义了其他方法。第二个是这样的:
public class OrderController : Controller
{
/* rest of the implementation ommited */
[HttpPost]
public ActionResult Create(Order order) { /* implementation ommited */ }
}
现在的问题是:当我收到带有表单数据的 POST 请求时,MVC 的默认绑定器会尝试实例化一个 Order 对象,这没关系,因为方法的类型就是这样。但是因为Order是抽象的,所以不能实例化,这是应该做的。
问题:如何发现视图发送的具体Order 类型?
我已经在 Stack Overflow 上进行了搜索,并在 Google 上搜索了很多相关信息(我正在处理这个问题大约 3 天!)并找到了一些解决类似问题的方法,但我找不到任何东西就像我真正的问题一样。解决这个问题的两种选择:
- 覆盖 ASP.NET MVC
DefaultModelBinder并使用直接注入来发现Order的类型; - 为每个订单创建一个方法(不美观,维护起来会有问题)。
我没有尝试过第二种选择,因为我认为这不是解决问题的正确方法。对于第一个选项,我尝试了 Ninject 来解析订单的类型并实例化它。我的 Ninject 模块如下所示:
private class OrdersService : NinjectModule
{
public override void Load()
{
Bind<Order>().To<OrderBottling>();
Bind<Order>().To<OrderFinishing>();
}
}
我曾尝试通过 Ninject 的 Get<>() 方法获取其中一种类型,但它告诉我解析该类型的方法不止一种。所以,我知道该模块没有很好地实现。我也尝试为这两种类型实现这样的:Bind<Order>().To<OrderBottling>().WithPropertyInject("OrderTypeId", 2);,但它有同样的问题......实现这个模块的正确方法是什么?
我也尝试过使用 MvcContrib Model Binder。我已经这样做了:
[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:无法创建抽象类。所以,我认为活页夹不是或无法解析为正确的类型。
提前非常感谢!
编辑:首先,感谢 Martin 和 Jason 的回答,并对延误表示歉意!我尝试了这两种方法,都奏效了!我将 Martin 的答案标记为正确,因为它更灵活并且可以满足我项目的一些需求。具体来说,每个请求的 ID 都存储在数据库中,如果我只在一个地方(数据库或班级)更改 ID,将它们放在班级上可能会破坏软件。 Martin 的方法在这一点上非常灵活。
@Martin:在我的代码中我更改了行
var concreteType = Assembly.GetExecutingAssembly().GetType(concreteTypeValue.AttemptedValue);
到
var concreteType = Assembly.GetAssembly(typeof(Order)).GetType(concreteTypeValue.AttemptedValue);
因为我的课程在另一个项目上(因此,在不同的程序集上)。我之所以分享这个,是因为它似乎比只获取无法解析外部程序集类型的执行程序集更灵活。在我的情况下,所有订单类都在同一个程序集中。这不是更好也不是一个神奇的公式,但我认为分享这个很有趣;)
【问题讨论】:
-
完整的堆栈跟踪总是更容易诊断问题。
-
@jmpcm - 只是为了确保,如果您从
Order中删除abstract修饰符,它是否有效? -
@Sergi:不,它也不起作用。之前生成模型的一次,我没有把 Order 作为抽象,结果是一样的,但是有不同的错误(不记得是什么了)。
-
@Sergi,没有 abstract 修饰符,默认模型绑定器将实例化一个 Order 对象并填充 Order 对象的属性;它不会绑定子类型的属性
-
@Martin - 子类型的属性无论如何都会丢失,因为该操作将
Order作为参数,不是吗? ModelBinder 将尝试根据它可以匹配的属性来理解Order对象。
标签: c# inheritance asp.net-mvc-3 mvccontrib ninject-2