1

I need help on how to use APM pattern, i am now reading some articles, but i am afraid i don't have much time. What i really want is to get all the persons(data from db) then get the photos and put it on a autocompletebox Code:

void listanomesautocomplete(object sender, ServicosLinkedIN.queryCompletedEventArgs e)
        {
            if (e.Result[0] != "")
            {
                for (int i = 0; i < e.Result.Count(); i = i + 3)
                {
                    Pessoa pessoa = new Pessoa();
                    pessoa.Nome = e.Result[i];
                    pessoa.Id = Convert.ToInt32(e.Result[i + 1]);
                    if (e.Result[i + 2] == "")
                        pessoa.Imagem = new BitmapImage(new Uri("Assets/default_perfil.png", UriKind.Relative));
                    else
                    {
                        ServicosLinkedIN.ServicosClient buscaimg = new ServicosLinkedIN.ServicosClient();
                        buscaimg.dlFotoAsync(e.Result[i + 2]);
                        buscaimg.dlFotoCompleted += buscaimg_dlFotoCompleted;//when this completes it saves into a public bitmapimage and then i save it into pessoa.Imagem
                        //basicly, what happends, the listadlfotosasync only happends after this function
                        //what i want is to wait until it completes and have into the class novamsg
                        pessoa.Imagem = img;//saving the photo from dlFotoAsync           

                    }
                    listaPessoas.Add(pessoa);
                }

                tbA_destinatario.ItemsSource = null;
                tbA_destinatario.ItemsSource = listaPessoas;

                BackgroundWorker listapessoas = new BackgroundWorker();
                listapessoas.DoWork += listapessoas_DoWork;
                listapessoas.RunWorkerAsync();
                tb_lerdestmsg.Text = "";
            }
            else
            {
                tbA_destinatario.ItemsSource = null;
                tb_lerdestmsg.Text = "Não encontrado";
            }
        }
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • What i need is this http://stackoverflow.com/questions/18753719/how-to-await-for-querycompleted-event OR(thesame) http://extensionmethod.net/csharp/dataservicequery-tresult/queryasync – user3285630 Feb 17 '14 at 03:11

2 Answers2

1

