joelweiss / ChangeTracking

Track changes in POCO objects
MIT License
203 stars 43 forks source link

Can't create method to get interceptors #62

Open sevek opened 4 years ago

sevek commented 4 years ago

I was wanting to create a custom method (to move to separate class later) that gets the list of Interceptors to add to the proxy.

I created a method

private IInterceptor[] GetInterceptors<T>(ChangeStatus status, Action<T> notifyParentListItemCanceled, ChangeTrackingSettings changeTrackingSettings, Graph graph) where T : class
{
    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);

    return new IInterceptor[]
        {
            notifyPropertyChangedInterceptor,
            changeTrackingInterceptor,
            editableObjectInterceptor,
            complexPropertyInterceptor,
            collectionPropertyInterceptor
         };
}

and modified the proxy create to

var interceptors = GetInterceptors(status, notifyParentListItemCanceled, 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) },
        target,
        GetOptions(typeof(T)),
        interceptors
        );

This all appears to work until I run the unit test where upon nearly all the collection tests fail. I have done a similar thing with the TrackableChild and this doesn't cause unit test failures.

I tried putting inserting the returned interceptors using array notation so the statement was basically the same but to no avail. If any of the interceptors were from the called method one gets the errors.

I'm a bit at my wits end with this problem so I'm hoping someone can show me the error of my ways.

I was hoping to add validation and mixin capability as well as the ability to use interfaces (the latter of which I have working) but have stumbled at this.

I'm also wanting to create a version that doesn't make changes to the target until AcceptChanges is called but this seems like a complete rewrite.

Thanks Sevek

joelweiss commented 4 years ago

can you please provide a small repro with your error?

sevek commented 4 years ago

Um I tried to push with GitHub Desktop but was denied access. Basically, you only need ChangeTrackingFactory.cs which I've added here.

ChangeTrackingFactory.zip

sevek commented 4 years ago

Hi Joel. Did you manage to see what I was talking about? It has to be one of the most frustratingly bizarre issues I have come across in my 40 years programming. It's a bit like the standard model going awry for a physicist. There's just no logic to it. In bygone years one occasionally got non displaying characters causing errors like this.

joelweiss commented 4 years ago

I don't know what you are trying to achieve.

The reason it was failing is because you were not calling IsInitialized = true on the correct instance.

This works

...
var interceptors = GetInterceptors(status, notifyParentListItemCanceled, 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) },
    target,
    GetOptions(typeof(T)),
    interceptors
    );

CopyFieldsAndProperties(source: target, target: proxy);

foreach (IInterceptorSettings interceptor in interceptors.OfType<IInterceptorSettings>())
{
    interceptor.IsInitialized = true;
}
...
sevek commented 4 years ago

Excellent. I had that for loop in my code a one stage but must have deleted at some point.

sevek commented 4 years ago

Did you say that you had implemented INotifyDataErrorInfo using attributes??? I would like to do this using FluentValidation and also be able to use mixins.

sevek commented 4 years ago

Okay, I'm pretty close I think.

I can now subclass ChangeTrackingFactory and override the actual proxy creation of the base graph class but unfortunately I have three issues.

  1. Creating a mixin with a sub list cause the creation of proxies in that list. Works okay for complex object but the list complains of multiple instances of the mixin interface.

  2. I'm not picking up where proxies for items in the list or complex proxies are created and hence are not able to customise these by type which I would need to do. Could you advise where I should pick this up from?

  3. When I changed the scope to enable subclassing the Speed test took a huge nose dive and I don't understand why Any ideas why this might be?

In the grand scheme of things I would like to be able to use FluentValidation and so want to be able to inject appropriate validators. I would also like to be able to have custom handlers for Complex properties and thus enable lazy or custom loading of those objects.

Enclosed is may current implementation.

ChangeTracking.zip

sevek commented 4 years ago
  1. should read causes the creation of proxies in that list to throw an InvalidMixinConfigurationException because

The list of mixins contains two mixins implementing the same interface

sevek commented 4 years ago

Any chance of having a Skype chat today (Thursday)? geoff.d.scott