после использования созданного мной 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));
}
}
}
Подскажите, пожалуйста, что не так с эталонным решателем?