Во-первых, извините за большой пост (сначала я попытался провести некоторое исследование) и за сочетание технологий по одному и тому же вопросу (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 */ }
Все эти классы созданы Entity Framework, поэтому я не буду их изменять, потому что мне нужно будет обновить модель (я знаю, что могу их расширить). Также будет больше заказов, но все производные от Order
.
У меня есть общее представление (Create.aspx
) для создания заказа, и это представление вызывает строго типизированное частичное представление для каждого из унаследованных заказов (в данном случае OrderBottling
и OrderFinishing
). Я определил метод Create()
для запроса GET и другой метод для запроса POST на OrderController
class. Второй выглядит следующим образом:
public class OrderController : Controller
{
/* rest of the implementation ommited */
[HttpPost]
public ActionResult Create(Order order) { /* implementation ommited */ }
}
Теперь проблема: когда я получаю запрос POST с данными из формы, связыватель MVC по умолчанию пытается создать экземпляр объекта Order
, что нормально, поскольку тип метода такой. Но поскольку Order
является абстрактным, его нельзя создать, что и предполагается делать.
Вопрос: как узнать, какой конкретный тип Order
отправляется представлением?
Я уже искал здесь на Stack Overflow и много гуглил об этом (я работаю над этой проблемой уже около 3 дней!) И нашел несколько способов решить некоторые похожие проблемы, но я не смог найти ничего похожего на мой реальный проблема. Два варианта решения этого:
- переопределить ASP.NET MVC
DefaultModelBinder
и использовать Direct Injection, чтобы узнать, какой тип являетсяOrder
; - создайте метод для каждого заказа (не красиво и будет проблематично поддерживать).
Я не пробовал второй вариант, потому что не думаю, что это правильный способ решить проблему. Для первого варианта я попробовал Ninject, чтобы определить тип заказа и создать его экземпляр. Мой модуль Ninject выглядит следующим образом:
private class OrdersService : NinjectModule
{
public override void Load()
{
Bind<Order>().To<OrderBottling>();
Bind<Order>().To<OrderFinishing>();
}
}
Я пытался получить один из типов с помощью метода Get<>()
Ninject, но он говорит мне, что существует более чем один способ разрешения типа. Итак, я так понимаю, что модуль не очень хорошо реализован. Я также пытался реализовать подобное для обоих типов: 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: в моем коде я изменил строку
var concreteType = Assembly.GetExecutingAssembly().GetType(concreteTypeValue.AttemptedValue);
to
var concreteType = Assembly.GetAssembly(typeof(Order)).GetType(concreteTypeValue.AttemptedValue);
потому что мои классы находились в другом проекте (и, следовательно, в другой сборке). Я делюсь этим, потому что это кажется более гибким, чем получение только исполняемой сборки, которая не может разрешать типы во внешних сборках. В моем случае все классы заказов находятся в одной сборке. Это не лучше и не волшебная формула, но я думаю, что интересно поделиться этим ;)
abstract
изOrder
? - person Sergi Papaseit   schedule 28.03.2011Order
в качестве аргумента, не так ли? ModelBinder попытается понять объектOrder
, основываясь на том, какие свойства ему могут соответствовать. - person Sergi Papaseit   schedule 28.03.2011modelType.Assembly.GetType(concreteTypeValue.AttemptedValue)
, это сохранит абстракцию между связующим и вашим бизнесом модель. - person Jay   schedule 07.09.2012