Last active
June 26, 2017 01:09
-
-
Save weitzhandler/117ad8f6d10ab6b997362fe5a8930500 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
namespace TrackableEntities.Client | |
{ | |
using IPropertyData = IEnumerable<(PropertyInfo Property, Type EntityType)>; | |
using CollectionProperties = Dictionary<Type, IEnumerable<(PropertyInfo Property, Type EntityType)>>; | |
public class AppEntityBase : EntityBase | |
{ | |
public AppEntityBase() : base() | |
{ | |
InitializeEntityCollections(); | |
} | |
bool ignoreChangeNotification; | |
protected new void NotifyPropertyChanged([CallerMemberName] string propertyName = null) | |
{ | |
if (ignoreChangeNotification || propertyName == null) return; | |
var propertyInfo = GetType().GetRuntimeProperty(propertyName); | |
if (propertyInfo == null) return; | |
var propertyType = propertyInfo.PropertyType; | |
OnNotifyPropertyChanged(propertyName, propertyInfo); | |
var constant = Expression.Constant(this); | |
var property = Expression.Property(constant, propertyName); | |
var lambda = Expression.Lambda(property, Enumerable.Empty<ParameterExpression>()); | |
var method = typeof(EntityBase) | |
.GetTypeInfo() | |
.DeclaredMethods | |
.Single(mi => mi.Name == nameof(NotifyPropertyChanged) && mi.IsGenericMethod); | |
method = method.MakeGenericMethod(propertyType); | |
method.Invoke(this, new[] { lambda }); | |
} | |
Dictionary<string, object> _ChangeTrackingCollections = new Dictionary<string, object>(); | |
void OnNotifyPropertyChanged(string propertyName, PropertyInfo propertyInfo) | |
{ | |
var value = propertyInfo.GetValue(this); | |
if (value == null) | |
_ChangeTrackingCollections.Remove(propertyName); | |
else if (value is EntityBase) | |
{ | |
var ctc = CreateChangeTrackingCollection(value.GetType()); | |
ctc.Add(value); | |
_ChangeTrackingCollections[propertyName] = ctc; | |
} | |
else | |
{ | |
var collectionProperties = CollectionPropertiesData.Get(GetType()); | |
var propertyData = collectionProperties.SingleOrDefault(p => p.Property == propertyInfo); | |
if (propertyData.Property != null && !(value is ITrackingCollection)) | |
{ | |
var ctc = CreateChangeTrackingCollection(propertyData.EntityType); | |
foreach (var item in (IEnumerable)value) | |
ctc.Add(item); | |
ignoreChangeNotification = true; | |
propertyInfo.SetValue(this, ctc); | |
ignoreChangeNotification = false; | |
} | |
} | |
} | |
void InitializeEntityCollections() | |
{ | |
var collections = CollectionPropertiesData.Get(GetType()); | |
foreach (var collectionProperty in collections) | |
{ | |
var collection = CreateChangeTrackingCollection(collectionProperty.EntityType); | |
ignoreChangeNotification = true; | |
collectionProperty.Property.SetValue(this, collection); | |
ignoreChangeNotification = false; | |
} | |
} | |
static Type GetEntityType(TypeInfo propertyTypeInfo) => | |
propertyTypeInfo.ImplementedInterfaces | |
.Select(i => i.GetTypeInfo()) | |
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)) | |
.Select(i => i.GenericTypeArguments.Single()) | |
.Where(t => t.GetTypeInfo().IsSubclassOf(typeof(AppEntityBase))) | |
.SingleOrDefault(); | |
static IList CreateChangeTrackingCollection(Type entityType) | |
{ | |
var ctcType = typeof(ChangeTrackingCollection<>).MakeGenericType(entityType); | |
var ctc = (ITrackingCollection)Activator.CreateInstance(ctcType); | |
//ctc.Tracking = true; | |
return (IList)ctc; | |
} | |
static class CollectionPropertiesData | |
{ | |
static object sync = new object(); | |
static CollectionProperties Data = new CollectionProperties(); | |
public static IPropertyData Get(Type type) | |
{ | |
var propertyData = default(IPropertyData); | |
lock (sync) | |
if (!Data.TryGetValue(type, out propertyData)) | |
{ | |
var collections = type | |
.GetRuntimeProperties() | |
.Select(pi => (Property: pi, EntityType: GetEntityType(pi.PropertyType.GetTypeInfo()))) | |
.Where(t => t.EntityType != null) | |
.ToArray(); | |
Data[type] = propertyData = collections; | |
} | |
return propertyData; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment