[Perf] Cache the metadata for known type "object"

Fixes #4377
This commit is contained in:
mnltejaswini 2016-04-21 15:07:44 -07:00
parent b3a65b485f
commit 0788edbd4b
4 changed files with 54 additions and 9 deletions

View File

@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
{
private readonly TypeCache _typeCache = new TypeCache();
private readonly Func<ModelMetadataIdentity, ModelMetadataCacheEntry> _cacheEntryFactory;
private readonly ModelMetadataCacheEntry _metadataCacheEntryForObjectType;
/// <summary>
/// Creates a new <see cref="DefaultModelMetadataProvider"/>.
@ -26,6 +27,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
DetailsProvider = detailsProvider;
_cacheEntryFactory = CreateCacheEntry;
_metadataCacheEntryForObjectType = GetMetadataCacheEntryForObjectType();
}
/// <summary>
@ -41,14 +44,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
throw new ArgumentNullException(nameof(modelType));
}
var key = ModelMetadataIdentity.ForType(modelType);
var cacheEntry = _typeCache.GetOrAdd(key, _cacheEntryFactory);
var cacheEntry = GetCacheEntry(modelType);
// We're relying on a safe race-condition for Properties - take care only
// to set the value onces the properties are fully-initialized.
if (cacheEntry.Details.Properties == null)
{
var key = ModelMetadataIdentity.ForType(modelType);
var propertyDetails = CreatePropertyDetails(key);
var properties = new ModelMetadata[propertyDetails.Length];
@ -71,12 +73,30 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
throw new ArgumentNullException(nameof(modelType));
}
var key = ModelMetadataIdentity.ForType(modelType);
var cacheEntry = GetCacheEntry(modelType);
var cacheEntry = _typeCache.GetOrAdd(key, _cacheEntryFactory);
return cacheEntry.Metadata;
}
private ModelMetadataCacheEntry GetCacheEntry(Type modelType)
{
ModelMetadataCacheEntry cacheEntry;
// Perf: We cached model metadata cache entry for "object" type to save ConcurrentDictionary lookups.
if (modelType == typeof(object))
{
cacheEntry = _metadataCacheEntryForObjectType;
}
else
{
var key = ModelMetadataIdentity.ForType(modelType);
cacheEntry = _typeCache.GetOrAdd(key, _cacheEntryFactory);
}
return cacheEntry;
}
private ModelMetadataCacheEntry CreateCacheEntry(ModelMetadataIdentity key)
{
var details = CreateTypeDetails(key);
@ -84,6 +104,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
return new ModelMetadataCacheEntry(metadata, details);
}
private ModelMetadataCacheEntry GetMetadataCacheEntryForObjectType()
{
var key = ModelMetadataIdentity.ForType(typeof(object));
var entry = CreateCacheEntry(key);
return entry;
}
/// <summary>
/// Creates a new <see cref="ModelMetadata"/> from a <see cref="DefaultMetadataDetails"/>.
/// </summary>

View File

@ -49,7 +49,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
private ViewDataDictionary GetViewDataDictionary(ControllerContext context)
{
var serviceProvider = context.HttpContext.RequestServices;
return new ViewDataDictionary(
_modelMetadataProvider,
context.ModelState);

View File

@ -44,6 +44,20 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
Assert.Same(metadata1.ValidationMetadata, metadata2.ValidationMetadata);
}
[Fact]
public void GetMetadataForObjectType_Cached()
{
// Arrange
var provider = CreateProvider();
// Act
var metadata1 = provider.GetMetadataForType(typeof(object));
var metadata2 = provider.GetMetadataForType(typeof(object));
// Assert
Assert.Same(metadata1, metadata2);
}
[Fact]
public void GetMetadataForProperties_IncludesAllProperties()
{

View File

@ -1074,9 +1074,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
protected override DefaultMetadataDetails CreateTypeDetails(ModelMetadataIdentity key)
{
var entry = base.CreateTypeDetails(key);
return new DefaultMetadataDetails(
key,
new ModelAttributes(_attributes.Concat(entry.ModelAttributes.TypeAttributes).ToArray()));
if (_attributes?.Length > 0)
{
return new DefaultMetadataDetails(
key,
new ModelAttributes(_attributes.Concat(entry.ModelAttributes.TypeAttributes).ToArray()));
}
return entry;
}
protected override DefaultMetadataDetails[] CreatePropertyDetails(ModelMetadataIdentity key)