ASP.NET MVC 3: DefaultModelBinder с наследованием / полиморфизмом

Во-первых, извините за большой пост (сначала я попытался провести небольшое исследование) и за сочетание технологий по одному и тому же вопросу (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 дней!) И нашел несколько способов решить некоторые похожие проблемы, но я не мог не нашел ничего похожего на мою настоящую проблему. Два варианта решения этой проблемы:

  • переопределить 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 () .To () .WithPropertyInject ("OrderTypeId", 2); , но у него та же проблема. .. Как правильно реализовать этот модуль?

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);

, потому что мои классы находятся в другом проекте (и, следовательно, в другой сборке). Я делюсь этим, потому что это кажется более гибким, чем получение только исполняемой сборки, которая не может разрешать типы во внешних сборках. В моем случае все классы заказов находятся в одной сборке. Это не лучше и не волшебная формула, но я думаю, что интересно поделиться этим;)

17
задан jmpcm 18 March 2012 в 03:10
поделиться