等于引用解析程序将Id引发异常

时间:2019-01-16 12:04:22

标签: c# exception reference json.net

现在使用我在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服务。这使用RestSharpRestSharp.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));
        }
    }
}

您能告诉我参考解析器怎么了吗?

0 个答案:

没有答案