Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions Source/ChangeTracking.Tests/InheritanceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace ChangeTracking.Tests
{
public class InheritanceTests

{
public class Container
{
public virtual IList<Item> Items { get; set; } = new List<Item>();

public void AddItem(Item item)
{
Items.Add(item);
}
}

public class Item
{
public Item()
{
}

public virtual int ValueInt { get; set; }
}

public class ItemDerived : Item
{
public ItemDerived() :
base()
{

}
}

[Fact]
public void ItemsAdd_DerivedCollectionItemClass_AddsItemOfDerivedCollectionItemClass()
{
Container container = new Container();

var trackable = container.AsTrackable();

trackable.Items.Add(new ItemDerived());

trackable.CastToIChangeTrackable().AcceptChanges();

Assert.True(container.Items[0] is ItemDerived);
}

}
}
6 changes: 3 additions & 3 deletions Source/ChangeTracking/ChangeTrackingCollectionInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ private void DeleteItem(T item)

private void ItemCanceled(T item) => _WrappedTarget.CancelNew(_WrappedTarget.IndexOf(item));

public IEnumerable<T> UnchangedItems => _WrappedTarget.Cast<IChangeTrackable<T>>().Where(ct => ct.ChangeTrackingStatus == ChangeStatus.Unchanged).Cast<T>();
public IEnumerable<T> UnchangedItems => _WrappedTarget.Cast<IChangeTrackable>().Where(ct => ct.ChangeTrackingStatus == ChangeStatus.Unchanged).Cast<T>();

public IEnumerable<T> AddedItems => _WrappedTarget.Cast<IChangeTrackable<T>>().Where(ct => ct.ChangeTrackingStatus == ChangeStatus.Added).Cast<T>();
public IEnumerable<T> AddedItems => _WrappedTarget.Cast<IChangeTrackable>().Where(ct => ct.ChangeTrackingStatus == ChangeStatus.Added).Cast<T>();

public IEnumerable<T> ChangedItems => _WrappedTarget.Cast<IChangeTrackable<T>>().Where(ct => ct.ChangeTrackingStatus == ChangeStatus.Changed).Cast<T>();
public IEnumerable<T> ChangedItems => _WrappedTarget.Cast<IChangeTrackable>().Where(ct => ct.ChangeTrackingStatus == ChangeStatus.Changed).Cast<T>();

public IEnumerable<T> DeletedItems => _DeletedItems.Select(i => i);

Expand Down
60 changes: 47 additions & 13 deletions Source/ChangeTracking/ChangeTrackingFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,30 +66,64 @@ internal T AsTrackable<T>(T target, ChangeStatus status, Action<T> notifyParentL
throw new InvalidOperationException("Only IList<T>, List<T> and ICollection<T> are supported");
}

