Question

I'm following this blog post on how to use batch support in ASP.NET Web API 2. The server code appears to work and listens on port 12345.

When I run the client code, it gets to this line:

dynamic listOfCustomers = JToken.Parse(await client.GetStringAsync("http://localhost:12345/api/WebCustomers"));

and exits without any exception being thrown. Here is the debug output:

'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities.Sync\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.Debugger.Runtime\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Debugger.Runtime.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'c:\users\documents\visual studio 2013\Projects\client_batch\client_batch\bin\Debug\client_batch.vshost.exe'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Data.DataSetExtensions\v4.0_4.0.0.0__b77a5c561934e089\System.Data.DataSetExtensions.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.CSharp\v4.0_4.0.0.0__b03f5f7f11d50a3a\Microsoft.CSharp.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Net.Http\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Net.Http.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
The thread 0x1b80 has exited with code 259 (0x103).
The thread 0x1fec has exited with code 259 (0x103).
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'c:\users\mryan.asavie\documents\visual studio 2013\Projects\client_batch\client_batch\bin\Debug\client_batch.exe'. Symbols loaded.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'c:\users\mryan.asavie\documents\visual studio 2013\Projects\client_batch\client_batch\bin\Debug\Newtonsoft.Json.dll'. Cannot find or open the PDB file.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'c:\users\mryan.asavie\documents\visual studio 2013\Projects\client_batch\client_batch\bin\Debug\System.Net.Http.Formatting.dll'. Cannot find or open the PDB file.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'c:\users\mryan.asavie\documents\visual studio 2013\Projects\client_batch\client_batch\bin\Debug\Ploeh.AutoFixture.dll'. Cannot find or open the PDB file.
'client_batch.vshost.exe' (CLR v4.0.30319: client_batch.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
The thread 0x1920 has exited with code 259 (0x103).
The thread 0x14ac has exited with code 259 (0x103).
The program '[7268] client_batch.vshost.exe' has exited with code 0 (0x0).

This is the code for the server:

namespace MyWebApp
{
    class Program {

        static void Main(string[] args)
        {
            string serviceUrl = "http://localhost:12345";
            using (WebApp.Start(serviceUrl, Configuration))
            {

                Console.WriteLine("Service listening at {0}", serviceUrl);
                Console.WriteLine("Press any key to stop the service and exit the application");
                Console.ReadKey();
            }
        }

        private static void Configuration(IAppBuilder builder)
        {
            HttpConfiguration configuration = new HttpConfiguration();
            HttpServer server = new HttpServer(configuration);
            configuration.Routes.MapHttpRoute(
                name: "api",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional });
            builder.UseWebApi(server);
        }

        public class Customer
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }

        public class WebCustomersController : ApiController
        {
            CustomersContext context = new CustomersContext();
            [Queryable(PageSize = 10, MaxExpansionDepth = 2)]
            public IHttpActionResult Get()
            {
                return Ok(context.Customers);
            }

            public async Task<IHttpActionResult> Post([FromBody] Customer entity)
            {
                if (entity == null)
                {
                    return BadRequest(ModelState);
                }
                context.Customers.Add(entity);
                await context.SaveChangesAsync();
                return CreatedAtRoute("api", new { controller = "ApiCustomers" }, entity);
            }

            public async Task<IHttpActionResult> Put(int id, [FromBody] Customer entity)
            {
                if (entity == null)
                {
                    return BadRequest(ModelState);
                }
                else if (id != entity.Id)
                {
                    return BadRequest("The key from the url must match the key of the entity in the body");
                }
                var originalCustomer = await context.Customers.FindAsync(id);
                if (originalCustomer == null)
                {
                    return NotFound();
                }
                else
                {
                    context.Entry(originalCustomer).CurrentValues.SetValues(entity);
                    await context.SaveChangesAsync();
                }
                return Content(HttpStatusCode.OK, entity);
            }

            public async Task<IHttpActionResult> Delete(int id)
            {
                Customer entity = await context.Customers.FindAsync(id);
                if (entity == null)
                {
                    return NotFound();
                }
                else
                {
                    context.Customers.Remove(entity);
                    await context.SaveChangesAsync();
                    return StatusCode(HttpStatusCode.NoContent);
                }
            }
        }

        public class CustomersContext : DbContext
        {
            static CustomersContext()
            {
                Database.SetInitializer<CustomersContext>(new CustomersContextInitializer());
            }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
            }

            private class CustomersContextInitializer : DropCreateDatabaseAlways<CustomersContext>
            {
                protected override void Seed(CustomersContext context)
                {
                    Fixture fixture = new Fixture();
                    IEnumerable<Customer> customers = fixture.CreateMany<Customer>(20).ToList();
                    context.Customers.AddRange(customers);
                }
            }

            public DbSet<Customer> Customers { get; set; }
        }
    }
}

