我有一个抽象对象BaseObject(示例名称),该对象具有2个特定于国家/地区的实现,即UsaObject和CanadaObject,它们都扩展了BaseObject。
public abstract class BaseObject...
public class UsaObject : BaseObject...
public class CanadaObject : BaseObject...
在我的控制器中,我有一个动作
public void UpdateObject(BaseObject object) {
...
}
该操作仅在BaseObject上调用方法,并且不需要强制转换。
我使用Json.NET执行序列化,当我序列化对象时,我使用JsonSerializationSettings.TypeNameHandling.Objects将类的类型添加到对象中。这使我可以轻松地将数据正确地发送到UI并根据国家/地区更改显示的内容,而不必担心直到需要的确切时间。
但是,我在将此数据发布回UpdateObject方法时遇到问题。它在反序列化时失败,并显示以下错误(请注意,尽管我根本不怀疑这是引起问题的原因,但我们正在使用Glimpse对应用程序执行性能分析),该错误被IExceptionFilter.OnException调用捕获:
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
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句