Подтвердить что ты не робот

ASP.NET MVC 2 - привязка к абстрактной модели

Если у меня есть следующее строго типизированное представление:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<XXX.DomainModel.Core.Locations.Location>" %>

Где Местоположение - абстрактный класс.

И у меня есть следующий контроллер, который принимает сильно типизированную модель с помощью POST:

[HttpPost]
public ActionResult Index(Location model)

Я получаю ошибку времени выполнения, указывающую "Невозможно создать абстрактный класс

Что, конечно, имеет смысл. Однако - я не уверен, какое лучшее решение здесь.

У меня много конкретных типов (около 8), и это представление, в котором вы можете редактировать свойства абстрактного класса.

Что мне пытались делать, это создавать перегрузки для всех разных конкретных типов и выполнять мою логику в общем методе.

[HttpPost]
public ActionResult Index(City model)
{
   UpdateLocationModel(model);
   return View(model);
}

[HttpPost]
public ActionResult Index(State model)
{
   UpdateLocationModel(model);
   return View(model);
}

и т.д.

И затем:

[NonAction]
private void UpdateLocationModel (Location model)
{
   // ..snip - update model
}

Но это не работает ни, MVC жалуется, что методы действия неоднозначны (также имеет смысл).

Что нам делать? Можем ли мы просто не привязываться к абстрактной модели?

4b9b3361

Ответ 1

Как насчет написания настраиваемого связующего объекта для этого абстрактного класса:

public class CustomBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        // TODO: based on some request parameter choose the proper child type
        // to instantiate here
        return new Child();
    }
}

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

Ответ 2

Вы также можете создать общий ModelBinder, который работает для всех ваших абстрактных моделей. Мое решение требует, чтобы вы добавили скрытое поле в ваше представление под названием " ModelTypeName" со значением, заданным для имени конкретного типа, который вы хотите. Однако должно быть возможно сделать эту вещь умнее и выбрать конкретный тип, сопоставив свойства типа с полями в представлении.

В файле Global.asax.cs в Application_Start():

ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

CustomModelBinder:

public class CustomModelBinder2 : DefaultModelBinder 
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var modelType = bindingContext.ModelType;
        if (modelType.IsAbstract)
        {
            var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ModelTypeName");
            if (modelTypeValue == null)
                throw new Exception("View does not contain ModelTypeName");

            var modelTypeName = modelTypeValue.AttemptedValue;

            var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName);

            if (type != null)
            {
                var instance= bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type);
                bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type);
            }
        }
        return base.BindModel(controllerContext, bindingContext);
    }
}

Ответ 3

Просто, чтобы выбросить его там - меня очень интересует то, что другие могут ответить, но это то, что я сделал в случае, когда у меня была аналогичная ситуация;

В принципе, я не использовал класс модели в качестве параметра в методе Action, а вместо этого передавал FormCollection и тестировал пару известных дискриминаторов, чтобы выяснить, какой тип для создания/редактирования, а затем использовал TryUpdateModel.

Казалось, что может быть лучший способ, но я никогда не думал об этом больше.