Search code examples
c#silverlightsilverlight-4.0dynamics-crmodata

Retrieve collection using Odata


I am trying to retrieve a group of entities based on an Id from Dynamics CRM in a Silverlight control.

I do not know how many entities will be returned from the query, but CRMs Rest service will only return 50 records per page. So you have to keep building the collection up until there are no pages left to retrieve from.

I am stuck trying to figure out how to let the MainPage know that the retrieve has been completed and there are no records left to retrieve. I ideally want to be able to do:

var opps = _crmManager.RetrieveOpportunities(accountId)
foreach (var opp in opps)
{
    //do stuff here
}

I am not even sure if that is possible in this scenario. If this is not possible I am guessing that I would have to do something like:

_crmManager.OpportunitiesLoaded += OpportunitiesLoaded
private void OpportunitiesLoaded(sender s, EventArgs e)
{
    foreach (var opp in _crmManager.Opportunities)
    {
        //Do work here
    }
}

If this is the case, how do I tell when my the entities have been completely retrieved and fire the event?

I have this set up so far:

//CrmManager.cs
private DataServiceCollection<Opportunity> _opportunity;
public DataServiceCollection<Opportunity> Opportunities
{
    get { return _opportunity;  }
    set { _opportunity= value; }
}

public void RetrieveOpportunities(Guid accountId)
{
    try
        {
            DataServiceQuery<Opportunity> query = (DataServiceQuery<Opportunity>)_context.OpportunitySet
                .AddQueryOption("$filter", "AccountId/Id eq guid'" + accountId + "'");

            query.BeginExecute(ProcessPages<Opportunity>, new PagingContext<Opportunity>()
            {
                ServiceContext = _context,
                Query = query,
                PageProcessor = delegate(DataServiceCollection<Opportunity> results)
                {
                    try
                    {
                        if (null == Opportunities)
                        {
                            Opportunities= new DataServiceCollection<Opportunity>(_context);
                            Opportunities.Load(results);
                        }
                        else
                        {
                            for (int i = 0; i < results.Count; i++)
                            {
                                Opportunities.Add(results[i]);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        //TODO catch
                    }

                    return true;
                }
            });
        }
        catch (SystemException ex)
        {
            //TODO Catch
        }
}
//
private static void ProcessPages<T>(IAsyncResult result)
{
    try
    {
        PagingContext<T> context = (PagingContext<T>)result.AsyncState;

        QueryOperationResponse<T> response;
        if (null == context.Query)
        {
            response = (QueryOperationResponse<T>)context.ServiceContext.EndExecute<T>(result);
        }
        else
        {
            response = (QueryOperationResponse<T>)context.Query.EndExecute(result);
            context.Query = null;
        }
         DataServiceCollection<T> results = new DataServiceCollection<T>(response);

        if (null != context.PageProcessor && !context.PageProcessor(results))
        {
            //Stop processing
            return;
        }

        DataServiceQueryContinuation<T> token = results.Continuation;
        if (null == token)
        {
            return;
        }

        context.ServiceContext.BeginExecute(token, ProcessPages<T>, context);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

The small PagingContext class it is using

sealed class PagingContext<T>
{
    public DataServiceContext ServiceContext { get; set; }

    public DataServiceQuery<T> Query { get; set; }

    public Func<DataServiceCollection<T>, bool> PageProcessor { get; set; }
}

On MainPage I currently call these methods using the following:

//MainPage.xaml.cs
_crmManager.RetrieveOpportunities(accountId)
foreach( var opportunity in _crmManager.Opportunities)
{
    //Do work here
}

The problem is that this works some of the time. Sometimes the Opportunities collection has some (not all) items and it will do work for those items. Other times it is still empty when I try to access it.

Any help or nudge in the right direction would be greatly appreciated.


Solution

  • I couldn't find a way to do it as I wanted: So I ended up firing an event when the collection was populated. I added this to the CrmManager class:

    public delegate void CrmManagerEventHandler(object sender, EventArgs e);
    public event CrmManagerEventHandler OpportunitiesPopulated;
    

    And then in the RetrieveOpportunities method in the same class:

    if ((results.Continuation == null) && (OpportunitiesPopulated!= null))
    {
        OpportunitiesPopulated(this, e);
    }
    

    Then in the MainWindow.xaml.cs, I subscribed to the event to do the work I wanted to do. In the set up of MainWindow.xaml.cs:

    _manager.OpportunitiesPopulated += OpportunitiesPopulated;
    

    And the actual method:

    private void OpportunitiesPopulated(object sender, EventArgs e)
    {
        //Do all the work here
    }
    

    This fired only when the opportunites had finished populating which is what I needed to happen.