Try Catch in a couroutine with yield return for result

Hello everyone!

I have a WebService doing requests to a database and a proxy class on my Unity Project to call the service.
I have this snippet of code:

public IEnumerator LoginOperation()
        {
            try
            {
                LoginInfo = webService.Login(Username, Password);
                yield return LoginInfo;
                // Do other stuff here
            }
            catch(Exception e)
            {
                Debug.Log("There was an error while contacting the Service");
                Debug.LogError(e.Message);
            }
        }

That would do the job perfectly if it wasn’t for this error:

Cannot yield in the body of a catch clause

I need the yield return to let the loading icon spin and not freeze the client while the Service is performing the request, and still i need the try catch block to handle errors (for example if the service is offline).

How can i do that? I’ve been stuck by 2 days trying to find a solution without succeed.
Thanks to everyone who will answer.

1 Like

You can put the yield between 2 try-catches:

public void LoginOperation()
        {
            try
            {
                LoginInfo = webService.Login(Username, Password);
            }
            catch(Exception e)
            {
                Debug.Log("There was an error while contacting the Service");
                Debug.LogError(e.Message);
            }
            yield return LoginInfo;
            try {
                // Do other stuff here
            }
            catch(Exception e)
            {
                Debug.Log("There was an error while contacting the Service");
                Debug.LogError(e.Message);
            }
        }
1 Like

Hello and thanks for reply!
I’ve noticed a thing:

If i modify the code in this way :

public void LoginOperation()
        {
            try
            {
                LoginInfo = webService.Login(Username, Password);
                LoginInfo = webService.Login(Username, Password);
                LoginInfo = webService.Login(Username, Password);
                // Other 200+ lines like the ones above
            }
            catch(Exception e)
            {
                Debug.Log("There was an error while contacting the Service");
                Debug.LogError(e.Message);
            }
            yield return LoginInfo;
        
            Debug.Log("All done!");
        }

The application freezes waiting for the requests, actually i have a Debug.Log in the Update function that doesn’t show logs while the requests are being transferred and performed but they resume as soon as “All Done” is being logged.
So for a guess, does it also freezes for just one request but i don’t see it because it’s very fast?

Also, if i put a System.Threading.Thread.Sleep(5000) in the Login function of the webservice to slow it down, Unity waits for it before doing everything else (also the icon animation on the canvas is blocked waiting for the request).

What should i do?

Coroutines are async, but they run on the main thread. You can’t have blocking calls in them. Make sure you login function is async

1 Like

So should i make the function on the WCF Service Async (using Tasks) and then call it in Unity from a coroutine?
That will fix the issue?

If you enable 4.x runtime in unity 2018 or later you can use tasks but they have nothing todo with coroutines, but they solve same problem running async code in a sync manner. Do your wcf client have old style async methods?

They often had a method called BeginAsync or similar which started the operation in a none blocking manner than you polled the completion state. You can use that from a corotuine.

1 Like

Thank you answers they’re helping me alot.
I cannot use 4.x runtime due it causes me some errors with the System.ServiceModel dll.

So what i did was to execute this svcutil command to generate the proxyclass:
svcutil -out:MyService.cs http://localhost/?wsdl

I red that for making async methods you should add
svcutil -out:MyService.cs http://localhost/?wsdl /a

This actually gives me a “Begin” method that i don’t know how to use since it asks for an AsyncCallback and a object), plus i don’t have the “Method” with the “Async” suffix generated.

I red that for doing that you should launch this command
svcutil -out:MyService.cs http://localhost/?wsdl /a /tcv:Version35

But it generates me this error

Mono service contract conversion tool  0.1.0.0 - Copyright (C) 2006 Novell, Inc.

Attempting to download metadata from 'http://localhost/?wsdl' using DISCO..
Disco found documents at the following URLs:
- Xml Schema at    http://localhost/?xsd=xsd2
- Xml Schema at    http://localhost/?xsd=xsd1
- WSDL document at  http://localhost/?wsdl
- Xml Schema at    http://localhost/?xsd=xsd0