And this for the client:

namespace client_batch
{
    class Program
    {
        static void Main(string[] args)
        {
            start();
        }

        private async static void start()
        {
            Fixture fixture = new Fixture();
            HttpClient client = new HttpClient();

            dynamic listOfCustomers = JToken.Parse(await client.GetStringAsync("http://localhost:12345/api/WebCustomers"));
            dynamic firstCustomer = listOfCustomers[0];
            firstCustomer.Name = "Peter";
            dynamic secondCustomer = listOfCustomers[1];
            JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter();

            //Create a request to query for customers
            HttpRequestMessage queryCustomers = new HttpRequestMessage(HttpMethod.Get, "http://localhost:12345/api/WebCustomers");
            //Create a message to add a customer
            HttpRequestMessage addCustomer = new HttpRequestMessage(HttpMethod.Post, "http://localhost:12345/api/WebCustomers");
            addCustomer.Content = new ObjectContent<Customer>(fixture.Create<Customer>(), formatter);
            //Create a message to update a customer
            HttpRequestMessage updateCustomer = new HttpRequestMessage(HttpMethod.Put, string.Format("http://localhost:12345/api/WebCustomers/{0}", firstCustomer.Id));
            updateCustomer.Content = new ObjectContent<dynamic>(firstCustomer, formatter);
            //Create a message to remove a customer.
            HttpRequestMessage removeCustomer = new HttpRequestMessage(HttpMethod.Delete, string.Format("http://localhost:12345/api/WebCustomers/{0}", secondCustomer.Id));

            //Create the different parts of the multipart content
            HttpMessageContent queryContent = new HttpMessageContent(queryCustomers);
            HttpMessageContent addCustomerContent = new HttpMessageContent(addCustomer);
            HttpMessageContent updateCustomerContent = new HttpMessageContent(updateCustomer);
            HttpMessageContent removeCustomerContent = new HttpMessageContent(removeCustomer);

            //Create the multipart/mixed message content
            MultipartContent content = new MultipartContent("mixed", "batch_" + Guid.NewGuid().ToString());
            content.Add(queryContent);
            content.Add(addCustomerContent);
            content.Add(updateCustomerContent);
            content.Add(removeCustomerContent);

            //Create the request to the batch service
            HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, "http://localhost:12345/api/batch");
            //Associate the content with the message
            batchRequest.Content = content;
        }

        public class Customer
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    }
}

I'm new to c# so any pointers would be appreciated!

Was it helpful?

Solution

When you use the await keyword in your code on an async method, what you're really telling the program is "Listen, you go ahead and do work, call me back when you're done (Of course, this isn't that simple, there is alot of overhead the compiler is doing for us which we dont see, you can read about it here)".

So what actually happends in your code is that

dynamic listOfCustomers = JToken.Parse(await client.GetStringAsync("http://localhost:12345/api/WebCustomers"));

yields control back to the caller, which in this case is a method called start(), which cannot be awaited since it is a Main method. start got the control back and wasn't awaited on, so the next line of code continues executing, which results in your program ending.

What you need to do is Wait() on start so it wont end when control is yielded back:

class Program
{
  static void Main(string[] args)
  {
    start().Wait();
  }

  private async static void start()
  {
    Fixture fixture = new Fixture();
    HttpClient client = new HttpClient();

    dynamic listOfCustomers = JToken.Parse(await client.GetStringAsync("http://localhost:12345/api/WebCustomers").ConfigureAwait(false));
     // Rest Of Code..
  }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top