1

它在测试版中工作

我有一个类型的对象,需要一些自定义反序列化才能使我的 web-api 的其余部分工作ChangeSet<T>ChangeSet它在 asp.net mvc 4 beta 中工作,但在 rc 中它被破坏了,而且我发现很难调试。

我的场景

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

我的问题

为了允许我填充属性,我需要访问原始 json,这就是我需要自定义 MediaTypeFormatter 的原因。(xml-serializer 有一个 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;
    }
}

这些是 ChangeSet 类:

[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).
4

1 回答 1

2

我终于弄清楚是什么坏了。ModelBinder 验证使用反射在 getter 上中断。在那个后续问题中,我问它为什么会坏。

在这个问题中,我追求的是什么,我刚刚回答了。因此,我要结束这个问题。

于 2012-08-01T12:06:12.743 回答