2011-05-02 27 views
15

Tengo una función de acción dentro de un controlador, que se está llamando con AJAX. Esa acción está tomando en 1 parámetro. En el lado del cliente, construyo un objeto JSON que debe serializarse en ese 1 parámetro. El problema con el que me encontré es que la clase de parámetro se declara como abstracta. Por lo tanto, no puede ser instanciado. Cuando AJAX golpea esa acción, obtengo lo siguiente:¿Puede la clase abstracta ser un parámetro en la acción de un controlador?

No se puede crear una clase abstracta.

Seguimiento de la pila:

[MissingMethodException:. No se puede crear una clase abstracta ]
System.RuntimeTypeHandle.CreateInstance (RuntimeType tipo, Boolean publicOnly, Boolean NOCHECK, Boolean & canBeCached, RuntimeMethodHandleInternal & ctor, Boolean & bNeedSecurityCheck) +0
System.RuntimeType.CreateInstanceSlow (Boolean publicOnly, Boolean skipCheckThis, booleana FillCache) 98
System.RuntimeType.CreateInstanceDefaultCtor (booleano publicOnly, Boolean, Boolean skipVisibilityChecks skipCheckThis, Boolean FillCache) 241 System.Activator.CreateInstance (Tipo tipo, Boole no pública) 69 .. .............

¿Hay alguna manera de lograr tal escenario sin crear un objeto de parámetro diferente, "no declarar" el objeto del parámetro como abstracto o profundizar en Mecánica de MVC? Gracias.

ACTUALIZACIÓN: Actualmente estoy trabajando con desarrolladores back-end para ajustar sus objetos. De cualquier manera, creo que esa sería la solución definitiva. Gracias a todos por sus respuestas o comentarios.

+4

creo que el parámetro tiene que ser un hormigón type.No se permiten tipos abstractos o tipo de interfaz. –

Respuesta

22

Actualización: Ejemplo utiliza ahora un AJAX JSON POSTAL

Si tiene que usar un tipo abstracto, se podría proporcionar una custom model binder para crear la instancia concreta. Un ejemplo se muestra a continuación:

Modelo/Model acciones Carpeta

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

Controlador

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

Ver

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

Agregado a Global.asax.cs

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

Esto es genial y me ayudó en un aprieto. – John

+0

Gracias por sugerir ModelBinders.Binders.Add en Application_Start(). – Dilip0165

-2

No, no tiene sentido intentar deserializar JSON a un objeto de una clase abstracta. ¿No puedes convertirlo en una clase adecuada?

+0

Estos objetos provienen de otra capa en la aplicación, prácticamente me los dieron. No tengo ningún poder sobre ellos. Entonces, supongo que solo crearía mi propio objeto y luego poblaría el resumen con los datos de mi cuenta :( – Dimskiy

+1

@Dimskiy - Siempre podría heredar su clase del objeto abstracto. –

4

Deberá crear una clase secundaria de la clase abstracta y pasarla. Las clases abstractas no están fundamentalmente autorizadas a ser creadas por ellos mismos. Sin embargo, si usted tiene un método C# como:

protected void Foo(MyAbstractClass param1) 

... entonces todavía puede pasar Foo una instancia de un tipo que se deriva de MyAbstractClass. Entonces puede crear una clase infantil concreta MyChildClass : MyAbstractClass y pasarla a su método, y aún debería funcionar. No tendrá que cambiar el método Foo, pero necesitará algo de acceso al código C# para poder crear MyChildClass.

Si está trabajando con los genéricos - por ejemplo, si su firma del método es:

protected void Foo(IEnumerable<MyAbstractClass> param1) 

... entonces se vuelve más complicado, y usted desee ver en covariance and contravariance en C# genéricos .

+0

justo lo que estaba pensando, entonces escribió una respuesta duplicada más o menos, pero +1 :) –

+0

En realidad tengo una lista y sí, tengo acceso a C#, pero solo dentro de mi proyecto MVC. No puedo tocar capas comerciales/DAL. – Dimskiy

+0

@Dimskiy - En ese caso, es posible que aún pueda pasar una 'List ' poblada con objetos 'ThatDerivedClass'. Usted * puede * pasar en una 'Lista 'si el método permite la covarianza, pero no estoy seguro. Sería bueno si pudieras escribir un contenedor C# para capturar los datos JSON y volver a empaquetarlos antes de enviarlos a tus capas comerciales/DAL. –

4

El marco no tiene forma de saber qué implementación específica desea, ni tomará la responsabilidad de tal decisión.Así que hay dos posibilidades:

  1. utilizar un tipo concreto como parámetro de acción
  2. Escribe un ligante modelo personalizado para esta clase abstracta que basa en algunos parámetros de la petición devolverá una instancia específica.
0

Si tiene acceso al controlador ¿podría agregar otra clase que hereda la clase abstracta sin especificar ningún miembro, y usarla para serializar la deserialización, y luego devolver su base a la otra capa?

Sé que esto no es una buena práctica, pero algunos piratear, sin embargo, no sé si las clases abstractas se pueden serializar de alguna manera.

Cuestiones relacionadas