var changeTrackingInterceptor = new ChangeTrackingInterceptor<T>(status);
var notifyPropertyChangedInterceptor = new NotifyPropertyChangedInterceptor<T>(changeTrackingInterceptor);
var editableObjectInterceptor = new EditableObjectInterceptor<T>(notifyParentListItemCanceled);
var complexPropertyInterceptor = new ComplexPropertyInterceptor<T>(changeTrackingSettings, graph);
var collectionPropertyInterceptor = new CollectionPropertyInterceptor<T>(changeTrackingSettings, graph);
object proxy = _ProxyGenerator.CreateClassProxyWithTarget(typeof(T),
new[] { typeof(IChangeTrackableInternal), typeof(IRevertibleChangeTrackingInternal), typeof(IChangeTrackable<T>), typeof(IChangeTrackingManager), typeof(IComplexPropertyTrackable), typeof(ICollectionPropertyTrackable), typeof(IEditableObjectInternal), typeof(INotifyPropertyChanged) },
//var changeTrackingInterceptor = new ChangeTrackingInterceptor<T>(status);
var changeTrackingInterceptor = (IInterceptor)Activator.CreateInstance(
GetGenericType(typeof(ChangeTrackingInterceptor<>), target.GetType()),
status);

//var notifyPropertyChangedInterceptor = new NotifyPropertyChangedInterceptor<T>(changeTrackingInterceptor);
var notifyPropertyChangedInterceptor = (IInterceptor)Activator.CreateInstance(
GetGenericType(typeof(NotifyPropertyChangedInterceptor<>), target.GetType()),
changeTrackingInterceptor);

//var editableObjectInterceptor = new EditableObjectInterceptor<T>(notifyParentListItemCanceled);
var editableObjectInterceptor = (IInterceptor)Activator.CreateInstance(
GetGenericType(typeof(EditableObjectInterceptor<>), target.GetType()),
notifyParentListItemCanceled);

//var complexPropertyInterceptor = new ComplexPropertyInterceptor<T>(changeTrackingSettings, graph);
var complexPropertyInterceptor = (IInterceptor)Activator.CreateInstance(
GetGenericType(typeof(ComplexPropertyInterceptor<>), target.GetType()),
changeTrackingSettings, graph);

//var collectionPropertyInterceptor = new CollectionPropertyInterceptor<T>(changeTrackingSettings, graph);
var collectionPropertyInterceptor = (IInterceptor)Activator.CreateInstance(
GetGenericType(typeof(CollectionPropertyInterceptor<>), target.GetType()),
changeTrackingSettings, graph);

object proxy = _ProxyGenerator.CreateClassProxyWithTarget(target.GetType(),
new[] {
typeof(IChangeTrackableInternal),
typeof(IRevertibleChangeTrackingInternal),
//typeof(IChangeTrackable<T>),
GetGenericType(typeof(IChangeTrackable<>), target.GetType()),
typeof(IChangeTrackingManager),
typeof(IComplexPropertyTrackable),
typeof(ICollectionPropertyTrackable),
typeof(IEditableObjectInternal),
typeof(INotifyPropertyChanged) },
target,
GetOptions(typeof(T)),
GetOptions(target.GetType()),
notifyPropertyChangedInterceptor,
changeTrackingInterceptor,
editableObjectInterceptor,
complexPropertyInterceptor,
collectionPropertyInterceptor);
CopyFieldsAndProperties(source: target, target: proxy);
notifyPropertyChangedInterceptor.IsInitialized = true;
changeTrackingInterceptor.IsInitialized = true;
editableObjectInterceptor.IsInitialized = true;
complexPropertyInterceptor.IsInitialized = true;
collectionPropertyInterceptor.IsInitialized = true;
(notifyPropertyChangedInterceptor as IInterceptorSettings).IsInitialized = true;
(changeTrackingInterceptor as IInterceptorSettings).IsInitialized = true;
(editableObjectInterceptor as IInterceptorSettings).IsInitialized = true;
(complexPropertyInterceptor as IInterceptorSettings).IsInitialized = true;
(collectionPropertyInterceptor as IInterceptorSettings).IsInitialized = true;
graph.Add(new ProxyWeakTargetMap(target, proxy));
return (T)proxy;
}

private Type GetGenericType(Type generic, Type genericTypeParameter)
{
return generic.MakeGenericType(genericTypeParameter);
}

internal ICollection<T> AsTrackableCollection<T>(ICollection<T> target, ChangeTrackingSettings changeTrackingSettings) where T : class
{
if (!(target is IList<T> list))
Expand Down
2 changes: 1 addition & 1 deletion Source/ChangeTracking/ChangeTrackingInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static ChangeTrackingInterceptor()
_Properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanWrite).ToDictionary(pi => pi.Name);
}

internal ChangeTrackingInterceptor(ChangeStatus status)
public ChangeTrackingInterceptor(ChangeStatus status)
{
_OriginalValueDictionary = new Dictionary<string, object>();
_ChangedComplexOrCollectionProperties = new HashSet<string>();
Expand Down
2 changes: 1 addition & 1 deletion Source/ChangeTracking/ComplexPropertyInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ static ComplexPropertyInterceptor()
}
}

internal ComplexPropertyInterceptor(ChangeTrackingSettings changeTrackingSettings, Graph graph)
public ComplexPropertyInterceptor(ChangeTrackingSettings changeTrackingSettings, Graph graph)
{
_ChangeTrackingSettings = changeTrackingSettings;
_Graph = graph;
Expand Down
2 changes: 1 addition & 1 deletion Source/ChangeTracking/EditableObjectInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal EditableObjectInterceptor()
_BeforeEditValues = new Dictionary<string, object>();
}

internal EditableObjectInterceptor(Action<T> notifyParentItemCanceled)
public EditableObjectInterceptor(Action<T> notifyParentItemCanceled)
: this()
{
_NotifyParentItemCanceled = notifyParentItemCanceled;
Expand Down
1 change: 1 addition & 0 deletions Source/ChangeTracking/IChangeTrackable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

namespace ChangeTracking
{

public interface IChangeTrackable<T> : IChangeTrackable
{
/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Source/ChangeTracking/NotifyPropertyChangedInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static NotifyPropertyChangedInterceptor()
_Properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(pi => pi.Name);
}

internal NotifyPropertyChangedInterceptor(ChangeTrackingInterceptor<T> changeTrackingInterceptor)
public NotifyPropertyChangedInterceptor(ChangeTrackingInterceptor<T> changeTrackingInterceptor)
{
_PropertyChangedEventHandlers = new Dictionary<string, PropertyChangedEventHandler>();
_PropertyChangedEventHandlersLock = new object();
Expand Down