Unhandled Exception: System.NotSupportedException: Not supported file extension:
   at Mono.ServiceContractTool.Driver.Run(String[] args)
   at Mono.ServiceContractTool.Driver.Main(String[] args)

Please help me, I’m struggling with this from 2 days and since i woke up this morning.

It’s just a delegate taking a IAsyncResult as argument. You can use that to wait for completion in your coroutine.

1 Like

I’ve managed to run that command:
svcutil -out:MyService.cs http://localhost/?wsdl /a /tcv:Version35

And now IT WORKS
Just one thing…
```csharp
** public IEnumerator LoginOperation()
{
client.Login += MyClient_LoginCompleted;
client.LoginAsync();

        yield return null;

    }

    private void MyClient_LoginCompleted(object sender, LoginCompletedEventArgs e)
    {
        Debug.Log("completed");
        Debug.Log(e.Result);
    }**</strong>

__**__ **With the code above everything works fine, i can get the result and all is good.** **With this code** **csharp
** public IEnumerator LoginOperation()
{
client.Login += MyClient_LoginCompleted;
client.LoginAsync();

        yield return null;

    }

    private void MyClient_LoginCompleted(object sender, LoginCompletedEventArgs e)
    {
        Debug.Log("completed");

        if (e.Error.Message != null)
            Debug.Log("there was an error");
       else
        Debug.Log(e.Result);
    }**

```
For some strange reason i only get the “completed” log without the last e.Result log…
I can’t even imagine why this is happening…

You can’t use the unity framework outside the main thread. You can set a bool to true and wait for that in the corot. You can also switch to a rest service and use the built in functions for rest

1 Like

Sorry i didn’t understood.

I cannot use the if/else statements inside that void MyClient_LoginCompleted?
Why i can’t? What should i do to retrieve and process that data?

Thank you again.

I saw now you only us debug log those should work.

1 Like

Nothing else outside of Debug.Log ?
So i can say this is useless!

It seems that framework spawns its own thread that will call that event, you can’t access unity framework from that thread

1 Like

I was about to write “Well, nice rip-off”.
But i just tried this

        LoginCompletedEventArgs x;

        private void Update()
        {
            if(x != null)
            {
                if (x.Result != null)
                    Debug.Log(x.Result);
                else if (x.Error.Message != null)
                    Debug.Log(x.Error.Message);
            }
        }

        public IEnumerator LoginOperation()
        {
            client.LoginCompleted += SopClient_LoginCompleted;
            client.LoginAsync();

            yield return null;

        }

        private void MyClient_LoginCompleted(object sender, LoginCompletedEventArgs e)
        {
            x = e;
        }

And it seems to work nicely.

If that is, what i did was to make a copy of that class (it seems i’m allowed to do that) from the thread to Unity, and now i have a copy of that information usable by the unity framework.

Am i wrong to say that this is not a hack?

It’s a bit hacky. I would probably handle it inside a coroutine using a local event handler in that corot method. Or write a custom yield instruction.

1 Like

Can you please show me how would you do it by showing some code?
I didn’t understood what you said, but it’s seems a nice way to go :stuck_out_tongue:

I’m at the summer house on a phone :slight_smile:

But basically a local delegate like this, pseudo code

Private IEnumerator MyCorot()
{
   LoginCompletedEventArgs result:
   Service.Completed += (o, r) => result = r;

   yield return new Waituntil(() => result != null);

   //Do stuff
}
1 Like

Thank you so much for your replyes, you can-t imagine how i’m appreciating it, also that you’re sharing your time with me.

But i didn’t understood that snippet of code…
MyCorot is basically my public IEnumerator LoginOperation() ?
Service.Completed is my “client.LoginCompleted += SopClient_LoginCompleted;”.?

If so i didn’t understood that line at all:
Service.Completed += (o, r) => result = r;
What is o? what is r? And result?

Sorry but i didn’t understood and i would love to do it in that way, without having multiple methods and classes like SopClient_LoginCompleted for every request i have to make.

Thanks a lot!