Ir para o conteúdo
  • Revista PROGRAMAR: Já está disponível a edição #60 da revista programar. Faz já o download aqui!

sparcopt

Service reference e callback

Mensagens Recomendadas

sparcopt

Bom dia!

Estou a construir uma aplicação móvel para Windows Phone 8 na qual vou ter de usar webservices de maneira a conseguir ler um determinado tipo de dados.

No Visual Studio Express 2012 for Windows Phone já adicionei um service reference e ele automaticamente gerou as classes que encontrou para o webservice em questão (ver imagem abaixo).

2w2kgmv.jpg

Tudo corre bem até aqui e consigo ler os dados que pretendo com o seguinte bloco de código, que armazena no vector providers o resultado obtido.

       public void getAllProvidersMethod()
       {
           sc.getAllProvidersCompleted += new EventHandler<ServiceReference2.getAllProvidersCompletedEventArgs>(GetCallback);
           sc.getAllProvidersAsync();
       }

       //GET ALL PROVIDERS
       public void GetCallback(object sender, ServiceReference2.getAllProvidersCompletedEventArgs e)
       {
           providers = new String[e.Result.Length];
           for (int i = 0; i < e.Result.Length; i++)
           {
               providers[i] = e.Result[i].ToString();
               MessageBox.Show(e.Result[i].ToString());
           }
       }

O problema está na altura em que o código é executado. Em pretendo que ele execute o código do método GetCallback de uma forma sequencial, ou seja, logo após o código anterior. Mas o que eu verifico é que ele só executa essa parte do código no final do programa e se eu tentar ler o vector providers ou tentar usá-lo noutra parte do programa antes do seu final, simplesmente não consigo pois ele não armazenou lá nada (não correu o código ainda).

Eu pesquisei um pouco sobre isto e cheguei à conclusão que existem dois tipos de chamadas (callback), as assíncronas e as síncronas. Fiquei também a saber que no caso das assíncronas ele continua a executar o resto do programa enquanto espera pelo retorno dos dados do webservice, o que não pretendo pois necessito que ele espere por essa chamada e só depois corra o resto do programa.

Como resolver isto?

Obrigado pela atenção, cumprimentos.

Editado por sparcopt

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pikax

Procura ver se existe a chamada 'a funcao(getAllProvidersAsync) sem ser async, se nao existir, podes criar uma variavel de referencia a bloquearte o resto do codigo ate' que seja executada o callback


Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nelsonr

Experimenta algo deste tipo

bool GetAllProvidersCompleted = false;

public void getAllProvidersMethod()
{
sc.getAllProvidersCompleted += new EventHandler<ServiceReference2.getAllProvidersCompletedEventArgs>(GetCallback);
sc.getAllProvidersAsync();

while (!GetAllProvidersCompleted)
{
	Thread.Sleep(500);
}

// Já deve ser possível ler os providers aqui
}

//GET ALL PROVIDERS
public void GetCallback(object sender, ServiceReference2.getAllProvidersCompletedEventArgs e)
{
providers = new String[e.Result.Length];
for (int i = 0; i < e.Result.Length; i++)
{
	providers[i] = e.Result[i].ToString();
	MessageBox.Show(e.Result[i].ToString());
}

GetAllProvidersCompleted = true;
}

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
sparcopt

Olá nelsonr! Desde já obrigado pela resposta.

Peguei no teu exemplo e testei mas não deu. Supostamente deveria dar pois tem toda a lógica mas ele apenas inicia a aplicação e fica-se pelo loading, ou seja a variável GetAllProvidersCompleted fica sempre a false.

Este é outro exemplo que tentei mas sem sucesso, fica-se pelo loading de igual forma.

private System.Threading.AutoResetEvent waitRun_m;

public void getAllProvidersMethod()
{
       waitRun_m = new System.Threading.AutoResetEvent(false);
       sc.getAllProvidersCompleted += new EventHandler<ServiceReference2.getAllProvidersCompletedEventArgs>(GetCallback);
       sc.getAllProvidersAsync();

       waitRun_m.WaitOne();
}

//GET ALL PROVIDERS
public void GetCallback(object sender, ServiceReference2.getAllProvidersCompletedEventArgs e)
{
       providers = new String[e.Result.Length];
       for (int i = 0; i < e.Result.Length; i++)
       {
               providers[i] = e.Result[i].ToString();
               MessageBox.Show(e.Result[i].ToString());
       }
       waitRun_m.Set();
}

EDIT: outra dúvida. Porque é que não consigo mudar a opção a vermelho quando adiciono um service reference? Se conseguisse tirar o check do asynchronous operations talvez já tivesse o meu problema resolvido? (a janela abaixo é a janela que me aparece quando clico no botão Advanced da primeira imagem deste tópico)

1zf3ei0.jpg

Editado por sparcopt

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nelsonr

