JSON ContractResolver создается при каждом выпуске запроса
У меня есть приложение.NET Core Web Api 2.1, в котором я только сериализую свойства, запрошенные клиентом.
Пример: GET orders/1?select=Id,TotalAmount
Пример: GET orders/1?select=Id,CustomerName,DeliveryAddress,Location,ZipCode
Для этого приложение создает объект в каждом запросе (Lifetime Scoped), чтобы получить запрошенные свойства.
Затем я создал Custom ContractResolver
класс, который создается в каждом запросе глобальным IResultFilter
:
public class SerializationFilter : IResultFilter
{
private readonly IApiQueryParameters _apiQueryParameters;
public SerializationFilter(IApiQueryParameters apiQueryParameters)
{
this._apiQueryParameters = apiQueryParameters;
}
public void OnResultExecuting(ResultExecutingContext context)
{
var objectResult = context.Result as ObjectResult;
if (objectResult != null)
{
var contractResolver = new SelectiveResourceContractResolver(this._apiQueryParameters);
var serializerSettings = new JsonSerializerSettings
{
ContractResolver = contractResolver
};
var jsonFormatter = new JsonOutputFormatter(
serializerSettings,
ArrayPool<char>.Shared);
objectResult.Formatters.Add(jsonFormatter);
}
}
}
Теперь это работает для первого запроса, сделанного после того, как приложение находится в сети. Второй запрос создает ContractResolver правильно, но ответ не является правильным. Возвращенные сериализованные свойства не являются запрошенными клиентом.
Отлаживая код, я заметил, что при создании SelectiveResourceContractResolver он получает доступ к методу конструктора,
public SelectiveResourceContractResolver(IApiQueryParameters apiQueryParameters)
{
this._apiQueryParameters = apiQueryParameters;
}
Кроме того, он запускает переопределенный метод CreateProperty:
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
Но во втором запросе CreatePropertyMethod
не запускается.
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize =
instance =>
{
...
};
return property;
}
Во второй раз он выполняет property.ShouldSerialize = instance =>
часть. Я думаю, что какое-то кэширование выполняется приложением, но более странным является то, что ContractResolver создается при каждом запросе (он не устанавливается глобально в Startup.cs) при выполнении фильтра. Кроме того, когда я отлаживаю его во втором запросе, свойство IApiQueryParameters в ContractResolver имеет все значения первого запроса, а не второго выполнения конструктора.
Что мне здесь не хватает?
0 ответов
Я предполагаю, что ваш SelectiveResourceContractResolver
наследует от DefaultContractResolver
,
DefaultContractResolver.ResolveContract
, который вызывается внутри во время сериализации, кэширует контракты в статическом поточно-ориентированном словаре, поэтому CreateContract
(который в свою очередь вызывает CreateProperty
) будет вызываться только один раз для каждого типа, пока выполняется процесс WebAPI.
Это потому что зовет CreateContract
стоит дорого, и отсутствие кэширования контрактов серьезно повлияет на производительность сериализации.
Если вам нужно только кешировать контракты для каждого ответа, вы можете создать свой собственный кеш экземпляра в SelectiveResourceContractResolver
и переопределить ResolveContract
вместо этого использовать ваш кеш:
public class InstanceCachingContractResolver : DefaultContractResolver
{
private readonly Dictionary<Type, JsonContract> contractCache = new Dictionary<Type, JsonContract>();
public override JsonContract ResolveContract(Type type)
{
if (!contractCache.TryGetValue(type, out JsonContract contract))
{
contract = CreateContract(type);
contractCache.Add(type, contract);
}
return contract;
}
}