Advertisement
Shimmy

Untitled

Jul 23rd, 2017
143
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.56 KB | None | 0 0
  1. namespace TrackableEntities.Client
  2. {
  3.   using IPropertyData = IEnumerable<(PropertyInfo Property, Type EntityType)>;
  4.   using CollectionProperties = Dictionary<Type, IEnumerable<(PropertyInfo Property, Type EntityType)>>;
  5.  
  6.   public class AppEntityBase : EntityBase, INavigationPropertyInspector, IRefPropertyChangeTrackerResolver
  7.   {
  8.     public AppEntityBase() : base()
  9.     {
  10.       InitializeEntityCollections();
  11.     }
  12.    
  13.     bool ignoreChangeNotification;
  14.     protected virtual void NotifyPropertyChanged(string propertyName, object before, object after)
  15.     {
  16.       if (before == after) return;
  17.       if (ignoreChangeNotification || propertyName == null) return;
  18.  
  19.       var propertyInfo = GetType().GetRuntimeProperty(propertyName);
  20.       if (propertyInfo == null) return;
  21.       var propertyType = propertyInfo.PropertyType;
  22.  
  23.       OnNotifyPropertyChanged(propertyName);
  24.       OnNotifyPropertyChanged(propertyName, propertyInfo, after);
  25.  
  26.       var constant = Expression.Constant(this);
  27.       var property = Expression.Property(constant, propertyName);
  28.       var lambda = Expression.Lambda(property, Enumerable.Empty<ParameterExpression>());
  29.       var method = typeof(EntityBase)
  30.         .GetTypeInfo()
  31.         .DeclaredMethods
  32.         .Single(mi => mi.Name == nameof(NotifyPropertyChanged) && mi.IsGenericMethod);
  33.       method = method.MakeGenericMethod(propertyType);
  34.       method.Invoke(this, new[] { lambda });
  35.  
  36.       if (propertyInfo.GetCustomAttribute<JsonIgnoreAttribute>() != null)
  37.         ModifiedProperties?.Remove(propertyName);
  38.     }
  39.  
  40.     protected virtual void OnNotifyPropertyChanged([CallerMemberName] string propertyName = null)
  41.     {    
  42.      
  43.     }
  44.  
  45.     Dictionary<string, ITrackingCollection> _RefPropertyChangeTrackers = new Dictionary<string, ITrackingCollection>();
  46.     void OnNotifyPropertyChanged(string propertyName, PropertyInfo propertyInfo, object value)
  47.     {
  48.       if (value == null)
  49.       {
  50.         var collectionProperties = CollectionPropertiesData.Get(GetType());
  51.         var propertyData = collectionProperties.SingleOrDefault(p => p.Property == propertyInfo);
  52.         if (propertyData.Property != null)
  53.           propertyData.Property.SetValue(this, null);
  54.         else if (_RefPropertyChangeTrackers.TryGetValue(propertyName, out var ctc))
  55.         {
  56.           var list = (IList)ctc;
  57.           while (list.Count > 0)
  58.             list.RemoveAt(list.Count - 1);
  59.         }
  60.       }
  61.       else if (value is EntityBase)
  62.       {
  63.         if (!_RefPropertyChangeTrackers.TryGetValue(propertyName, out var ctc))
  64.           ctc = CreateChangeTrackingCollection(propertyInfo.PropertyType, new[] { value });
  65.         else
  66.         {
  67.           var list = (IList)ctc;
  68.           if (list.Count > 0)
  69.             list.RemoveAt(0);
  70.           //should only contain a single entry
  71.           list.Add(value);
  72.         }
  73.  
  74.         _RefPropertyChangeTrackers[propertyName] = ctc;
  75.       }
  76.       else
  77.       {
  78.         var collectionProperties = CollectionPropertiesData.Get(GetType());
  79.         var propertyData = collectionProperties.SingleOrDefault(p => p.Property == propertyInfo);
  80.         if (propertyData.Property != null && !(value is ITrackingCollection))
  81.         {
  82.           var ctc = CreateChangeTrackingCollection(propertyData.EntityType, (IEnumerable)value);
  83.           ignoreChangeNotification = true;
  84.           propertyInfo.SetValue(this, ctc);
  85.           ignoreChangeNotification = false;
  86.         }
  87.       }
  88.     }
  89.  
  90.     void InitializeEntityCollections()
  91.     {
  92.       var collections = CollectionPropertiesData.Get(GetType());
  93.       foreach (var collectionProperty in collections)
  94.       {
  95.         var collection = CreateChangeTrackingCollection(collectionProperty.EntityType);
  96.         ignoreChangeNotification = true;
  97.         collectionProperty.Property.SetValue(this, collection);
  98.         ignoreChangeNotification = false;
  99.       }
  100.     }
  101.  
  102.     static Type GetEntityType(TypeInfo propertyTypeInfo) =>
  103.       propertyTypeInfo.ImplementedInterfaces
  104.        .Select(i => i.GetTypeInfo())
  105.        .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
  106.        .Select(i => i.GenericTypeArguments.Single())
  107.        .Where(t => t.GetTypeInfo().IsSubclassOf(typeof(AppEntityBase)))
  108.        .SingleOrDefault();
  109.  
  110.     ITrackingCollection CreateChangeTrackingCollection(Type entityType, IEnumerable entities = null)
  111.     {
  112.       var ctcType = typeof(ChangeTrackingCollection<>).MakeGenericType(entityType);
  113.       var ctc = (ITrackingCollection)Activator.CreateInstance(ctcType);
  114.       ctc.Parent = this;
  115.  
  116.       var list = (IList)ctc;
  117.       if (entities != null)
  118.         foreach (var entity in entities)
  119.           list.Add(entity);
  120.  
  121.       return ctc;
  122.     }
  123.  
  124.     public IEnumerable<EntityNavigationProperty> GetNavigationProperties()
  125.     {
  126.       var baseProperties = typeof(AppEntityBase).GetRuntimeProperties().Select(p => p.Name).ToArray();
  127.       var type = GetType();
  128.       // TODO: cache properties?
  129.       foreach (var prop in type.GetRuntimeProperties().Where(p => !baseProperties.Contains(p.Name)))
  130.       {
  131.         // 1-1 and M-1 properties
  132.         if (typeof(ITrackable).GetTypeInfo().IsAssignableFrom(prop.PropertyType.GetTypeInfo()))
  133.         {
  134.           var trackableRef = prop.GetValue(this) as ITrackable;
  135.           yield return new EntityReferenceProperty(prop, trackableRef);
  136.         }
  137.  
  138.         // 1-M and M-M properties
  139.         if (typeof(IEnumerable<ITrackable>).GetTypeInfo().IsAssignableFrom(prop.PropertyType.GetTypeInfo()))
  140.         {
  141.           var items = prop.GetValue(this) as IEnumerable<ITrackable>;
  142.           yield return new EntityCollectionProperty(prop, items);
  143.         }
  144.       }
  145.     }
  146.  
  147.     public ITrackingCollection GetRefPropertyChangeTracker(string propertyName)
  148.     {
  149.       return _RefPropertyChangeTrackers.TryGetValue(propertyName, out var tracker) ? tracker : null;
  150.     }
  151.  
  152.     static class CollectionPropertiesData
  153.     {
  154.       static object sync = new object();
  155.       static CollectionProperties Data = new CollectionProperties();
  156.       public static IPropertyData Get(Type type)
  157.       {
  158.         var propertyData = default(IPropertyData);
  159.         lock (sync)
  160.           if (!Data.TryGetValue(type, out propertyData))
  161.           {
  162.             var collections = type
  163.               .GetRuntimeProperties()
  164.               .Select(pi => (Property: pi, EntityType: GetEntityType(pi.PropertyType.GetTypeInfo())))
  165.               .Where(t => t.EntityType != null)
  166.               .ToArray();
  167.             Data[type] = propertyData = collections;
  168.           }
  169.         return propertyData;
  170.       }
  171.     }
  172.   }
  173. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement