Databinding and context

Posts   
 
    
Kirk
User
Posts: 22
Joined: 26-Apr-2004
# Posted on: 18-May-2005 17:42:59   

I was wondering if there is a problem with retrieving an entity from Context and the ListChanged event firing. For example and EntityCollection is added to context and bound to a control. An updated entity is Fetched and Got from Context. The problem is the update does not seem to be reflected in databinding for entities already existing in Context. If I add a method to the collection to kick OnListChanged for the updated entity then all seems to work well.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39930
Joined: 17-Aug-2003
# Posted on: 18-May-2005 18:26:12   

Could you paste the code you're using for this update in the collection? I think it also shows a potential error in the events bound to an entity in a collection, but I need the exact code you're using to see if I'm right or not.

For example, if you do: collection[index] = myContext.Get(theEntity);

the OnListChanged event is fired. Though I now see there is a potential bug in doing this in relation to the events bound to the entity already on that index in the collection, because the Set clause of the indexer doesn't set the event handler nor resets the event handler. But I think that's not completely related to your question, though might be related.

Frans Bouma | Lead developer LLBLGen Pro
Kirk
User
Posts: 22
Joined: 26-Apr-2004
# Posted on: 19-May-2005 18:18:42   

I think the following is the essential code. appointsments is the bound collection. appts is another collection that contains AppointmentsEntity(s) just fetched. KickDataBindingRefresh is the method I added to EntityCollection that calls OnListChange. Due to the small snippet I hope what I am doing makes sense. If not I can expand code or try to explain better.


foreach (AppointmentsEntity appt in appts)
                {
                    AppointmentsEntity apptFromContext = (AppointmentsEntity)appointmentsContext.Get(appt);
                    if (apptFromContext.Deleted)
                    {
                        if (appointments.Contains(apptFromContext))
                        {
                            appointments.Remove(apptFromContext);
                        }
                    }
                    else
                    {               
                        appointments.KickDataBindingRefresh(appointments.Add(apptFromContext));
                    }
                }

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39930
Joined: 17-Aug-2003
# Posted on: 20-May-2005 11:54:13   

I've a problem understanding what's really going on, sorry. The text you posted:


For example and EntityCollection is added to context and bound to a control. An updated entity is Fetched and Got from Context. The problem is the update does not seem to be reflected in databinding for entities already existing in Context.

Now, the addition of the collection to the context, I understand. You then fetch an updated entity. My problem is: is this a new entity instance then added to the collection, or is this an entity already in the collection and you call some fetch method to get it updated? simple_smile So if possible could you paste the code of those steps so I have a clear view what the steps are and thus what's going on simple_smile . thanks.

Frans Bouma | Lead developer LLBLGen Pro
Kirk
User
Posts: 22
Joined: 26-Apr-2004
# Posted on: 20-May-2005 16:02:22   

Sorry, I hope this is all the pertinent code. frmAppointmentGrouping_Load initializes the appointments collection via GetAppointments. RefreshAppointments retrieves AppointmentsEntity(s) added to/updated in the db since last GetAppointments into another collection. This collection is then iterated through and appropriate action is taken against the appointments collection. If appointments.Add(apptFromContext); is used instead of appointments.KickDataBindingRefresh(appointments.Add(apptFromContext)); in RefreshAppointments when an existing item is added to appointments then it seems ItemChanged is not processed. Deleting or adding a new AppointmentsEntity does seem to work correctly.


private DateTime dtLastUpdate = new DateTime(1900,1,1);
private Context appointmentsContext = null;
private EntityCollection appointments = null;

private void GetAppointments(EntityCollection appts, DateTime dtSince, bool includeDeleted)
{
    DataAccessAdapter da = new DataAccessAdapter(); 

    IRelationPredicateBucket filterBucket = new RelationPredicateBucket();
    filterBucket.PredicateExpression.Add(
        PredicateFactory.CompareValue(AppointmentsFieldIndex.LastUpdate, ComparisonOperator.GreaterEqual, dtSince));
        
    if (!includeDeleted)
    {
        filterBucket.PredicateExpression.Add(
            PredicateFactory.CompareValue(AppointmentsFieldIndex.Deleted, ComparisonOperator.Equal, false));
    }

    ISortExpression sortClauses = new SortExpression();
    sortClauses.Add(SortClauseFactory.Create(AppointmentsFieldIndex.LastUpdate, SortOperator.Descending));

    da.FetchEntityCollection(appts, filterBucket, 0, sortClauses);
}

private void frmAppointmentGrouping_Load(object sender, System.EventArgs e)
{   
    appointmentsContext = new Context();
    appointments = new EntityCollection(new AppointmentsEntityFactory());           
    appointments.AllowNew = true;
    appointments.AllowEdit = true;
    appointments.AllowRemove = true;
    appointments.DoNotPerformAddIfPresent = true;
    appointments.BeforeRemove +=new EventHandler(appointments_BeforeRemove);
    appointmentsContext.Add(appointments);

    GetAppointments(appointments, dtLastUpdate);
}

