现在使用我在dbc的帮助下创建的here的解析器后,它不再正常工作。
我总是收到以下异常:
读取对象引用“ 87”时出错。路径“ $ values [1] .EntityB.Bar”,第1行,位置49698。
内部异常:
已经为值“ EntityB”分配了另一个ID。此错误可能是由于对象在反序列化过程中多次重复使用而引起的,可以通过设置ObjectCreationHandling.Replace进行修复。
我也尝试了建议的ObjectCreationHandling,但是它不起作用。
我尝试在add和get方法周围添加锁语句,但是仍然不能解决问题。
如另一则帖子here所述,在解析器下面:
public abstract class EquivalencingReferenceResolver : IReferenceResolver
{
private readonly IReferenceResolver _defaultResolver;
public string GetReference(object context, object value)
{
var representative = this.GetOrAddRepresentativeObject(value);
return this._defaultResolver.GetReference(context, representative);
}
public bool IsReferenced(object context, object value)
{
return this.TryGetRepresentativeObject(value, out var representative) && this._defaultResolver.IsReferenced(context, representative);
}
public object ResolveReference(object context, string reference)
{
return this._defaultResolver.ResolveReference(context, reference);
}
public void AddReference(object context, string reference, object value)
{
var representative = this.GetOrAddRepresentativeObject(value);
this._defaultResolver.AddReference(context, reference, representative);
}
protected EquivalencingReferenceResolver(IReferenceResolver defaultResolver)
{
this._defaultResolver = defaultResolver ?? throw new ArgumentNullException();
}
protected virtual bool TryGetRepresentativeObject(object obj, out object representative)
{
representative = obj;
return true;
}
protected virtual object GetOrAddRepresentativeObject(object obj)
{
return obj;
}
}
以及具体的实现方式:
public class SelectiveValueEqualityReferenceResolver : EquivalencingReferenceResolver
{
private readonly Dictionary<Type, Dictionary<object, object>> _representatives;
public SelectiveValueEqualityReferenceResolver(IReferenceResolver defaultResolver)
: base(defaultResolver)
{
this._representatives = new Dictionary<Type, Dictionary<object, object>>();
}
protected override bool TryGetRepresentativeObject(object obj, out object representative)
{
var type = obj.GetType();
if (type.GetTypeInfo().IsClass && this._representatives.TryGetValue(type, out var typedItems))
return typedItems.TryGetValue(obj, out representative);
return base.TryGetRepresentativeObject(obj, out representative);
}
protected override object GetOrAddRepresentativeObject(object obj)
{
var type = obj.GetType();
if (!type.GetTypeInfo().IsClass)
return base.GetOrAddRepresentativeObject(obj);
if (!this._representatives.TryGetValue(type, out var typedItems))
{
typedItems = new Dictionary<object, object>();
this._representatives.Add(type, typedItems);
}
if (!typedItems.TryGetValue(obj, out var representative))
representative = typedItems[obj] = obj;
return representative;
}
}
序列化类:
public static class Serialization
{
private static readonly ILogger Log = Serilog.Log.ForContext(typeof(Serialization));
public static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
NullValueHandling = NullValueHandling.Ignore,
FloatParseHandling = FloatParseHandling.Decimal,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
DateTimeZoneHandling = DateTimeZoneHandling.Local,
Formatting = Formatting.None,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
ReferenceResolverProvider = () => new CustomReferenceResolver(new JsonSerializer().ReferenceResolver),
Error = (sender, args) => Log.Error(args.ErrorContext.Error, $"Error while (de)serializing: {args.ErrorContext}; object: {args.CurrentObject}")
};
private static readonly JsonSerializerSettings SimpleJsonSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.None,
NullValueHandling = NullValueHandling.Ignore,
FloatParseHandling = FloatParseHandling.Decimal,
Formatting = Formatting.Indented,
Error = (sender, args) => Log.Error(args.ErrorContext.Error, $"Error while (de)serializing: {args.ErrorContext}; object: {args.CurrentObject}")
};
public static string Serialize<T>(T item)
{
return Serialize(item, JsonSerializerSettings);
}
public static string SerializeSimple<T>(T item)
{
return Serialize(item, SimpleJsonSerializerSettings);
}
public static string Serialize<T>(T item, JsonSerializerSettings settings)
{
var jsonString = JsonConvert.SerializeObject(item, settings);
return jsonString;
}
public static T Deserialize<T>(string data)
{
return Deserialize<T>(data, JsonSerializerSettings);
}
public static T DeserializeSimple<T>(string data)
{
return Deserialize<T>(data, SimpleJsonSerializerSettings);
}
public static T Deserialize<T>(string data, JsonSerializerSettings settings)
{
try
{
return JsonConvert.DeserializeObject<T>(data, settings);
}
catch (Exception ex)
{
Log.Error(ex, "Exception in deserializing data.");
throw;
}
}
}
我在其中使用的安装程序当前是Web服务。这使用RestSharp和RestSharp.Newtonsoft.Json来解析来自Web服务的响应。该Web服务基本上还使用Newtonsoft进行具有相同JSON设置的响应。 (因为它基本上是相同的服务,只是在其他节点上)。
对于主机,我使用以下配置:
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration
{
IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always
};
ConfigureIocContainer(appBuilder, config);
config.MapHttpAttributeRoutes();
config.ConfigureDefaultRoutes()
.ConfigureSwagger(ServiceDescription.DisplayName)
.Services.Add(typeof(IExceptionLogger), new SerilogExceptionLogger());
appBuilder.ConfigureAuthenticationSchema(AuthenticationSchemes.Ntlm);
config.Formatters.XmlFormatter.UseXmlSerializer = true;
config.Formatters.JsonFormatter.SerializerSettings = Serialization.JsonSerializerSettings;
appBuilder.UseWebApi(config);
}
所以这里的设置基本相同。主机是带有Swagger的自托管Owin主机。
通过以下方式调用服务:
private static string RemoteUrl = "http://localhost/";
private IRestClient GetAvailableClient()
{
var client = new RestClient(RemoteUrl)
{
Authenticator = new NtlmAuthenticator(),
RemoteCertificateValidationCallback = this.RemoteCertificateValidationCallback,
};
client.AddHandler("application/json", MyJsonSerializer);
client.AddHandler("text/json", MyJsonSerializer);
client.AddHandler("text/x-json", MyJsonSerializer);
client.AddHandler("text/javascript", MyJsonSerializer);
client.AddHandler("*+json", MyJsonSerializer);
return client;
}
protected TResult ExecuteRequest<TResult>([CallerMemberName] string requestName = null, Method method = Method.GET, object parameter = null) where TResult : new()
{
IRestResponse<TResult> result = null;
try
{
var client = this.GetAvailableClient();
var request = BuildRequest(this._serviceName, requestName, method, parameter);
result = client.Execute<TResult>(request);
if (result.IsSuccessful)
return result.Data;
throw GetExceptionFromResponse(result);
}
catch (Exception ex)
{
this._logger.Error(ex, $"Error while calling Remote Service {this._serviceName} with Request {requestName} and Parameter {parameter} and ResponseContent {result?.Content}");
throw;
}
}
当我在异常处理中创建一个断点并尝试使用Serialization.Deserialze<TResponse>(result.Content)
手动对其进行反序列化时,它将引发相同的异常。
/ /更新
似乎解析器不是线程安全的。我不确定是哪一个,但我认为Internal Newtonsoft Reference Resolver不是Threadsafe的,因为我也使用lock statements
进行了尝试,但这也没有解决问题。
在我的自定义实现下方
public class CustomReferenceResolver : IReferenceResolver
{
private readonly Dictionary<object, ReferencedContext> _referencedContexts = new Dictionary<object, ReferencedContext>();
private readonly object _referencedContextsLock = new object();
public object ResolveReference(object context, string reference)
{
lock (this._referencedContextsLock)
return this._referencedContexts.ContainsKey(context) ? this._referencedContexts[context].GetValueByKey(reference) : null;
}
public string GetReference(object context, object value)
{
lock (this._referencedContextsLock)
return this.IsReferenced(context, value) ? this._referencedContexts[context].GetKeyByValue(value) : this.AddObjectInternal(context, value);
}
public bool IsReferenced(object context, object value)
{
lock (this._referencedContexts)
return this._referencedContexts.TryGetValue(context, out var currentContext) && currentContext.IsObjectReferenced(value);
}
public void AddReference(object context, string reference, object value)
{
this.AddObjectInternal(context, value, reference);
}
private string AddObjectInternal(object context, object value, string reference = null)
{
lock (this._referencedContextsLock)
{
if (this._referencedContexts.TryGetValue(context, out var referenceContext))
return referenceContext.AddObject(value, reference);
referenceContext = new ReferencedContext();
this._referencedContexts.Add(context, referenceContext);
return referenceContext.AddObject(value, reference);
}
}
private class ReferencedContext
{
private int _idCount;
private readonly Dictionary<string, object> _referencedObjects = new Dictionary<string, object>();
private readonly object _referenceLock = new object();
private string GetNextKey()
{
return Interlocked.Increment(ref this._idCount).ToString();
}
public object GetValueByKey(string key)
{
lock (this._referenceLock)
return this._referencedObjects.ContainsKey(key) ? this._referencedObjects[key] : null;
}
public string GetKeyByValue(object value)
{
lock (this._referenceLock)
return this._referencedObjects.SingleOrDefault(x => x.Value.Equals(value)).Key;
}
public string AddObject(object value, string key = null)
{
lock (this._referenceLock)
{
if (this.IsObjectReferenced(value))
return this.GetKeyByValue(value);
if (key == null)
key = this.GetNextKey();
this._referencedObjects.Add(key, value);
return key;
}
}
public bool IsObjectReferenced(object value)
{
lock (this._referenceLock)
return this._referencedObjects.Any(x => x.Value.Equals(value));
}
}
}
您能告诉我参考解析器怎么了吗?