我有一个抽象对象BaseObject(示例名称),它有两个特定于国家/地区的实现,UsaObject和CanadaObject,这两个实现都扩展了BaseObject。
public abstract class BaseObject...
public class UsaObject : BaseObject...
public class CanadaObject : BaseObject...
在我的控制器中,我有一个动作
public void UpdateObject(BaseObject object) {
...
}
该操作只调用BaseObject上的方法,不需要强制转换。
我使用Json.NET执行序列化,在序列化对象时,我使用Json SerializationSettings.TypeNameHandling.objects将类的类型添加到对象中。这使我可以轻松地将数据正确发送到UI,并根据国家/地区更改显示内容,而无需担心确切的时间,直到我需要为止。
但是,我在UpdateObject方法中发布这些数据时遇到了问题。它在反序列化时失败,并出现以下错误(请注意,我们正在使用Glimpse对应用程序执行性能分析,尽管我根本不怀疑这是原因),这是在IExceptionFilter.OException调用中捕获的:
System.MissingMethodException: Cannot create an abstract class.
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
at System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at Castle.Proxies.DefaultModelBinderProxy.BindModel_callback(ControllerContext controllerContext, ModelBindingContext bindingContext)
at Castle.Proxies.Invocations.DefaultModelBinder_BindModel.InvokeMethodOnTarget()
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Glimpse.Core.Extensibility.CastleInvocationToAlternateMethodContextAdapter.Proceed()
at Glimpse.Core.Extensions.AlternateMethodContextExtensions.TryProceedWithTimer(IAlternateMethodContext context, TimerResult& timerResult)
at Glimpse.Core.Extensibility.AlternateMethod.NewImplementation(IAlternateMethodContext context)
at Glimpse.Core.Extensibility.AlternateTypeToCastleInterceptorAdapter.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.DefaultModelBinderProxy.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass1e.<BeginInvokeAction>b__16(AsyncCallback asyncCallback, Object asyncState)
有没有一种方法可以修改MVC 5处理反序列化的方式?如果有,我应该在页面生命周期的哪个阶段完成?
原来我做了一个错误的假设,认为MVC使用Json.NET进行反序列化(真让我丢脸)。MVC有自己的ModelBinder,默认情况下是DefaultModelBinder。请耐心等待我的解决方案,它有点仓促,需要测试。我能够扩展DefaultModelBinder,
public class AbstractModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType.IsAbstract)
{
var typeName = bindingContext.ValueProvider.GetValue("$type");
if (typeName == null)
throw new Exception("Cannot create abstract model");
var type = Type.GetType(typeName);
if (type == null)
throw new Exception("Cannot create abstract model");
if (!type.IsSubclassOf(modelType))
throw new Exception("Incorrect model type specified");
var model = Activator.CreateInstance(type);
// this line is very important.
// It updates the metadata (and type) of the model on the binding context,
// which allows MVC to properly reflect over the properties
// and assign their values. Without this,
// it will keep the type as specified in the parameter list,
// and only the properties of the base class will be populated
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
并在启动时将其注册为我的默认绑定器。
// in Global.asax.cs on application start
ModelBinders.Binders.DefaultBinder = new AbstractModelBinder();
我只在模型类型为抽象时更改行为,否则为默认行为。如果它是抽象的,但我不能创建类型的实例,或者类型名称不可用,等等,那么现在我抛出一个异常,但这是我稍后可以弄清楚的细节。这样,我就不需要在Actions的参数上使用任何属性,而且Json.NET类型说明符的使用已经尽我所能。让Json.NET为我反序列化模型会很好,但这是迄今为止我找到的最好的解决方案。
我想参考那些帮助我找到答案的帖子。更改asp.net MVC中的默认模型绑定器,asp.net MVC 3:具有继承/多态的DefaultModelBinder