Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to support my custom Collection type? #46

Open
shaneh20 opened this issue Aug 31, 2018 · 10 comments
Open

How to support my custom Collection type? #46

shaneh20 opened this issue Aug 31, 2018 · 10 comments

Comments

@shaneh20
Copy link

Hi, I've just started using TrackerDog (nice work btw!) and having a bit of trouble getting it to support my custom ObservableCollectionEx class that my model classes use. Here's some demo code:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using TrackerDog;

class Program
    {
        static void Main(string[] args)
        {
            //Initialise
            var config = ObjectChangeTracking.CreateConfiguration().TrackThisTypeRecursive(typeof(Car));
            //config.Collections.AddOrUpdateImplementation<IList<object>, ObservableCollectionEx<object>, DefaultCollectionChangeInterceptor<object>>();
            var tracker = config.CreateTrackableObjectFactory();

            //Create a Car
            Car car = new Car();
            car.Passengers = new ObservableCollectionEx<Passenger>();
            car.Passengers.Add(new Passenger { Name = "Frank" });

            //Start tracking
            car = tracker.CreateFrom(car);
        }
    }

    public class ObservableCollectionEx<T> : ObservableCollection<T>
    {
        public ObservableCollectionEx() : base()
        {
        }

        public ObservableCollectionEx(List<T> list) : base(list)
        {
        }
        
        public ObservableCollectionEx(IEnumerable<T> collection) : base(collection)
        {
        }
    }

    public class Car 
    {
        public virtual ObservableCollectionEx<Passenger> Passengers { get; set; }
    }

    public class Passenger 
    {
        public virtual string Name { get; set; }
    }
}

When the car = tracker.CreateFrom(car) line runs, it throws the exception:

Object of type 'Castle.Proxies.IList1Proxy' cannot be converted to type 'ConsoleApp37.ObservableCollectionEx1[ConsoleApp37.Passenger]

Looking at your code, it seems TrackerDog is creating a proxy IList collection which it tries to assign to Car.Passengers and fails since their types differ. I tried using AddOrUpdateImplementation to map IList to my ObservableCollectionEx class, but it produces the same error.

I noticed in your documentation, you advised us to use collection interfaces (IList, ICollection etc) in our models rather than concrete collection types. Unfortunately the model classes in my actual project are all dependent on my ObservableCollectionEx class. So, I can't easily change this.

Is there a way to make TrackerDog work with my custom collection type?

Thanks

@mfidemraizer
Copy link
Owner

Hey! Thank you for using TrackerDog.

I'll read your message today carefully and I'll add an answer.

@mfidemraizer
Copy link
Owner

@shaneh20 Actually, if I'm not mistaken, why you would need to use ObservableCollection? An IList<T> proxy already implements INotifyPropertyChanged and INotifyCollectionChanged, so if you're trying to use two-way binding, the proxy should be enough.

In the other hand, currently TrackerDog won't support concrete classes: you need to use interfaces, because what you get is a proxy, not your desired implementation.

Maybe TrackerDog could be configured to wrap trackable collections with anything that implements INotifyPropertyChanged and INotifyCollectionChanged instead of creating run-time proxies, but currently I don't know what could be the impact of implementing this.

Is it possible that you use the proxies instead of ObservableCollection?

Another approach is that you track the whole changes and you implement an extension method .ToObservable() so you may do a.Passengers.ToObservable().

Please let me know your thoughts.

@toumir
Copy link

toumir commented Sep 6, 2018

@shaneh20 let me know if @mfidemraizer's answer helps

@shaneh20
Copy link
Author

shaneh20 commented Sep 7, 2018

My custom ObservableCollectionEx class (inherits from ObservableCollection) contains custom behaviour that various parts of my applications depend on (it contains various extra events that the base class does not provide). So, although its a great idea to make the IList proxy implement the INotifyPropertyChanged interfaces, I also need the extra custom logic in my custom ObservableCollectionEx too.

I like your idea of TrackerDog being able to wrap any existing collections instead of creating proxies as that would be ideal for me, although I understand it may require a lot of work on your part to rework TrackerDog to support it.

If I created an IObservableCollectionEx interface and changed all of my model classes to use this instead of the concrete ObservableCollectionEx class, could I then setup an 'AddOrUpdateImplementation' mapping to force TrackerDog to create ObservableCollectionEx proxies instead of IList proxies?

@mfidemraizer
Copy link
Owner

@shaneh20 I'm going check it this weekend, so I can give you a good answer to your question 👍

@mfidemraizer
Copy link
Owner

@shaneh20 I'm really sorry I've not been able too invest time on this issue. I've had to solve some personal affairs and I couldn't work on this.

@shaneh20
Copy link
Author

That's cool. I'm not really in any rush

@mfidemraizer
Copy link
Owner

@shaneh20 Ok, first of all I need to create a .NET Core 2.x-only solution file to let me open and run TrackerDog project and tests in my current setup (Linux) so I can look further on this issue and others.

@jnonereacher
Copy link

This thread seems to have gone quiet, and I have just stumbled upon this exact issue. @mfidemraizer have you got any further news or updates on this? Great work with this library btw!

@Stoom
Copy link

Stoom commented Mar 7, 2021

I have the same problem too. I'm extending a HashSet, have created an interface, a concrete class, and a new ChangeInterceptor that extends DefaultCollectionChangeInterceptor<T>

    public interface ISetEx<TEntity> : ICollection<TEntity>, IEnumerable<TEntity>, IEnumerable {
        new bool Add(TEntity item);
        void ExceptWith(IEnumerable<TEntity> other);
        void IntersectWith(IEnumerable<TEntity> other);
        bool IsProperSubsetOf(IEnumerable<TEntity> other);
        bool IsProperSupersetOf(IEnumerable<TEntity> other);
        bool IsSubsetOf(IEnumerable<TEntity> other);
        bool IsSupersetOf(IEnumerable<TEntity> other);
        bool Overlaps(IEnumerable<TEntity> other);
        bool SetEquals(IEnumerable<TEntity> other);
        void SymmetricExceptWith(IEnumerable<TEntity> other);
        void UnionWith(IEnumerable<TEntity> other);
    }

    public class HashSetEx<TEntity> : HashSet<TEntity>, ISetEx<TEntity>, IReadOnlySet<TEntity>, IDeserializationCallback, ISerializable {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants