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

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

<%@ 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 жалуется, что методы действий неоднозначны (также имеет смысл).

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

Ответы [ 3 ]

7 голосов
/ 25 октября 2010

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

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

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

3 голосов
/ 22 сентября 2011

Вы также можете создать общий 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);
    }
}
1 голос
/ 25 октября 2010

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

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

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

...