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