Skip to content

Instantly share code, notes, and snippets.

@weitzhandler
Last active June 26, 2017 01:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weitzhandler/117ad8f6d10ab6b997362fe5a8930500 to your computer and use it in GitHub Desktop.
Save weitzhandler/117ad8f6d10ab6b997362fe5a8930500 to your computer and use it in GitHub Desktop.
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