Então nunca chega a chamar a função de GetCallback? Experimentaste por um breakpoint?

E como o pikax sugeriu, já viste se não existe uma função para fazer a chamada em modo sync? (do tipo sc.getAllProvidersSync)

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nelsonr

Uma outra hipotese, é usares o componente de timer, ligas apos a chamda à função sc.getAllProvidersAsync();

Crias na mesma uma variavel na class (GetAllProvidersCompleted ) e no tick do timer verificas se mudou para true.

Desligas o timer depois.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
sparcopt

Então nunca chega a chamar a função de GetCallback? Experimentaste por um breakpoint?

E como o pikax sugeriu, já viste se não existe uma função para fazer a chamada em modo sync? (do tipo sc.getAllProvidersSync)

Sim, ele não chega a chamar a função GetCallback pois a variável fica sempre a false. Isto acontece pois ele só chama a função GetCallback mesmo no fim do programa. Ele não a chama dentro da função GetAllProvidersMethod.

Pelo que tenho pesquisado, até agora ainda não encontrei para fazer em modo sync.

Descobri que no meu programa tenho um ficheiro Reference.cs no qual tenho algum código gerado automaticamente pelo service reference e que é usado para fazer a chamada. Pegando no seguinte excerto de código:

public System.IAsyncResult BeginGetCitiesByCountry(string CountryName, System.AsyncCallback callback, object asyncState) {
               object[] _args = new object[1];
               _args[0] = CountryName;
               System.IAsyncResult _result = base.BeginInvoke("GetCitiesByCountry", _args, callback, asyncState);
               return _result;
           }

public string EndGetCitiesByCountry(System.IAsyncResult result) {
               object[] _args = new object[0];
               string _result = ((string)(base.EndInvoke("GetCitiesByCountry", _args, result)));
               return _result;
           }

E olhando para este exemplo: http://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncwaithandle.aspx

Parece relativamente simples de esperar que ele ele complete a chamada e só depois corra o resto do código, mas ainda não estou a conseguir. Isto foi o que tentei:

public System.IAsyncResult BeginGetCitiesByCountry(string CountryName, System.AsyncCallback callback, object asyncState) {
               object[] _args = new object[1];
               _args[0] = CountryName;
               System.IAsyncResult _result = base.BeginInvoke("GetCitiesByCountry", _args, callback, asyncState);
               _result.AsyncWaitHandle.WaitOne();
               //FAZER ENDEVOKE
               //string returnValue = base.EndInvoke("GetCitiesByCountry", _args, ??????);
               _result.AsyncWaitHandle.Close();
               return _result;
           }

O problema é no EndInvoke que não estou a conseguir implementar.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nelsonr

A questão é que o async não deve estar a ser executado num thread separado, por isso é que o exemplo que te dei inicialmente não funciona.

Experimentaste com o timer?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
sparcopt

Tenho continuado de volta disto e tenho algumas actualizações.

Ao que parece os projectos para Windows Phone 8 não suportam task based operations daí a opção que mostrei numa resposta atrás estar desactivada.

Encontrei um exemplo que de certa forma da a volta a isto mas no entanto não estou a conseguir fazer a chamada como deve ser. O exemplo encontra-se aqui: http://stackoverflow.com/questions/14103288/how-can-i-use-async-await-to-call-a-webservice

Nota: adaptei o exemplo dessa página ao meu programa.

public static class Extension
       {
           public static void TransferCompletion<T>(TaskCompletionSource<T> tcs, System.ComponentModel.AsyncCompletedEventArgs e, Func<T> getResult)
           {
               if (e.Error != null)
               {
                   tcs.TrySetException(e.Error);
               }
               else if (e.Cancelled)
               {
                   tcs.TrySetCanceled();
               }
               else
               {
                   tcs.TrySetResult(getResult());
               }
           }

           public static Task<GetCitiesByCountryCompletedEventArgs> LoginAsyncTask(this GlobalWeatherSoapClient client, string name)
           {
               var tcs = new TaskCompletionSource<GetCitiesByCountryCompletedEventArgs>();
               client.GetCitiesByCountryCompleted += (s, e) => TransferCompletion(tcs, e, () => e);
               client.GetCitiesByCountryAsync(name);
               return tcs.Task;
           }
       }

A chamada. Ao escrever client. não me aparece nenhum LoginAsyncTask :/

async void example()
       {
           GlobalWeatherSoapClient client = new GlobalWeatherSoapClient();
           var login = await client.LoginAsyncTask(name);
       }

Editado por sparcopt

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
sparcopt

Já consegui resolver o problema acima mas tenho uma dúvida.

Onde e como posso imprimir o resultado obtido, ou seja, o conteúdo que me é devolvido?

Supostamente deveria ser algo do tipo e.Result.toString()....

Mas o e. não encontra Result...

Edit: já consegui.

Editado por sparcopt

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.