using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace NzbDrone.Core.Datastore { public class IdService { private readonly IndexProvider _indexProvider; private static readonly ConcurrentDictionary> propertyCache = new ConcurrentDictionary>(); public IdService(IndexProvider indexProvider) { _indexProvider = indexProvider; } public void EnsureIds(T obj, HashSet context) { //context is use to prevent infinite loop if objects are recursively looped. if (obj == null || context.Contains(obj)) { return; } context.Add(obj); var modelBase = obj as BaseRepositoryModel; if (modelBase != null && modelBase.Id == 0) { modelBase.Id = _indexProvider.Next(obj.GetType()); } var list = obj as IEnumerable; if (list != null) { foreach (var item in list) { EnsureIds(item, context); } return; } foreach (var propertyInfo in GetPotentialProperties(obj.GetType())) { var propValue = propertyInfo.GetValue(obj, null); EnsureIds(propValue, context); } } private IList GetPotentialProperties(Type type) { IList result; if (!propertyCache.TryGetValue(type.FullName, out result)) { result = type.GetProperties().Where(ShouldCrawl).ToList(); propertyCache.TryAdd(type.FullName, result); } return result; } private bool ShouldCrawl(PropertyInfo propertyInfo) { return propertyInfo.CanRead && ShouldCrawl(propertyInfo.PropertyType); } private bool ShouldCrawl(Type type) { if (type.IsGenericType) { var genericArg = type.GetGenericArguments()[0]; //skip if generic argument type isn't interesting if (!ShouldCrawl(genericArg)) { return false; } var listType = typeof(IList<>).MakeGenericType(genericArg); return listType.IsAssignableFrom(type); } return type.IsClass && type.FullName.StartsWith("NzbDrone"); } } }