Change tracking breaks with string lists
gcottrell13 opened this issue · 3 comments
gcottrell13 commented
I ran into a problem with a string list on my model where it would try to track the strings, and it would then die. Is this a known issue, and can you replicate?
I've included a small example that also died in 2.0.13
public class TestThing
{
public virtual IList<string> MyStrings { get; set; }
public TestThing()
{
MyStrings = new List<string>();
}
}
// ----------------------------------------------------------------
var test = new TestThing();
test.MyStrings.Add("wat");
test = test.AsTrackable();
test.MyStrings = new List<string>() { "ok", "yea" };
Exception:
|System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Property Chars is not virtual. Can't track classes with non-virtual properties.
at ChangeTracking.ChangeTrackingProxyGenerationHook.NonProxyableMemberNotification(Type type, MemberInfo memberInfo)
at Castle.DynamicProxy.Contributors.MembersCollector.AcceptMethod(MethodInfo method, Boolean onlyVirtuals, IProxyGenerationHook hook)
at Castle.DynamicProxy.Contributors.WrappedClassMembersCollector.GetMethodToGenerate(MethodInfo method, IProxyGenerationHook hook, Boolean isStandalone)
at Castle.DynamicProxy.Contributors.MembersCollector.AddMethod(MethodInfo method, IProxyGenerationHook hook, Boolean isStandalone)
at Castle.DynamicProxy.Contributors.MembersCollector.AddProperty(PropertyInfo property, IProxyGenerationHook hook)
at Castle.DynamicProxy.Contributors.MembersCollector.CollectProperties(IProxyGenerationHook hook)
at Castle.DynamicProxy.Contributors.MembersCollector.CollectMembersToProxy(IProxyGenerationHook hook)
at Castle.DynamicProxy.Contributors.ClassProxyWithTargetTargetContributor.CollectElementsToProxyInternal(IProxyGenerationHook hook)+MoveNext()
at Castle.DynamicProxy.Contributors.CompositeTypeContributor.CollectElementsToProxy(IProxyGenerationHook hook, MetaType model)
at Castle.DynamicProxy.Generators.ClassProxyWithTargetGenerator.GenerateType(String name, INamingScope namingScope)
at Castle.DynamicProxy.Generators.BaseProxyGenerator.ObtainProxyType(CacheKey cacheKey, Func`3 factory)
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyWithTarget(Type classToProxy, Type[] additionalInterfacesToProxy, Object target, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
at ChangeTracking.Core.AsTrackable[T](T target, ChangeStatus status, Action`1 notifyParentListItemCanceled, Boolean makeComplexPropertiesTrackable, Boolean makeCollectionPropertiesTrackable)
at ChangeTracking.ChangeTrackingCollectionInterceptor`1..ctor(IList`1 target, Boolean makeComplexPropertiesTrackable, Boolean makeCollectionPropertiesTrackable)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
at ChangeTracking.Core.AsTrackableCollectionChild(Type type, Object target, Boolean makeComplexPropertiesTrackable, Boolean makeCollectionPropertiesTrackable)
at ChangeTracking.CollectionPropertyInterceptor`1.<>c__DisplayClass12_0.<GetGetterAction>b__0(IInvocation invocation, Dictionary`2 trackables, Boolean makeComplexPropertiesTrackable, Boolean makeCollectionPropertiesTrackable)
at ChangeTracking.CollectionPropertyInterceptor`1.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at ChangeTracking.ComplexPropertyInterceptor`1.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at ChangeTracking.ChangeTrackingInterceptor`1.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at ChangeTracking.NotifyPropertyChangedInterceptor`1.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.TestThingProxy.get_MyStrings()
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
at ChangeTracking.NotifyPropertyChangedInterceptor`1.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
mayphi commented
We had the same problem.
As far as i remember the problem is, that the string class is sealed.
Requirements and restrictions
We solved it by creating a new class to wrap the string.
For example:
public class TrackableString : INotifyPropertyChanged
{
private string _value;
public virtual string Value
{
get => _value;
set
{
_value = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
monodop commented
It would be great if this library supported lists of primitives, even if you just get a record of the old list and new list and if the list as a whole was modified - it doesn't have to tell you per-item what was added/removed/modified