private void RefreshAppointments()
{
    EntityCollection appts = new EntityCollection(new AppointmentsEntityFactory());     
    GetAppointments(appts, dtLastUpdate, true); 

    if (appts.Count > 0)
    {
        dtLastUpdate = ((AppointmentsEntity)appts[0]).LastUpdate;

        foreach (AppointmentsEntity appt in appts)
        {
            AppointmentsEntity apptFromContext = (AppointmentsEntity)appointmentsContext.Get(appt);
            if (apptFromContext.Deleted)
            {
                if (appointments.Contains(apptFromContext))
                {
                    appointments.Remove(apptFromContext);
                }
            }
            else
            {               
                appointments.KickDataBindingRefresh(appointments.Add(apptFromContext));
            }
        }
    }
}

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39930
Joined: 17-Aug-2003
# Posted on: 21-May-2005 11:13:38   

Ah, I now understand what's the case.

The 'Add()' method checks if the entity is already in the collection (it is) and simply returns the index. I.o.w.: it doesn't add it, if it's already there.

This uses the Equals method on an entity, which checks PK values. So if other fields in an entity are changed, this indeed doesn't get reflected by the event.

Frans Bouma | Lead developer LLBLGen Pro
Kirk
User
Posts: 22
Joined: 26-Apr-2004
# Posted on: 05-Apr-2006 15:49:16   

I have encountered this problem again. It has manifested itself when I perform a sort as follows:

private void SortHoursRanges() { System.Collections.ArrayList l = new System.Collections.ArrayList(hoursRanges.Items); l.Sort(); for (int i = 0; i < l.Count; i++) { hoursRanges.Items[i] = l[i]; }

hoursRanges.KickDataBindingRefresh(); }

Again the list does not update visually in the listbox it is bound to (I assume due to re-add of existing entity), but the indexes do change. If I kick binding with following that was added to EntityCollection all works well:

public void KickDataBindingRefresh() { this.OnListChanged(0, ListChangedType.Reset); }

Is this a fixable problem, or should I live with above solution?

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 05-Apr-2006 16:08:57   

In the sort method: I think you may clear your EntityCollection just before inserting the newly sorted items.

By the way, what's that for loop doing?

for (int i = 0; i < l.Count; i++)
{
hoursRanges.Items = l;
}

I see no index is used within the loop, I think you should use the index to get the items one by one from the sorted list and use EntityCOllection.Add() to insert them.

Kirk
User
Posts: 22
Joined: 26-Apr-2004
# Posted on: 05-Apr-2006 16:58:11   

There is an index i

hoursRanges.Items[i] = l[i]).

I lost it pasting the code outside code tags.

Unfortunately I do not want to Clear the collection in that I have other code tracking and taking action on entity removal.

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 05-Apr-2006 17:11:15   

Another suggestion:

Based on what do you perform the sort? You may always use the entityCollection sort functionality.

Please check the Client side sorting title under "Using the generated code -> Adapter -> Using the entity collection classes" in the LLBLGen Pro documentation manual.

Kirk
User
Posts: 22
Joined: 26-Apr-2004
# Posted on: 05-Apr-2006 20:56:57   

Thanks again for the suggestion. Unfortunately the sorting involves more than a single property (field) and use the following CompareTo:

public int CompareTo(object obj)
        {       
            CollectionLocationHoursRangeEntity comparee = (CollectionLocationHoursRangeEntity)obj;
            int compare = DateTimeHelper.CompareDayOfWeekMasks(
                (DateTimeHelper.DayOfWeekMask)this.DayOfWeekMask, (DateTimeHelper.DayOfWeekMask)comparee.DayOfWeekMask);

            if (compare == 0)
            {               
                if (this.StartTime == comparee.StartTime)
                {
                    if (this.EndTime == comparee.EndTime)
                    {
                        return 0;
                    }

                    return (this.EndTime > comparee.EndTime) ? 1 : -1;
                }

                return (this.StartTime > comparee.StartTime) ? 1 : -1;
            }

            return compare;
        }
Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 06-Apr-2006 08:21:45   

Walaa said: I think you may clear your EntityCollection just before inserting the newly sorted items Kirk said: Unfortunately I do not want to Clear the collection in that I have other code tracking and taking action on entity removal.

Another suggestion is to add the sorted entities to a new Collection.

If this is not possible then I see no other alternative than your previously mentioned:

If I kick binding with following that was added to EntityCollection all works well: public void KickDataBindingRefresh() { this.OnListChanged(0, ListChangedType.Reset); } Is this a fixable problem, or should I live with above solution?

Kirk
User
Posts: 22
Joined: 26-Apr-2004
# Posted on: 06-Apr-2006 22:58:20   

One more question, and then I'll let it be.

My alternate solution will work fine for my specific case, but shouldn't a ListChanged event be generated since the index of a currently existing item is changing (i.e ListChangedType.ItemMoved)?

Thanks, Kirk

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39930
Joined: 17-Aug-2003
# Posted on: 08-Apr-2006 12:03:17   

You overwrite the innerlist and that's not noticed by the code, so no OnListChanged event is raised.

Frans Bouma | Lead developer LLBLGen Pro