diff options
Diffstat (limited to 'src/System.Web.Mvc/CachedAssociatedMetadataProvider`1.cs')
-rw-r--r-- | src/System.Web.Mvc/CachedAssociatedMetadataProvider`1.cs | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/System.Web.Mvc/CachedAssociatedMetadataProvider`1.cs b/src/System.Web.Mvc/CachedAssociatedMetadataProvider`1.cs new file mode 100644 index 00000000..b0a00cb2 --- /dev/null +++ b/src/System.Web.Mvc/CachedAssociatedMetadataProvider`1.cs @@ -0,0 +1,94 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.Caching; + +namespace System.Web.Mvc +{ + public abstract class CachedAssociatedMetadataProvider<TModelMetadata> : AssociatedMetadataProvider + where TModelMetadata : ModelMetadata + { + private static ConcurrentDictionary<Type, string> _typeIds = new ConcurrentDictionary<Type, string>(); + private string _cacheKeyPrefix; + private CacheItemPolicy _cacheItemPolicy = new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(20) }; + private ObjectCache _prototypeCache; + + protected internal CacheItemPolicy CacheItemPolicy + { + get { return _cacheItemPolicy; } + set { _cacheItemPolicy = value; } + } + + protected string CacheKeyPrefix + { + get + { + if (_cacheKeyPrefix == null) + { + _cacheKeyPrefix = "MetadataPrototypes::" + GetType().GUID.ToString("B"); + } + return _cacheKeyPrefix; + } + } + + protected internal ObjectCache PrototypeCache + { + get { return _prototypeCache ?? MemoryCache.Default; } + set { _prototypeCache = value; } + } + + protected sealed override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) + { + // If metadata is being created for a property then containerType != null && propertyName != null + // If metadata is being created for a type then containerType == null && propertyName == null, so we have to use modelType for the cache key. + Type typeForCache = containerType ?? modelType; + string cacheKey = GetCacheKey(typeForCache, propertyName); + TModelMetadata prototype = PrototypeCache.Get(cacheKey) as TModelMetadata; + if (prototype == null) + { + prototype = CreateMetadataPrototype(attributes, containerType, modelType, propertyName); + PrototypeCache.Add(cacheKey, prototype, CacheItemPolicy); + } + + return CreateMetadataFromPrototype(prototype, modelAccessor); + } + + // New override for creating the prototype metadata (without the accessor) + protected abstract TModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes, Type containerType, Type modelType, string propertyName); + + // New override for applying the prototype + modelAccess to yield the final metadata + protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype, Func<object> modelAccessor); + + internal string GetCacheKey(Type type, string propertyName = null) + { + propertyName = propertyName ?? String.Empty; + return CacheKeyPrefix + GetTypeId(type) + propertyName; + } + + public sealed override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName) + { + return base.GetMetadataForProperty(modelAccessor, containerType, propertyName); + } + + protected sealed override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor) + { + return base.GetMetadataForProperty(modelAccessor, containerType, propertyDescriptor); + } + + public sealed override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType) + { + return base.GetMetadataForProperties(container, containerType); + } + + public sealed override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType) + { + return base.GetMetadataForType(modelAccessor, modelType); + } + + private static string GetTypeId(Type type) + { + // It's fine using a random Guid since we store the mapping for types to guids. + return _typeIds.GetOrAdd(type, _ => Guid.NewGuid().ToString("B")); + } + } +} |