trackedbindinglist becomes bindinglist bug (newest q3 and q2)

4 posts, 0 answers
  1. MATEUSZ
    MATEUSZ avatar
    18 posts
    Member since:
    Jul 2011

    Posted 18 Dec 2011 Link to this post

    some time ago I realized there is a bug, but I found a workaround so it wasn't urgent.
    however, bug is a bug (or maybe my knowledge is poor?)

    we have two classes, order and modification. modification references order, order has list of modification and wants to be notified (trackedbindinglist) that list has changed. Nothing like this happens.

    [Test]
    public void Modification_gets_changed()
    {
        var orderRepository = Container.Resolve<IRepository<Order>>();
        var order = new Order();
        bool flag = false;
        orderRepository.Add(order);
        UnitOfWork.Commit();
     
        (order.Modifications as IBindingList).ListChanged += (s, e) => flag = true;
        order.Modifications.Add(new Modification() {Order = order});
        Assert.IsTrue(flag);
    }

    I tried to track the bug and it appeared that really deeply withing openaccess code when the class is "recreated" from its metadata, the type IList<Modification> is recreated as TrackedList<Modification> Modifications. the workaround is to implicitly declare modifications as TrackedBindingList<Modification> Modifications { get; set; }. The bad side is that then we can't have meny-to-many relationships of trackedbindinglist - withopposite takes just IList<T> as an argument.

    I attach full (simplified) code of the classes.

    public class Order
    {
        public Order()
        {
            Modifications = new TrackedBindingList<Modification>();
        }
     
        [RaisePropertyChanged]
        public int Id { get; set; }
     
     
        //public TrackedBindingList<Modification> Modifications { get; set; }
        public IList<Modification> Modifications { get; set; }
        public Modification LastModification
        {
            get { return Modifications.LastOrDefault(); }
        }
     
        public static MappingConfiguration<Order> CreateConfiguration()
        {
            var mapping = new MappingConfiguration<Order>();
            mapping.MapType(o => new
            {
                Id = o.Id,
            }).ToTable("Orders");
     
            mapping.HasProperty(o => o.Id).IsIdentity(KeyGenerator.Autoinc);
     
            return mapping;
        }
    }
     
     
     
    public class Modification
    {
        public Modification()
        {
            OrderId = null;
            ModificationDate = DateTime.Now;
        }
     
        [RaisePropertyChanged]
        public int Id { get; set; }
     
        [RaisePropertyChanged]
        public DateTime ModificationDate { get; set; }
     
        [RaisePropertyChanged("Order")]
        public int? OrderId { get; set; }
        [RaisePropertyChanged("OrderId")]
        public Order Order { get; set; }
     
        public static MappingConfiguration<Modification> CreateConfiguration()
        {
            var orderConfiguration = new MappingConfiguration<Modification>();
            orderConfiguration.MapType(o => new
            {
                Id = o.Id,
                ModificationDate = o.ModificationDate,
                OrderId = o.OrderId,
            }).ToTable("Modifications");
     
            orderConfiguration.HasProperty(m => m.Id).IsIdentity(KeyGenerator.Autoinc);
     
            orderConfiguration.HasAssociation(m => m.Order)
                .WithOpposite(o => o.Modifications)
                .HasConstraint((m, o) => m.OrderId == o.Id);
     
            return orderConfiguration;
        }
    }
     
    [Test]
    public void Modification_gets_changed()
    {
        var orderRepository = Container.Resolve<IRepository<Order>>();
        var order = new Order();
        bool flag = false;
        orderRepository.Add(order);
        UnitOfWork.Commit();
     
        (order.Modifications as IBindingList).ListChanged += (s, e) => flag = true;
        order.Modifications.Add(new Modification() {Order = order});
        Assert.IsTrue(flag);
    }

  2. Alexander
    Admin
    Alexander avatar
    727 posts

    Posted 20 Dec 2011 Link to this post

    Hello Mateusz,

    OpenAccess creates the collections internally as TrackedList<T> in order to support collection change tracking, so this is the expected behavior. However, you are right that currently the fluent mapping does not support collections which do not implement IList<T>, and this is something we should improve.

    I will consider this as a feature request and add it for discussion. Thank you for your feedback.

    Regards,
    Alexander
    the Telerik team

    Q3’11 of Telerik OpenAccess ORM is available for download. Register for the What's New in Data Tools webinar to see what's new and get a chance to WIN A FREE LICENSE!

  3. DevCraft banner
  4. MATEUSZ
    MATEUSZ avatar
    18 posts
    Member since:
    Jul 2011

    Posted 20 Dec 2011 Link to this post

    ok, it creates trackedlist internally... but is it ok to have property of type TrackedBindingList<T> instead of IList<T>? such a declaration makes openaccess create expected (by me) type. but makes many-to-many relationships impossible.

    fluent mapping supports only IList<T>... yeah, I know, but TrackedBindingList<T> implements it ;)



    edit:
    ok, it turned out, that openaccess supports fields covered by get-only properties. so the problem is solved.
    private TrackedBindingList<Modification> modifications = new TrackedBindingList<Modification>();
    public IList<Modification> Modifications
    {
        get { return modifications; }
    }

  5. Alexander
    Admin
    Alexander avatar
    727 posts

    Posted 23 Dec 2011 Link to this post

    Hi Mateusz,

    You are right, TrackedBindingList<T> indeed implements IList<T>. After looking into the compilation error more carefully it turned out that the compiler is not able to resolve implicitly the HasAssociation overload which should be used for many to many associations. It actually uses the overload for one to many associations and considers that TrackedBindingList<T> is the target type for the association, while it should be just T. 

      This can be easily avoided by explicitly specifying the target type as a generic parameter of the HasAssociation method like this:  
    territoryMapping.HasAssociation<Employee>(t => t.Employees).WithOpposite(e => e.Territories)
        .MapJoinTable("EmployeeTerritories", (territory, employee) => new
        {
            EmployeeID = employee.EmployeeID,
            TerritoryID = territory.TerritoryID
        });
    The t.Employees collection above is actually a TrackedBindingList<Employee>. 

    Please note that OpenAccess generates collection properties with getters only but this is not mandatory, it is just to prevent the user from assigning collection instances which do not support change tracking. You can still create your properties with getters and setters.
    Hope that helps. 

    Kind regards,
    Alexander
    the Telerik team

    Q3’11 of Telerik OpenAccess ORM is available for download. Register for the What's New in Data Tools webinar to see what's new and get a chance to WIN A FREE LICENSE!

Back to Top