views:

438

answers:

3

I have a WCF web service that currently searches multiple, hard-coded dtSearch indices and then merges the resulting datasets to be returned back to the client. I have the following C# code:

public class Search : ISearch
{
    delegate DataTable PDelegate(string term, int cid);
    delegate DataTable CDelegate(string term, int sid);

    public DataTable SearchPIndex(string term, int cid) {/* do search */}
    public DataTable SearchCIndex(string term, int sid) {/* do search */}

    public DataTable SearchAll(string term, int cid, int sid)
    {
        PDelegate pDel = new PDelegate(SearchPIndex);
        CDelegate cDel = new CDelegate(SearchCIndex);

        IAsyncResult pInvoke = pDel.BeginInvoke(term, cid, null, null);
        IAsyncResult cInvoke = cDel.BeginInvoke(temr, sid, null, null);

        DataTable pResults = pdel.EndInvoke(pInvoke);
        DataTable cResults = cdel.EndInvoke(cInvoke);

        // combine the DataTables and return them
    }
}

My question is: what is the best way to move this logic into a separate, generic class and do this for a List of 1...n objects?

I have a generic object that I have created that now performs all the physical searching (replacing the SearchPIndex and SearchCIndex methods), but I am uncertain as how I can integrate the delegate/IAsyncResult calls into the generic.

Is there a best-practice I can follow for this?

A: 

First thing to note about your code is that only 1 delegate type is required:

delegate DataTabe SearchAsync(string term, int index);

This means that the of SearchAll method can be something like:

public DataTable SearchAll(string term, List<int> indices) 
{
    var asyncs = new List<IAsyncResult>();
    foreach(int index in indices) 
    {
         SearchAsync sa = new SearchAsync(NewMethodSearchWithTermAndIndexParemeter);
         asyncs.Add(sa.BeginInvoke(term, index, null, null));
    }
    var tables = new List<DataTable>();
    foreach(IAsyncResult iar in asyncs) 
    {
         try 
         {
              tables.Add(sa.EndInvoke(iar));
         }
         catch 
         {
             ...appropriately handle
         } 
    }
    .... merge tables
}

I don't think your new method needs to be generic in the <T> sense. I hope this helps.

ps. I wrote this down off the top off my head without the aid of the compiler so please beware of the typos.

Bas Bossink
Thanks for the reply, this is close, but it doesn't quite work. The theory is good up until the "tables.Add(iar.EndInvoke());" line -- EndInvoke() is a method of the base delegate "sa" not of the IAsyncResult. The best I get with the iar variable is an "IsCompleted" property.
asmorger
Note that the above code seems nice but the fact the EndInvoke's are being performed sequentially and are blocking does not make this a very good example of 'improving' the performance by making things asynchronous. You probably want to use an AsyncCallBack like here: http://msdn.microsoft.com/en-us/library/2e08f6yc(VS.71).aspx
Bas Bossink
A: 

EDIT: Sorry... first time as a "user" on the site ... an "answer" seems like a better place for this than the "comment" above.

I'm going to play with it, but would this work within the method?

SearchAsync sa = new SearchAsync(SearchIndex); 
var asyncs = new List<IAsyncResult>(); 

foreach(int index in indices) 
{ 
    asyncs.Add(sa.BeginInvoke(term, index, null, null));
} 

var tables = new List<DataTable>(); 
foreach(IAsyncResult iar in asyncs)
{ 
    try
    { 
        tables.Add(sa.EndInvoke(iar)); 
    } 
    catch 
    { 
        //etc. 
    } 
}
asmorger
A: 

Ok, after further use of Google and your very helpful link, here's what I have now:

public class Search : ISearch
{
    delegate DataTable SearchAsync(int stypeid, string term, int? cid, int? sid);
    List<DataTable> tables;

    private void ProcessCallBack(IAsyncResult result)
    {
        AsyncResult asyncResult = (AsyncResult)result;
        SearchAsync async = (SearchAsync)asyncResult.AsyncDelegate;

        if(tables == null)
        {
            tables = new List<DataTable>();
        }

        try
        {
            tables.Add(async.EndInvoke(result));
        }
        catch(Exception ex)
        {
            /* handle error */
            tables.Add(null);
        }
    }

    public DataTable SearchIndex(int stypeid, string term, int? cid, int? sid) 
    {/* do search */}

    public DataTable SerachGlobal(string term, int? cid, int? sid)
    {
        List<SearchTypes> types ...; /* load types from db */
        SearchAsync async = new SearchAsync(SearchIndex);
        AsyncCallback callBack = new AsyncCallback(ProcessCallBack);

        foreach(SearchType t in types)
        {
            async.BeginInvoke(t.searchtypeid, term, cid, sid, callBack, null);
        }

        do
        {
            Thread.Sleep(100);
        }
        while(tables == null || tables.Count < types.Count);

        /* combine the tables */

    }
}

How does this look? The only thing that concerns me is the possibility of an infinite loop on the do/while...

asmorger