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 => _objectTypeCache ?? System.Threading.Interlocked.CompareExchange(ref _objectTypeCache, new MruCache<Type, ObjectPropertyInfos>(10000), null) ?? _objectTypeCache;
|
||||||
private MruCache<Type, ObjectPropertyInfos>? _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 readonly IServiceProvider _serviceProvider;
|
||||||
private IObjectTypeTransformer ObjectTypeTransformation => _objectTypeTransformation ?? (_objectTypeTransformation = _serviceProvider?.GetService<IObjectTypeTransformer>() ?? this);
|
private IObjectTypeTransformer ObjectTypeTransformation => _objectTypeTransformation ?? (_objectTypeTransformation = _serviceProvider?.GetService<IObjectTypeTransformer>() ?? this);
|
||||||
private IObjectTypeTransformer? _objectTypeTransformation;
|
private IObjectTypeTransformer? _objectTypeTransformation;
|
||||||
@@ -97,13 +95,14 @@ namespace NLog.Internal
|
|||||||
}
|
}
|
||||||
|
|
||||||
var objectType = value.GetType();
|
var objectType = value.GetType();
|
||||||
var typeCache = includeFields ? ObjectTypeFieldCache : ObjectTypeCache;
|
var propertyInfos = CheckForObjectProperties(value) ? BuildObjectPropertyInfos(value, includeFields) : ObjectPropertyInfos.SimpleToString;
|
||||||
if (!typeCache.TryGetValue(objectType, out var propertyInfos))
|
if (includeFields && propertyInfos.FieldLookup is null)
|
||||||
{
|
{
|
||||||
propertyInfos = CheckForObjectProperties(value) ? BuildObjectPropertyInfos(value, includeFields) : ObjectPropertyInfos.SimpleToString;
|
// Remember that the public fields were resolved, so the type is not reflected again
|
||||||
typeCache.TryAddValue(objectType, propertyInfos);
|
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>
|
/// <summary>
|
||||||
@@ -162,15 +161,15 @@ namespace NLog.Internal
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
Type objectType = value.GetType();
|
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)
|
if (!propertyInfos.HasFastLookup)
|
||||||
{
|
{
|
||||||
var fastLookup = BuildFastLookup(propertyInfos.Properties, false);
|
var fastLookup = BuildFastLookup(propertyInfos.Properties, false);
|
||||||
propertyInfos = new ObjectPropertyInfos(propertyInfos.Properties, fastLookup);
|
propertyInfos = new ObjectPropertyInfos(propertyInfos.Properties, fastLookup, propertyInfos.FieldLookup);
|
||||||
ObjectTypeCache.TryAddValue(objectType, propertyInfos);
|
ObjectTypeCache.TryAddValue(objectType, propertyInfos);
|
||||||
}
|
}
|
||||||
objectPropertyList = new ObjectPropertyList(value, propertyInfos.Properties, propertyInfos.FastLookup);
|
objectPropertyList = propertyInfos.CreatePropertyList(value, includeFields);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,15 +199,12 @@ namespace NLog.Internal
|
|||||||
}
|
}
|
||||||
else if (properties.Length == 0)
|
else if (properties.Length == 0)
|
||||||
{
|
{
|
||||||
// Nothing to show as properties, so fall back to the public fields when asked to.
|
if (!includeFields)
|
||||||
var fields = includeFields ? GetPublicFields(objectType) : ArrayHelper.Empty<FieldInfo>();
|
return ObjectPropertyInfos.SimpleToString;
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
@@ -626,20 +622,32 @@ namespace NLog.Internal
|
|||||||
{
|
{
|
||||||
public readonly PropertyInfo[] Properties;
|
public readonly PropertyInfo[] Properties;
|
||||||
public readonly FastPropertyLookup[]? FastLookup;
|
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 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;
|
Properties = properties;
|
||||||
FastLookup = fastLookup;
|
FastLookup = fastLookup;
|
||||||
|
FieldLookup = fieldLookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasFastLookup => FastLookup != null;
|
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)
|
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());
|
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]
|
[Fact]
|
||||||
public void IncludePublicFields_ShadowedField_IsWrittenOnce()
|
public void IncludePublicFields_ShadowedField_IsWrittenOnce()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user