Может ли абстрактный класс быть параметром в действии контроллера? - PullRequest
15 голосов
/ 02 мая 2011

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

Когда AJAX нажимает на это действие, я получаю следующее:

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

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

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

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

Обновление

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

Ответы [ 5 ]

23 голосов
/ 02 мая 2011

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

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

Связыватель модели / модели

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()));
}
4 голосов
/ 02 мая 2011

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

protected void Foo(MyAbstractClass param1)

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

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

protected void Foo(IEnumerable<MyAbstractClass> param1)

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

4 голосов
/ 02 мая 2011

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

  1. Использовать конкретный тип в качестве параметра действия
  2. Написать пользовательский механизм связывания модели для этого абстрактного класса, который на основе некоторых параметров запроса будет возвращать конкретный экземпляр.
0 голосов
/ 02 мая 2011

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

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

0 голосов
/ 02 мая 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...