Drop the extra MruCache and resolve public fields lazily into the existing cache entry
This commit is contained in:
1 parent
7c96e99fb3
commit
eb43ff4c98
2 files changed
+46
-20
No files matched your search
@@ -49,8 +49,6 @@ namespace NLog.Internal
|
||||
{
|
||||
private MruCache<Type, ObjectPropertyInfos> ObjectTypeCache => _objectTypeCache ?? System.Threading.Interlocked.CompareExchange(ref _objectTypeCache, new MruCache<Type, ObjectPropertyInfos>(10000), null) ?? _objectTypeCache;
|
||||
private MruCache<Type, ObjectPropertyInfos>? _objectTypeCache;
|
||||
private MruCache<Type, ObjectPropertyInfos> ObjectTypeFieldCache => _objectTypeFieldCache ?? System.Threading.Interlocked.CompareExchange(ref _objectTypeFieldCache, new MruCache<Type, ObjectPropertyInfos>(10000), null) ?? _objectTypeFieldCache;
|
||||
private MruCache<Type, ObjectPropertyInfos>? _objectTypeFieldCache;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private IObjectTypeTransformer ObjectTypeTransformation => _objectTypeTransformation ?? (_objectTypeTransformation = _serviceProvider?.GetService<IObjectTypeTransformer>() ?? this);
|
||||
private IObjectTypeTransformer? _objectTypeTransformation;
|
||||
@@ -97,13 +95,14 @@ namespace NLog.Internal
|
||||
}
|
||||
|
||||
var objectType = value.GetType();
|
||||
var typeCache = includeFields ? ObjectTypeFieldCache : ObjectTypeCache;
|
||||
if (!typeCache.TryGetValue(objectType, out var propertyInfos))
|
||||
var propertyInfos = CheckForObjectProperties(value) ? BuildObjectPropertyInfos(value, includeFields) : ObjectPropertyInfos.SimpleToString;
|
||||
if (includeFields && propertyInfos.FieldLookup is null)
|
||||
{
|
||||
propertyInfos = CheckForObjectProperties(value) ? BuildObjectPropertyInfos(value, includeFields) : ObjectPropertyInfos.SimpleToString;
|
||||
typeCache.TryAddValue(objectType, propertyInfos);
|
||||
// Remember that the public fields were resolved, so the type is not reflected again
|
||||
propertyInfos = new ObjectPropertyInfos(propertyInfos.Properties, propertyInfos.FastLookup, ArrayHelper.Empty<FastPropertyLookup>());
|
||||
}
|
||||
return new ObjectPropertyList(value, propertyInfos.Properties, propertyInfos.FastLookup);
|
||||
ObjectTypeCache.TryAddValue(objectType, propertyInfos);
|
||||
return propertyInfos.CreatePropertyList(value, includeFields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -162,15 +161,15 @@ namespace NLog.Internal
|
||||
#endif
|
||||
|
||||
Type objectType = value.GetType();
|
||||
if (!includeFields && ObjectTypeCache.TryGetValue(objectType, out var propertyInfos))
|
||||
if (ObjectTypeCache.TryGetValue(objectType, out var propertyInfos) && !(includeFields && propertyInfos.NeedsFieldLookup))
|
||||
{
|
||||
if (!propertyInfos.HasFastLookup)
|
||||
{
|
||||
var fastLookup = BuildFastLookup(propertyInfos.Properties, false);
|
||||
propertyInfos = new ObjectPropertyInfos(propertyInfos.Properties, fastLookup);
|
||||
propertyInfos = new ObjectPropertyInfos(propertyInfos.Properties, fastLookup, propertyInfos.FieldLookup);
|
||||
ObjectTypeCache.TryAddValue(objectType, propertyInfos);
|
||||
}
|
||||
objectPropertyList = new ObjectPropertyList(value, propertyInfos.Properties, propertyInfos.FastLookup);
|
||||
objectPropertyList = propertyInfos.CreatePropertyList(value, includeFields);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -200,15 +199,12 @@ namespace NLog.Internal
|
||||
}
|
||||
else if (properties.Length == 0)
|
||||
{
|
||||
// Nothing to show as properties, so fall back to the public fields when asked to.
|
||||
var fields = includeFields ? GetPublicFields(objectType) : ArrayHelper.Empty<FieldInfo>();
|
||||
if (fields.Length > 0)
|
||||
{
|
||||
// Fields are only reachable through the fast-lookup, so it cannot be built lazily
|
||||
return new ObjectPropertyInfos(ArrayHelper.Empty<PropertyInfo>(), BuildFieldLookup(fields));
|
||||
}
|
||||
|
||||
if (!includeFields)
|
||||
return ObjectPropertyInfos.SimpleToString;
|
||||
|
||||
// Nothing to show as properties, so keep the public fields aside for the IncludePublicFields fallback
|
||||
var fields = GetPublicFields(objectType);
|
||||
return new ObjectPropertyInfos(ArrayHelper.Empty<PropertyInfo>(), ArrayHelper.Empty<FastPropertyLookup>(), BuildFieldLookup(fields));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -626,20 +622,32 @@ namespace NLog.Internal
|
||||
{
|
||||
public readonly PropertyInfo[] Properties;
|
||||
public readonly FastPropertyLookup[]? FastLookup;
|
||||
/// <summary>Public fields of a type without public properties, null until resolved</summary>
|
||||
public readonly FastPropertyLookup[]? FieldLookup;
|
||||
|
||||
public static readonly ObjectPropertyInfos SimpleToString = new ObjectPropertyInfos(ArrayHelper.Empty<PropertyInfo>(), ArrayHelper.Empty<FastPropertyLookup>());
|
||||
|
||||
public ObjectPropertyInfos(PropertyInfo[] properties, FastPropertyLookup[]? fastLookup)
|
||||
public ObjectPropertyInfos(PropertyInfo[] properties, FastPropertyLookup[]? fastLookup, FastPropertyLookup[]? fieldLookup = null)
|
||||
{
|
||||
Properties = properties;
|
||||
FastLookup = fastLookup;
|
||||
FieldLookup = fieldLookup;
|
||||
}
|
||||
|
||||
public bool HasFastLookup => FastLookup != null;
|
||||
|
||||
public bool NeedsFieldLookup => FieldLookup is null && Properties.Length == 0 && FastLookup?.Length == 0;
|
||||
|
||||
public ObjectPropertyList CreatePropertyList(object value, bool includeFields)
|
||||
{
|
||||
if (includeFields && FieldLookup?.Length > 0)
|
||||
return new ObjectPropertyList(value, Properties, FieldLookup);
|
||||
return new ObjectPropertyList(value, Properties, FastLookup);
|
||||
}
|
||||
|
||||
public bool Equals(ObjectPropertyInfos other)
|
||||
{
|
||||
return ReferenceEquals(Properties, other.Properties) && FastLookup?.Length == other.FastLookup?.Length;
|
||||
return ReferenceEquals(Properties, other.Properties) && FastLookup?.Length == other.FastLookup?.Length && ReferenceEquals(FieldLookup, other.FieldLookup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -246,6 +246,24 @@ namespace NLog.UnitTests.Targets
|
||||
Assert.Equal(@"{""Name"":""John"",""Age"":42}", withFieldsAgain.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IncludePublicFields_SameTypeWithoutOptionFirst_StillSerializesFieldsLater()
|
||||
{
|
||||
var testObject = new ClassWithOnlyFields { Name = "John", Age = 42 };
|
||||
var jsonSerializer = new DefaultJsonSerializer(null);
|
||||
|
||||
var withoutFields = new StringBuilder();
|
||||
jsonSerializer.SerializeObject(testObject, withoutFields, new JsonSerializeOptions { IncludePublicFields = false });
|
||||
var withFields = new StringBuilder();
|
||||
jsonSerializer.SerializeObject(testObject, withFields, new JsonSerializeOptions { IncludePublicFields = true });
|
||||
var withoutFieldsAgain = new StringBuilder();
|
||||
jsonSerializer.SerializeObject(testObject, withoutFieldsAgain, new JsonSerializeOptions { IncludePublicFields = false });
|
||||
|
||||
Assert.DoesNotContain(@"""Age"":42", withoutFields.ToString());
|
||||
Assert.Equal(@"{""Name"":""John"",""Age"":42}", withFields.ToString());
|
||||
Assert.Equal(withoutFields.ToString(), withoutFieldsAgain.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IncludePublicFields_ShadowedField_IsWrittenOnce()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user