抽象类可以作为控制器动作中的参数吗?

时间:2011-05-02 19:01:02

标签: c# ajax serialization asp.net-mvc-3

我在Controller中有一个Action函数,它是用AJAX调用的。该行动涉及1个参数。客户端,我构造了一个JSON对象,它应该序列化为该1参数。我遇到的问题是参数类被声明为抽象。因此,它无法实例化。

当AJAX点击该Action时,我得到以下内容:

  

无法创建抽象类。

     

堆栈追踪:

     

[MissingMethodException:无法创建   一个抽象的课。]
  System.RuntimeTypeHandle.CreateInstance(RuntimeType   type,Boolean publicOnly,Boolean   noCheck,布尔& canBeCached,   RuntimeMethodHandleInternal&安培;构造函数,   布尔和放大器; bNeedSecurityCheck)+0
  System.RuntimeType.CreateInstanceSlow(布尔   publicOnly,Boolean skipCheckThis,   Boolean fillCache)+98
  System.RuntimeType.CreateInstanceDefaultCtor(布尔   publicOnly,布尔   skipVisibilityChecks,布尔值   skipCheckThis,Boolean fillCache)+241   System.Activator.CreateInstance(类型   type,Boolean nonPublic)+69   ...............

有没有办法在没有创建不同参数对象的情况下实现这样的场景,“取消声明”参数对象为抽象,或者深入研究MVC的机制?

更新

我目前正在与后端开发人员合作调整他们的对象。无论哪种方式,我认为这将是最终的解决方案。谢谢大家的答案。

5 个答案:

答案 0 :(得分:23)

更新:示例现在使用AJAX JSON POST

如果必须使用抽象类型,则可以提供custom model 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()));
}

答案 1 :(得分:4)

您必须创建抽象类的子类并将其传递给它。从根本上说,抽象类不允许自己实例化。但是,如果你有一个C#方法,如:

protected void Foo(MyAbstractClass param1)

...然后你仍然可以传递Foo一个派生自MyAbstractClass的类型的实例。因此,您可以创建一个具体的子类MyChildClass : MyAbstractClass并将其传递给您的方法,它应该仍然有效。您不必更改Foo方法,但需要访问C#代码,以便创建MyChildClass

如果您正在使用泛型 - 例如,如果您的方法签名是:

protected void Foo(IEnumerable<MyAbstractClass> param1)

...然后它变得更复杂,你想要在C#泛型中查看covariance and contravariance

答案 2 :(得分:4)

框架无法知道您想要的具体实施方式,也不会承担此类决策的责任。所以你有两种可能性:

  1. 使用具体类型作为操作参数
  2. 为此抽象类编写自定义模型绑定器,基于某些请求参数将返回特定实例。

答案 3 :(得分:0)

如果您有权访问控制器,您可以添加另一个继承抽象类的类而不指定任何成员,并使用它来序列化反序列化,然后将其基础返回到另一个层吗?

我知道这不是好习惯,但有些黑客但我不知道抽象类是否可以某种方式序列化。

答案 4 :(得分:-2)

不 - 尝试将JSON反序列化为抽象类的对象是没有意义的。你能不能成为一个合适的班级?