This is a migrated thread and some comments may be shown as answers.

trackedbindinglist becomes bindinglist bug (newest q3 and q2)

3 Answers 85 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
MATEUSZ
Top achievements
Rank 1
MATEUSZ asked on 19 Dec 2011, 12:58 AM
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);
}

3 Answers, 1 is accepted

Sort by
0
Alexander
Telerik team
answered on 20 Dec 2011, 06:28 PM
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!

0
MATEUSZ
Top achievements
Rank 1
answered on 20 Dec 2011, 06:40 PM
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; }
}

0
Alexander
Telerik team
answered on 23 Dec 2011, 03:06 PM
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!

Tags
General Discussions
Asked by
MATEUSZ
Top achievements
Rank 1
Answers by
Alexander
Telerik team
MATEUSZ
Top achievements
Rank 1
Share this question
or