See More

using JSONAPI.Attributes; using JSONAPI.Json; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace JSONAPI.Core { public class ModelManager : IModelManager { public ModelManager(IPluralizationService pluralizationService) { _pluralizationService = pluralizationService; } protected IPluralizationService _pluralizationService = null; public IPluralizationService PluralizationService { get { return _pluralizationService; } } #region Cache storage protected Lazy> _idProperties = new Lazy>( () => new Dictionary() ); protected Lazy>> _propertyMaps = new Lazy>>( () => new Dictionary>() ); protected Lazy> _jsonKeysForType = new Lazy>( () => new Dictionary() ); protected Lazy> _isSerializedAsMany = new Lazy>( () => new Dictionary() ); protected Lazy> _getElementType = new Lazy>( () => new Dictionary() ); #endregion #region Id property determination public PropertyInfo GetIdProperty(Type type) { PropertyInfo idprop = null; var idPropCache = _idProperties.Value; lock (idPropCache) { if (idPropCache.TryGetValue(type, out idprop)) return idprop; // First, look for UseAsIdAttribute idprop = type.GetProperties() .Where(p => p.CustomAttributes.Any(attr => attr.AttributeType == typeof(UseAsIdAttribute))) .FirstOrDefault(); if (idprop == null) { idprop = type.GetProperty("Id"); } if (idprop == null) throw new InvalidOperationException(String.Format("Unable to determine Id property for type {0}", type)); idPropCache.Add(type, idprop); } return idprop; } #endregion #region Property Maps protected IDictionary GetPropertyMap(Type type) { Dictionary propMap = null; var propMapCache = _propertyMaps.Value; lock (propMapCache) { if (propMapCache.TryGetValue(type, out propMap)) return propMap; propMap = new Dictionary(); PropertyInfo[] props = type.GetProperties(); foreach (PropertyInfo prop in props) { propMap[GetJsonKeyForProperty(prop)] = prop; } propMapCache.Add(type, propMap); } return propMap; } public PropertyInfo[] GetProperties(Type type) { return GetPropertyMap(type).Values.ToArray(); } public PropertyInfo GetPropertyForJsonKey(Type type, string jsonKey) { PropertyInfo propInfo; if (GetPropertyMap(type).TryGetValue(jsonKey, out propInfo)) return propInfo; else return null; // Or, throw an exception here?? } #endregion public string GetJsonKeyForType(Type type) { string key = null; var keyCache = _jsonKeysForType.Value; lock (keyCache) { if (IsSerializedAsMany(type)) type = GetElementType(type); if (keyCache.TryGetValue(type, out key)) return key; var attrs = type.CustomAttributes.Where(x => x.AttributeType == typeof(Newtonsoft.Json.JsonObjectAttribute)).ToList(); string title = type.Name; if (attrs.Any()) { var titles = attrs.First().NamedArguments.Where(arg => arg.MemberName == "Title") .Select(arg => arg.TypedValue.Value.ToString()).ToList(); if (titles.Any()) title = titles.First(); } key = FormatPropertyName(PluralizationService.Pluralize(title)); keyCache.Add(type, key); } return key; } public string GetJsonKeyForProperty(PropertyInfo propInfo) { return FormatPropertyName(propInfo.Name); //TODO: Respect [JsonProperty(PropertyName = "FooBar")], and probably cache the result. } protected static string FormatPropertyName(string propertyName) { string result = propertyName.Substring(0, 1).ToLower() + propertyName.Substring(1); return result; } public bool IsSerializedAsMany(Type type) { bool isMany; var isManyCache = _isSerializedAsMany.Value; lock (isManyCache) { if (isManyCache.TryGetValue(type, out isMany)) return isMany; isMany = type.IsArray || (type.GetInterfaces().Contains(typeof(System.Collections.IEnumerable)) && type.IsGenericType); isManyCache.Add(type, isMany); } return isMany; } public Type GetElementType(Type manyType) { Type etype = null; var etypeCache = _getElementType.Value; lock (etypeCache) { if (etypeCache.TryGetValue(manyType, out etype)) return etype; if (manyType.IsGenericType) etype = manyType.GetGenericArguments()[0]; else etype = manyType.GetElementType(); etypeCache.Add(manyType, etype); } return etype; } } }