Во-первых, извините за большой пост (сначала я попытался провести некоторое исследование) и для сочетания технологий по одному и тому же вопросу (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. Второй вариант выглядит следующим образом:
public class OrderController : Controller
{
/* rest of the implementation ommited */
[HttpPost]
public ActionResult Create(Order order) { /* implementation ommited */ }
}
Теперь проблема: когда я получаю запрос POST с данными из формы, связующее по умолчанию MVC пытается создать экземпляр объекта Order, который является ОК, так как тип метода таков. Но поскольку Order является абстрактным, он не может быть создан, что и должно делать.
Вопрос:, как я могу узнать, какой конкретный тип Order отправляется представлением?
Я уже искал здесь в Qaru и много рассказывал об этом (я работаю над этой проблемой уже около 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>();
}
}
Я попытался получить один из типов с помощью метода 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: в моем коде я изменил строку
var concreteType = Assembly.GetExecutingAssembly().GetType(concreteTypeValue.AttemptedValue);
к
var concreteType = Assembly.GetAssembly(typeof(Order)).GetType(concreteTypeValue.AttemptedValue);
потому что мои классы, где на другом проекте (и так, на другой сборке). Я разделяю это, потому что это кажется более гибким, чем получение только исполняющей сборки, которая не может разрешать типы на внешних сборках. В моем случае все классы заказов находятся на одной и той же сборке. Это не лучше и не волшебная формула, но мне интересно поделиться этим;)