There are a few things to address here:

  • The listanomesautocomplete event handler should be async. Handle and report all exceptions possibly thrown inside it (generally, this rule applies to any event handler, but it's even more important for async event handlers, because the asynchronous operation inside your handler continues outside the scope of the code which has fired the event).

  • Register the dlFotoCompleted event handler first, then call dlFotoAsync. Do not assume that dlFotoAsync will be always executed asynchronously.

  • Think about the case when another listanomesautocomplete is fired while the previous operation is still pending. This well may happen as a result of the user's action. You may want to cancel and restart the pending operation (like this), or just queue a new one to be executed as soon as the pending one has completed (like this).

Back to the question, it's the EAP pattern that you need to wrap as Task, not APM. Use TaskCompletionSource for that. The relevant part of your code may look like this:

async void listanomesautocomplete(object sender, ServicosLinkedIN.queryCompletedEventArgs e)
{
    if (e.Result[0] != "")
    {
        for (int i = 0; i < e.Result.Count(); i = i + 3)
        {
            Pessoa pessoa = new Pessoa();
            pessoa.Nome = e.Result[i];
            pessoa.Id = Convert.ToInt32(e.Result[i + 1]);
            if (e.Result[i + 2] == "")
                pessoa.Imagem = new BitmapImage(new Uri("Assets/default_perfil.png", UriKind.Relative));
            else
            {
                // you probably want to create the service proxy 
                // outside the for loop
                using (ServicosLinkedIN.ServicosClient buscaimg = new ServicosLinkedIN.ServicosClient())
                {
                    FotoCompletedEventHandler handler = null;
                    var tcs = new TaskCompletionSource<Image>();

                    handler = (sHandler, eHandler) =>
                    {
                        try
                        {
                            // you can move the code from buscaimg_dlFotoCompleted here,
                            // rather than calling buscaimg_dlFotoCompleted
                            buscaimg_dlFotoCompleted(sHandler, eHandler);

                            tcs.TrySetResult(eHandler.Result);
                        }
                        catch (Exception ex)
                        {
                            tcs.TrySetException(ex);
                        }
                    };

                    try
                    {
                        buscaimg.dlFotoCompleted += handler;
                        buscaimg.dlFotoAsync(e.Result[i + 2]);

                        // saving the photo from dlFotoAsync
                        pessoa.Imagem = await tcs.Task;
                    }
                    finally
                    {
                        buscaimg.dlFotoCompleted -= handler;
                    }
                }
            }
            listaPessoas.Add(pessoa);
        }

        tbA_destinatario.ItemsSource = null;
        tbA_destinatario.ItemsSource = listaPessoas;

        BackgroundWorker listapessoas = new BackgroundWorker();
        listapessoas.DoWork += listapessoas_DoWork;
        listapessoas.RunWorkerAsync();
        tb_lerdestmsg.Text = "";
    }
    else
    {
        tbA_destinatario.ItemsSource = null;
        tb_lerdestmsg.Text = "Não encontrado";
    }
}
Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
0
void listanomesautocomplete(object sender, ServicosLinkedIN.queryCompletedEventArgs e)
        {

            if (e.Result[0] != "")
            {

                List<Pessoa> listaPessoas = new List<Pessoa>();

                for (int i = 0; i < e.Result.Count(); i = i + 3)
                {
                    Pessoa pessoa = new Pessoa();
                    pessoa.Nome = e.Result[i];
                    pessoa.Id = Convert.ToInt32(e.Result[i + 1]);
                    if (e.Result[i + 2] == "")
                        pessoa.Imagem = new BitmapImage(new Uri("Assets/default_perfil.png", UriKind.Relative));
                    else
                    {
                        ServicosLinkedIN.ServicosClient buscaimg = new ServicosLinkedIN.ServicosClient();
                        buscaimg.dlFotoAsync(e.Result[i + 2]);
                        //THIS ACTUALLY WORKS!!!
                        //THE THREAD WAITS FOR IT!
                        buscaimg.dlFotoCompleted += (s, a) =>
                        {
                            pessoa.Imagem = ConvertToBitmapImage(a.Result);
                        };
                    }
                    listaPessoas.Add(pessoa);
                }

                if (tbA_destinatario.ItemsSource == null)
                {

                    tbA_destinatario.ItemsSource = listaPessoas;
                }
                tb_lerdestmsg.Text = "";
            }
            else
            {
                tbA_destinatario.ItemsSource = null;
                tb_lerdestmsg.Text = "Não encontrado";
            }
        }

Man, i am not even mad, i am amazed. Noseratio, you answer gave me an idea and it actually worked!! Thanks a lot man, i can't thank you enough ;)

  • No problem, but this code is still incorrect. Think about what happens if `dlFotoCompleted` is fired in 1 second after you have called `dlFotoAsync`. The whole `for ` loop may be finished by then. – noseratio Feb 17 '14 at 04:29
  • Impossible i think, the way i tested, only 1 thread did all the work, but i will keep stressing the program, once again thank you for your time! – user3285630 Feb 17 '14 at 04:41
  • Something is wrong... i replicate exactly the code on other page and it doesn't work. I observed the debugging on the working on, and its pretty much magic, really, if (tbA_destinatario.ItemsSource == null) { tbA_destinatario.ItemsSource = listaPessoas; } if you check that code above it makes no sense right? after the first update on itemsource it shouldn't work anymore... but it does i have checked the whole solution by 'tbA_destinatario.ItemsSource' and nothing appears, something is filling the tbA_destinatario.ItemsSource, i wonder what. – user3285630 Feb 17 '14 at 05:14
  • You really should be using `TaskCompletionSource` to await for the event, similarly to how it's done in my answer. I can't help debugging this. – noseratio Feb 17 '14 at 05:24
  • 1
    Oh i copied again exactly, and it worked... after i deliver this project (in about 5 hours) i will check out those links you gave and not do ugly workarounds – user3285630 Feb 17 '14 at 05:26