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

本文关键字:参数 控制器 抽象类 | 更新日期: 2023-09-27 17:54:08

我在控制器内部有一个Action函数,它是用AJAX调用的。这个Action只接受一个参数。在客户端,我构造了一个JSON对象,该对象应该序列化为1参数。我遇到的问题是参数类被声明为抽象的。因此,它不能被实例化。

当AJAX命中该Action时,我得到以下内容:

不能创建抽象类。

堆栈跟踪:

[MissingMethodException: Cannot create .一个抽象类[br>System.RuntimeTypeHandle.CreateInstance (RuntimeTypepublicOnly, BooleannoCheck Boolean&canBeCached,RuntimeMethodHandleInternal&男星,Boolean&bNeedSecurityCheck) + 0
System.RuntimeType.CreateInstanceSlow(布尔publicOnly, Boolean skipCheckThis;

System.RuntimeType.CreateInstanceDefaultCtor(布尔publicOnly,布尔skipVisibilityChecks,布尔skipCheckThis, Boolean fillCache) +241System.Activator.CreateInstance(类型type, Boolean (nonPublic) +69...............

有没有办法实现这样的场景,而不创建一个不同的参数对象,"取消声明"参数对象为抽象,或挖掘MVC的机制?

更新

我目前正在与后端开发人员一起调整他们的对象。不管怎样,我认为这将是最终的解决方案。谢谢大家的回答。

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

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

如果必须使用抽象类型,则可以提供自定义模型绑定器来创建具体实例。下面是一个示例:

Model/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 };
    }
}
<<p> 控制器操作/strong>
public ActionResult Index()
{
    return View(new GoodStudent { Age = 13, Name = "John Smith" });
}
[HttpPost]
public ActionResult Index(Student student)
{
    return View(student);
}
<<p> 视图/strong>
@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. 使用具体类型作为动作参数
  2. 为这个抽象类编写一个自定义模型绑定器,它将根据一些请求参数返回一个特定的实例。

您必须创建抽象类的子类并传递它。抽象类基本上不允许自己实例化。然而,如果你有一个c#方法,如:

protected void Foo(MyAbstractClass param1)

…那么您仍然可以向Foo传递一个派生自MyAbstractClass的类型的实例。你可以创建一个具体的子类MyChildClass : MyAbstractClass并把它传递给你的方法,它应该仍然可以工作。您不必更改Foo方法,但您需要访问c#代码,以便创建MyChildClass

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

protected void Foo(IEnumerable<MyAbstractClass> param1)

…然后它会变得更复杂,你会想看看c#泛型中的协方差和逆变。

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

我知道这不是一个好的实践,但是有些hack,但是我不知道抽象类是否可以以某种方式序列化

不——试图将JSON反序列化为抽象类的对象是没有意义的。你就不能让它成为一个正规的课程吗?