Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- namespace TrackableEntities.Client
- {
- using IPropertyData = IEnumerable<(PropertyInfo Property, Type EntityType)>;
- using CollectionProperties = Dictionary<Type, IEnumerable<(PropertyInfo Property, Type EntityType)>>;
- public class AppEntityBase : EntityBase, INavigationPropertyInspector, IRefPropertyChangeTrackerResolver
- {
- public AppEntityBase() : base()
- {
- InitializeEntityCollections();
- }
- bool ignoreChangeNotification;
- protected virtual void NotifyPropertyChanged(string propertyName, object before, object after)
- {
- if (before == after) return;
- if (ignoreChangeNotification || propertyName == null) return;
- var propertyInfo = GetType().GetRuntimeProperty(propertyName);
- if (propertyInfo == null) return;
- var propertyType = propertyInfo.PropertyType;
- OnNotifyPropertyChanged(propertyName);
- OnNotifyPropertyChanged(propertyName, propertyInfo, after);
- 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 });
- if (propertyInfo.GetCustomAttribute<JsonIgnoreAttribute>() != null)
- ModifiedProperties?.Remove(propertyName);
- }
- protected virtual void OnNotifyPropertyChanged([CallerMemberName] string propertyName = null)
- {
- }
- Dictionary<string, ITrackingCollection> _RefPropertyChangeTrackers = new Dictionary<string, ITrackingCollection>();
- void OnNotifyPropertyChanged(string propertyName, PropertyInfo propertyInfo, object value)
- {
- if (value == null)
- {
- var collectionProperties = CollectionPropertiesData.Get(GetType());
- var propertyData = collectionProperties.SingleOrDefault(p => p.Property == propertyInfo);
- if (propertyData.Property != null)
- propertyData.Property.SetValue(this, null);
- else if (_RefPropertyChangeTrackers.TryGetValue(propertyName, out var ctc))
- {
- var list = (IList)ctc;
- while (list.Count > 0)
- list.RemoveAt(list.Count - 1);
- }
- }
- else if (value is EntityBase)
- {
- if (!_RefPropertyChangeTrackers.TryGetValue(propertyName, out var ctc))
- ctc = CreateChangeTrackingCollection(propertyInfo.PropertyType, new[] { value });
- else
- {
- var list = (IList)ctc;
- if (list.Count > 0)
- list.RemoveAt(0);
- //should only contain a single entry
- list.Add(value);
- }
- _RefPropertyChangeTrackers[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, (IEnumerable)value);
- 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();
- ITrackingCollection CreateChangeTrackingCollection(Type entityType, IEnumerable entities = null)
- {
- var ctcType = typeof(ChangeTrackingCollection<>).MakeGenericType(entityType);
- var ctc = (ITrackingCollection)Activator.CreateInstance(ctcType);
- ctc.Parent = this;
- var list = (IList)ctc;
- if (entities != null)
- foreach (var entity in entities)
- list.Add(entity);
- return ctc;
- }
- public IEnumerable<EntityNavigationProperty> GetNavigationProperties()
- {
- var baseProperties = typeof(AppEntityBase).GetRuntimeProperties().Select(p => p.Name).ToArray();
- var type = GetType();
- // TODO: cache properties?
- foreach (var prop in type.GetRuntimeProperties().Where(p => !baseProperties.Contains(p.Name)))
- {
- // 1-1 and M-1 properties
- if (typeof(ITrackable).GetTypeInfo().IsAssignableFrom(prop.PropertyType.GetTypeInfo()))
- {
- var trackableRef = prop.GetValue(this) as ITrackable;
- yield return new EntityReferenceProperty(prop, trackableRef);
- }
- // 1-M and M-M properties
- if (typeof(IEnumerable<ITrackable>).GetTypeInfo().IsAssignableFrom(prop.PropertyType.GetTypeInfo()))
- {
- var items = prop.GetValue(this) as IEnumerable<ITrackable>;
- yield return new EntityCollectionProperty(prop, items);
- }
- }
- }
- public ITrackingCollection GetRefPropertyChangeTracker(string propertyName)
- {
- return _RefPropertyChangeTrackers.TryGetValue(propertyName, out var tracker) ? tracker : null;
- }
- 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;
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement