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;
    }
}
Другие вопросы по тегам