首页 文章

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

提问于
浏览
15

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

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

无法创建抽象类 . 堆栈跟踪:[MissingMethodException:无法创建抽象类 . ] System.RuntimeTypeHandle.CreateInstance(RuntimeType类型,Boolean publicOnly,Boolean noCheck,Boolean&canBeCached,RuntimeMethodHandleInternal&ctor,Boolean&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 type,Boolean nonPublic)69 .............. .

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

更新

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

5 回答

  • 22

    Update: 示例现在使用AJAX JSON POST

    如果必须使用抽象类型,则可以提供custom model binder来创建具体实例 . 一个例子如下所示:

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

    Controller Actions

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

    View

    @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>
    

    Added to Global.asax.cs

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

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

    • 使用具体类型作为操作参数

    • 为此抽象类编写自定义模型 Binders ,它基于某些请求参数将返回特定实例 .

  • -2

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

    protected void Foo(MyAbstractClass param1)
    

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

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

    protected void Foo(IEnumerable<MyAbstractClass> param1)
    

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

  • 4

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

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

  • 0

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

相关问题