它在测试版中工作
我有一个类型的对象,需要一些自定义反序列化才能使我的 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
.
我的问题
- 这可能是什么原因?
- 我怎样才能正确调试呢?我有 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).