Во-первых, извините за большой пост (сначала я попытался провести некоторые исследования) и за сочетание технологий по одному и тому же вопросу (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
отправляется представлением?
Я уже искал здесь в 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>();
}
}
Я пытался получить один из типов, используя метод 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);
потому что мои занятия где-то в другом проекте (и так, в другой сборке).Я делюсь этим, потому что это кажется более гибким, чем получение только исполняемой сборки, которая не может разрешать типы на внешних сборках.В моем случае все классы заказов находятся на одной сборке.Это не лучше и не волшебная формула, но я думаю, что интересно поделиться этим;)