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

У меня есть функция Action внутри контроллера, который вызывается с AJAX. Это действие принимает один параметр. Клиент, я создаю объект JSON, который должен быть сериализован в этот параметр. Проблема, с которой я столкнулся, заключается в том, что класс параметра объявлен как абстрактный. Таким образом, он не может быть создан. Когда AJAX попадает в это действие, я получаю следующее:

Невозможно создать абстрактный класс.

Трассировка стека:

[MissingMethodException: невозможно создать абстрактный класс.]
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean & canBeCached, RuntimeMethodHandleInternal & т е р, Логическое & bNeedSecurityCheck) +0
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241 System.Activator.CreateInstance(Тип type, Boolean nonPublic) +69...............

Есть ли способ снять такой сценарий, не создавая другой объект параметра, "не объявляя" объект параметра абстрактным или копаясь в механике MVC? Спасибо.

UPDATE: В настоящее время я работаю с сторонними разработчиками, чтобы настроить их объекты. В любом случае, я думаю, что это будет окончательное решение. Спасибо всем за ваши ответы.

Ответы

Ответ 1

Обновление: Пример теперь использует AJAX JSON POST

Если вы должны использовать абстрактный тип, вы можете предоставить настраиваемое связующее устройство для создания конкретного экземпляра. Пример показан ниже:

Модель/модель Binder

public abstract class Student
{
    public abstract int Age { get; set; }
    public abstract string Name { get; set; }
}
public class GoodStudent : Student
{
    public override int Age { get; set; }
    public override string Name { get; set; }
}
public class BadStudent : Student
{
    public override int Age { get; set; }
    public override string Name { get; set; }
}
public class StudentBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var values = (ValueProviderCollection) bindingContext.ValueProvider;
        var age = (int) values.GetValue("Age").ConvertTo(typeof (int));
        var name = (string) values.GetValue("Name").ConvertTo(typeof(string));
        return age > 10 ? (Student) new GoodStudent { Age = age, Name = name } : new BadStudent { Age = age, Name = name };
    }
}

Действия с контроллером

public ActionResult Index()
{
    return View(new GoodStudent { Age = 13, Name = "John Smith" });
}
[HttpPost]
public ActionResult Index(Student student)
{
    return View(student);
}

Просмотр

@model AbstractTest.Models.Student

@using (Html.BeginForm())
{
    <div id="StudentEditor">
        <p>Age @Html.TextBoxFor(m => m.Age)</p>
        <p>Name @Html.TextBoxFor(m => m.Name)</p>
        <p><input type="button" value="Save" id="Save" /></p>
    </div>
}

<script type="text/javascript">
    $('document').ready(function () {
        $('input#Save').click(function () {
            $.ajax({
                url: '@Ajax.JavaScriptStringEncode(Url.Action("Index"))',
                type: 'POST',
                data: GetStudentJsonData($('div#StudentEditor')),
                contentType: 'application/json; charset=utf-8',
                success: function (data, status, jqxhr) { window.location.href = '@Url.Action("Index")'; }
            });
        });
    });

    var GetStudentJsonData = function ($container) {
             return JSON.stringify({
                 'Age': $container.find('input#Age').attr('value'),
                 'Name': $container.find('input#Name').attr('value')
             });
         };
</script>

Добавлено в Global.asax.cs

protected void Application_Start()
{
    ...
    ModelBinders.Binders.Add(new KeyValuePair<Type, IModelBinder>(typeof(Student), new StudentBinder()));
}

Ответ 2

Вам нужно будет создать дочерний класс абстрактного класса и передать это вместо этого. Абстрактные классы в принципе не допускаются к созданию самих себя. Однако, если у вас есть метод С#, например:

protected void Foo(MyAbstractClass param1)

... тогда вы все равно можете передать Foo экземпляр типа, который происходит из MyAbstractClass. Таким образом, вы можете создать конкретный дочерний класс MyChildClass : MyAbstractClass и передать его вашему методу, и он все равно должен работать. Вам не придется менять метод Foo, но вам потребуется некоторый доступ к С# -коду, чтобы вы могли создать MyChildClass.

Если вы работаете с дженериками - например, если ваша подпись метода:

protected void Foo(IEnumerable<MyAbstractClass> param1)

..., тогда это становится более сложным, и вы захотите изучить ковариацию и контравариантность в генераторах С#.

Ответ 3

У рамки нет возможности узнать, какую конкретную реализацию вы хотите, и она не возьмет на себя ответственность за такое решение. Таким образом, у вас есть две возможности:

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

Ответ 4

Если у вас есть доступ к контроллеру, вы можете добавить еще один класс, наследующий абстрактный класс, без указания какого-либо члена, и использовать его для сериализации десериализации, а затем вернуть его базу на другой уровень?

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

Ответ 5

Нет - нет смысла пытаться десериализовать JSON для объекта абстрактного класса. Разве вы не можете сделать его подходящим классом?