自定义JsonMediaTypeFormatter在get_DeclaringMethod()上引发InvalidOpe



它在Beta版中工作

我有一个从ChangeSet派生的ChangeSet<T>类型的对象,它需要一些自定义的反序列化,以便我的web api的其余部分能够工作。它在asp.net mvc 4测试版中工作,但在rc中它坏了,在某种程度上我发现很难调试。

我的场景

ChangeSet可以表示ID列表所描述的多个T对象的更改。我想反序列化为某种类型的对象数组或字典,以描述应该更改T对象的哪些属性,但后来在反序列化为正确的数据类型时遇到了问题。因此,我引入了T类型的Change属性。新的问题是,并非所有Change属性都应该持久化。只有json中包含的那些。所以我让一个Properties属性包含一个字符串列表。

我的问题

为了允许我填充Properties,我需要访问原始json,这就是为什么我需要一个自定义的MediaTypeFormatter。(xml序列化程序有一个SetSerializer(..),但JsonSerializer没有这样的方法。在下面的ReadFromStreamAsync中,我这样做了,问题是它崩溃了,我无法进入崩溃的地步来获得内部异常。我得到的只是对我的请求的以下json响应:

{
    "ExceptionType": "System.InvalidOperationException",
    "Message":       "Method may only be called on a Type for which Type.IsGenericParameter is true.",
    "StackTrace":    "   at System.RuntimeType.get_DeclaringMethod()"
}

如果我删除ChangeSetJsonFormatter,代码不会中断,但我当然没有Properties

我的问题

  1. 这可能是什么原因造成的
  2. 如何正确调试?我有Log4Net,但我应该看什么?我能以某种方式介入这个问题吗

代码

这是我的FormatterConfig:

public class FormatterConfig
{
    public static void RegisterGlobalFormatters(MediaTypeFormatterCollection formatters)
    {
        var jsonSerializerSettings = formatters.JsonFormatter.SerializerSettings;
        jsonSerializerSettings.Converters.Add(new IsoDateTimeConverter());
        jsonSerializerSettings.NullValueHandling = NullValueHandling.Ignore;
        // At index 0 so that it will try this before the default handler.
        formatters.Insert(0, new ChangeSetJsonFormatter(jsonSerializerSettings));
        formatters.Remove(formatters.XmlFormatter);
    }
}

这是我的自定义媒体格式化程序(很抱歉长度太长):

public class ChangeSetJsonFormatter : JsonMediaTypeFormatter
{
    private static readonly Type ChangeSetType = typeof (ChangeSet<Entity>);
    public ChangeSetJsonFormatter(JsonSerializerSettings jsonSerializerSettings)
    {
        SerializerSettings = jsonSerializerSettings;
    }
    public override bool CanReadType(Type type)
    {
        return type.IsGenericType && type.Namespace == ChangeSetType.Namespace && type.Name == ChangeSetType.Name;
    }
    public override bool CanWriteType(Type type)
    {
        return type.IsGenericType && type.Namespace == ChangeSetType.Namespace && type.Name == ChangeSetType.Name;
    }
    public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
    {
        var task = Task.Factory.StartNew(() =>
        {
            using (var streamReader = new StreamReader(stream))
            {
                var jsonSource = streamReader.ReadToEnd();
                var deserializedObject =
                    JsonConvert.DeserializeObject(jsonSource, type,
                                                SerializerSettings);
                var changeSet = deserializedObject as ChangeSet;
                if (changeSet != null &&
                    (changeSet.Properties == null ||
                    changeSet.Properties.Count == 0))
                {
                    var properties =
                        JObject.Parse(jsonSource)["Change"]
                               .Select(t => ((JProperty) t).Name)
                               .ToList();
                    changeSet.Properties = properties;
                }
                return deserializedObject;
            }
        });
        return task;
    }
}

这些是变更集类:

[DataContract]
public abstract class ChangeSet
{
    [DataMember]
    public IList<string> Properties { get; set; }
    [DataMember]
    public IList<int> IdList { get; set; }
}
[DataContract]
public class ChangeSet<T> : ChangeSet where T : Entity
{
    [DataMember]
    public T Change { get; set; }
    public Dictionary<PropertyInfo, object> Changes
    {
        get 
        {
            if (Properties == null || Properties.Count == 0)
                return new Dictionary<PropertyInfo, object>();
            return typeof (T)
                .GetProperties()
                .Where(pi => Properties.Contains(pi.Name))
                .ToDictionary(pi => pi, pi => pi.GetValue(Change, null));
        }
    }
}

编辑:根据本文档,我能够为服务堆栈生成跟踪。现在,如果我能为我的例外获得一个条目就好了。以下是默认侦听器的输出:

System.Web.Http.Request: ;;http://localhost:50500/MyService/User?_dc=1343396746443
System.Web.Http.Controllers: DefaultHttpControllerSelector;SelectController;Route='controller:User'
System.Web.Http.Controllers: DefaultHttpControllerSelector;SelectController;User
System.Web.Http.Controllers: HttpControllerDescriptor;CreateController;
System.Web.Http.Controllers: DefaultHttpControllerActivator;Create;
System.Web.Http.Controllers: DefaultHttpControllerActivator;Create;MyCompany.Admin.Service.Controllers.UserController
System.Web.Http.Controllers: HttpControllerDescriptor;CreateController;MyCompany.Admin.Service.Controllers.UserController
System.Web.Http.Controllers: UserController;ExecuteAsync;
System.Web.Http.Action: ApiControllerActionSelector;SelectAction;
System.Web.Http.Action: ApiControllerActionSelector;SelectAction;Selected action 'Put(ChangeSet`1 changeSet)'
System.Web.Http.ModelBinding: HttpActionBinding;ExecuteBindingAsync;
System.Web.Http.ModelBinding: FormatterParameterBinding;ExecuteBindingAsync;Binding parameter 'changeSet'
System.Net.Http.Formatting: ChangeSetJsonFormatter;ReadFromStreamAsync;Type='ChangeSet`1', content-type='application/json'
System.Net.Http.Formatting: ChangeSetJsonFormatter;ReadFromStreamAsync;Value read='MyCompany.Admin.Service.Data.ChangeSet`1[MyCompany.Data.Model.User]'
System.Web.Http.ModelBinding: FormatterParameterBinding;ExecuteBindingAsync;
System.Web.Http.ModelBinding: HttpActionBinding;ExecuteBindingAsync;
System.Web.Http.Controllers: UserController;ExecuteAsync;
System.Net.Http.Formatting: DefaultContentNegotiator;Negotiate;Type='HttpError', formatters=[JsonMediaTypeFormatterTracer, JsonMediaTypeFormatterTracer, FormUrlEncodedMediaTypeFormatterTracer, FormUrlEncodedMediaTypeFormatterTracer]
System.Net.Http.Formatting: JsonMediaTypeFormatter;GetPerRequestFormatterInstance;Obtaining formatter of type 'JsonMediaTypeFormatter' for type='HttpError', mediaType='application/json; charset=utf-8'
System.Net.Http.Formatting: JsonMediaTypeFormatter;GetPerRequestFormatterInstance;Will use same 'JsonMediaTypeFormatter' formatter
System.Net.Http.Formatting: DefaultContentNegotiator;Negotiate;Selected formatter='JsonMediaTypeFormatter', content-type='application/json; charset=utf-8'
System.Web.Http.Request: ;;Content-type='application/json; charset=utf-8', content-length=unknown
System.Net.Http.Formatting: JsonMediaTypeFormatter;WriteToStreamAsync;Value='System.Web.Http.HttpError', type='HttpError', content-type='application/json; charset=utf-8'
System.Net.Http.Formatting: JsonMediaTypeFormatter;WriteToStreamAsync;
System.Web.Http.Controllers: UserController;Dispose;
System.Web.Http.Controllers: UserController;Dispose;
The thread '<No Name>' (0x1ea0) has exited with code 0 (0x0).
The thread '<No Name>' (0x1d6c) has exited with code 0 (0x0).
The thread '<No Name>' (0x19b4) has exited with code 0 (0x0).

我终于弄清楚是什么坏了。ModelBinder验证使用反射中断getter。在接下来的问题中,我问它为什么会坏。

在这个问题上,我想知道发生了什么,我刚刚回答了这个问题。今天我结束这个问题。

相关内容

  • 没有找到相关文章

最新更新