Эквивалент эталонного равноправия создает исключение для идентификатора - PullRequest
0 голосов
/ 16 января 2019

после использования созданного мной Resolver здесь с помощью dbc он больше не работает должным образом.

Я всегда получаю следующее исключение:

Ошибка чтения ссылки на объект «87». Путь '$ values ​​[1] .EntityB.Bar', строка 1, позиция 49698.

Внутреннее исключение:

Другой идентификатор уже назначен для значения «EntityB». Эта ошибка может быть вызвана многократным повторным использованием объекта во время десериализации и может быть исправлена ​​с помощью параметра ObjectCreationHandling.Replace.

Я также попробовал предложенный ObjectCreationHandling, но он не работает.

Я пытался добавить операторы блокировки вокруг методов add и get, но это по-прежнему не решает проблему.

Ниже Резольвера, как упоминалось в другом посте здесь :

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;
        }
    }
}

Мои настройки, где я использую это, в настоящее время являются веб-сервисом. При этом используются RestSharp и RestSharp.Newtonsoft.Json для разрешения ответа от веб-службы. Этот веб-сервис в основном использует также 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.

Звонок в Сервис осуществляется следующим образом:

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), выдается то же исключение.

/ / Обновление

Кажется, что Resolver не является Threadsafe. Я не совсем уверен, какой именно, но я предполагаю, что Внутренний эталонный резольвер Newtonsoft не является 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));
        }
    }
}

Подскажите, пожалуйста, что не так с эталонным решателем?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...