-1

I'm trying to make a quick and dirty c# asp.net webforms app. Nothing fancy.

new webforms project, open the designer for default.aspx.cs

Then drag a button and a textbox off the toolbar onto the page.

Then double-click on the button in the designer to get the click method and it's 100% reproducible. The app is literally as simple as you can get.

This may be a really basic issue, I suspect it's an update on another thread issue.

In my default.aspx.cs file, I'm hitting a webAPi, getting "OK" back. I want the text of the textbox1 to change from "?" to "OK" returned from the web service.

I click the button everything executes as expected, I see the text get changed in the debugger, but on the webpage it's still the old "?" value.

I'm guessing it's because I'm updating it in a response on a different thread?

protected void Button1_Click( object sender, EventArgs e )
        {
            try
            {
                // Create an HttpClient instance 
                HttpClient client = new HttpClient();

                // Send a request asynchronously continue when complete 
                client.GetAsync( "http://localhost:44331/DoCdssLoginTests?sAdName=bob.bob" ).ContinueWith(
                    ( requestTask ) =>
                    {
                        // Get HTTP response from completed task. 
                        HttpResponseMessage response = requestTask.Result;

                        // Check that response was successful or throw exception 
                        response.EnsureSuccessStatusCode();

                        // Read response asynchronously as JsonValue
                        response.Content.ReadAsStringAsync().ContinueWith(
                            ( readTask ) =>
                            {
                                var result = readTask.Result;
                                //Do something with the result
                                TextBox1.Text = result.ToString();
                                Button1.Text = result.ToString();
                            } );
                    } );
            }
            catch ( Exception ex )
            {
                TextBox1.Text = ex.Message;
                throw;
            }

Can anyone see what I'm doing wrong? I tried updating the text on the button too, but it also looks like it got changed and is unaltered on the page refresh.

Eric Brown - Cal
  • 14,135
  • 12
  • 58
  • 97
  • 1
    I can't see how attempts at doing this async would matter, since the code has to wait for the response. And since that web method is part of the same project, then why not just call that method directly in code behind in place of a web request? (as now its confusing). Remember if your code exits the button routine(s), then that page is dumped out of memory and goes out of scope. If it goes out of scope, then you don't have a instance of the page class or anything to return to. That method being called is static? Not clear what the method does, since I don't see any file extension. – Albert D. Kallal Mar 06 '23 at 23:30
  • 1
    `ContinueWith` executes the provided delegate asynchronously on a separate thread, which means that any exceptions thrown within it will not be propagated to the calling thread. So if an exception is thrown within the `ContinueWith` method, it will not be caught by a try-catch block that is outside of it. This means that you may have an error inside the code in the `ContinueWith` method that you are not aware of. To catch exceptions thrown within `ContinueWith`, you need to wrap the delegate code in a try-catch block. – Vadim Martynov Mar 06 '23 at 23:33
  • 1
    "I've done classic asp.net with codebehinds...but not Web Forms" - those are contradictory statements. Web Forms was the original framework that ASP.NET shipped with. Perhaps you meant to say you've done classic ASP - though I don't remember seeing that having a code behind paradigm. Classic ASP did not have any .NET in it. – mason Mar 06 '23 at 23:50
  • You need to provide a [mcve]. At the very least, I'd expect to see your markup, and possibly your Page_Load method or anywhere else that interacts with this text box that you've verified is related to the issue. – mason Mar 06 '23 at 23:52
  • Turns out the error was in the code pasted and it reproduces in a new project. As expected it was an updating across threads issue. – Eric Brown - Cal Mar 07 '23 at 19:23

1 Answers1

0

Okay I figured it out.

Vadims comment about ContinueWith running in its own thread was the key.

I pulled the AsynchHelper from this post:

How to call asynchronous method from synchronous method in C#?

and added it to the class.

and then changed the method thusly:

    protected void Button1_Click( object sender, EventArgs e )
    {
        try
        {
            string buffer = string.Empty;
            // Create an HttpClient instance 
            HttpClient client = new HttpClient();

            var result = AsyncHelper.RunSync<string>( () => client.GetStringAsync( "http://localhost:44331/DoCdssLoginTests?sAdName=bob.bob" ) );
            
            TextBox1.Text = result;

        }
        catch ( Exception ex )
        {
            TextBox1.Text = ex.Message;
            throw;
        }

        
    }

internal static class AsyncHelper
        {
            private static readonly TaskFactory _myTaskFactory = new
                TaskFactory( CancellationToken.None,
                    TaskCreationOptions.None,
                    TaskContinuationOptions.None,
                    TaskScheduler.Default );

            public static TResult RunSync<TResult>( Func<Task<TResult>> func )
            {
                return AsyncHelper._myTaskFactory
                    .StartNew<Task<TResult>>( func )
                    .Unwrap<TResult>()
                    .GetAwaiter()
                    .GetResult();
            }

            public static void RunSync( Func<Task> func )
            {
                AsyncHelper._myTaskFactory
                    .StartNew<Task>( func )
                    .Unwrap()
                    .GetAwaiter()
                    .GetResult();
            }
        }

This eliminated the cross thread updating (which is know to be an issue) and simplified away the asynch call in a synchronous method.

Now it's working as expected.

Eric Brown - Cal
  • 14,135
  • 12
  • 58
  • 97