.NET Core OData client using Microsoft.OData.Client

If you are using .NET Core, you’ll have quickly found that the .NET OData packages don’t work with it. Fortunately, the most recent (as of today) version of Microsoft.OData.Client (v7.5.0) works fine with .NET Core. Once you add the package to your project, you get a T4 file generated for you (.tt extension). Edit the Configuration class in the file and put in your connection info.

public static class Configuration
{
  public const string MetadataDocumentUri = "http://localhost:58200/odata/";

  public const bool UseDataServiceCollection = true;

  public const string NamespacePrefix = "ConsoleAppClient";

  public const string TargetLanguage = "CSharp";

  public const bool EnableNamingAlias = true;

  public const bool IgnoreUnexpectedElementsAndAttributes = true;
}

Now you can use the auto-generated Container class to make OData calls against any OData service. Here’s some example usage.

var container = new Container(new Uri("http://localhost:58200/odata/"));

var response = await container.Clients.ExecuteAsync();

var query = (DataServiceQuery)container
    .Clients
    .Expand("Address")
    .Where(it => it.Id > 10);

var resp2 = await query.ExecuteAsync();
foreach (Client item in resp2)
{
    Console.WriteLine(item.Address.City);
}

foreach (Client item in response)
{
    Console.WriteLine(item.Name);
}

var c = container.Clients.ByKey(100);
var s = await c.NameLength().GetValueAsync();
Console.WriteLine(s);

If you look at your IIS logs, you’ll see corresponding OData queries in there.

GET /odata/Clients - 58200 - ::1 Microsoft.OData.Client/7.5.0 - 200 0 0 31
GET /odata/Clients $filter=Id gt 10&$expand=Address 58200 - ::1 Microsoft.OData.Client/7.5.0 - 200 0 0 32
GET /odata/Clients(100)/Default.NameLength() - 58200 - ::1 Microsoft.OData.Client/7.5.0 - 200 0 0 130
Advertisements

ASP.NET Core, OData, and Swashbuckle – workaround for error

If you are trying to use Swashbuckle with an ASP.NET Core project that uses OData, you are going to get an error on the swagger endpoint. The error will be something like this.

InvalidOperationException: No media types found in ‘Microsoft.AspNet.OData.Formatter.ODataOutputFormatter.SupportedMediaTypes’. Add at least one media type to the list of supported media types.

Until one of these guys between them fix this, here’s the hackish fix. It won’t enable Swagger for the OData controllers, but it will stop Swagger from breaking for the other controllers.

services.AddMvc(op =>
{
    foreach (var formatter in op.OutputFormatters
        .OfType<ODataOutputFormatter>()
        .Where(it => !it.SupportedMediaTypes.Any()))
    {
        formatter.SupportedMediaTypes.Add(
            new MediaTypeHeaderValue("application/prs.mock-odata"));
    }
    foreach (var formatter in op.InputFormatters
        .OfType<ODataInputFormatter>()
        .Where(it => !it.SupportedMediaTypes.Any()))
    {
        formatter.SupportedMediaTypes.Add(
            new MediaTypeHeaderValue("application/prs.mock-odata"));
    }
});

OData with ASP.NET Core

If you are writing .NET Core REST services to expose data entities, I would recommend using OData now that ASP.NET Core support has been added. You need to add the Nuget package – Microsoft.AspNetCore.OData (at the time of writing, the latest stable version is 7.0.1). One caveat is that Swagger and Swashbuckle will not work with your OData controllers. The issue has been reported on the Swashbuckle git forums, and hopefully it will be resolved in the not too far future. I quickly tested out a demo app and it seemed to work as expected. Here are the model classes.

public class Client
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public Address Address { get; set; }
}

public class Address
{
    [Key]
    public int Id { get; set; }

    public string Street { get; set; }

    public string City { get; set; }

    public string State { get; set; }
}

Here’s what you add to ConfigureServices. Add it before the call to AddMvc.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ClientContext>(op =>
    {
        op.UseInMemoryDatabase("clientsDatabase");
    });

    services.AddOData();

And here are the changes made to the Configure method. Those are extension methods added by the OData package, and essentially turns on those OData features on your service. Note that I’ve added two entity sets as well as a function on one of the entities.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseMvc(rb => 
    {
        rb.Expand().OrderBy().Select().Filter();
        rb.MapODataServiceRoute("odata", "odata", BuildEdmModel());
    });
}

public static IEdmModel BuildEdmModel()
{
    var builder = new ODataConventionModelBuilder();
    builder.EntitySet<Client>("Clients");
    builder.EntitySet<Address>("Addresses");
    builder.EntityType<Client>()
        .Function("NameLength")
        .Returns<string>();
    return builder.GetEdmModel();
}

And this is my mocked up data context class (based off EF Core).

public class ClientContext : DbContext
{
    public ClientContext(DbContextOptions<ClientContext> options) : base(options)
    {
    }

    public DbSet<Client> Clients { get; set; }

    public DbSet<Address> Addresses { get; set; }

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

    public void InitializeIfEmpty()
    {
        if (this.Clients.Count() == 0)
        {
            var address = new Address()
            {
                Id = 25,
                Street = "100 Main St.",
                City = "Columbus",
                State = "OH"
            };

            this.Addresses.Add(address);

            this.Clients.Add(new Client()
            {
                Id = 100,
                Name = "Sam Slick",
                Address = address
            });

            this.Clients.Add(new Client()
            {
                Id = 105,
                Name = "Janet Slick",
                Address = address
            });

            this.SaveChanges();
        }
    }
}

This is how we setup a Clients endpoint.

public class ClientsController : ODataController
{
    private ClientContext _dbContext;

    public ClientsController(ClientContext context)
    {
        _dbContext = context;

        _dbContext.InitializeIfEmpty();
    }

Adding methods to pull all clients, a specific client, and a specific client’s address.

[EnableQuery]
public IActionResult Get()
{
    return Ok(_dbContext.Clients);
}

[EnableQuery]
public IActionResult Get(int key)
{
    Client client = _dbContext.Clients
        .Include(c => c.Address)
        .FirstOrDefault(it => it.Id == key);

    return Ok(client);
}

[EnableQuery]
public IActionResult GetAddress([FromODataUri]int key)
{
    Client client = _dbContext.Clients
        .Include(c => c.Address)
        .FirstOrDefault(it => it.Id == key);

    return base.Ok(client.Address);
}

Here’s how you would add a Post action.

[EnableQuery]
public IActionResult Post([FromBody] Client client)
{
    _dbContext.Add(client);
    _dbContext.SaveChanges();
    return Created(client);
}

And here’s how you expose the OData action.

public IActionResult NameLength([FromODataUri]int key)
{
    var client = _dbContext.Clients.FirstOrDefault(it => it.Id == key);

    return Ok($"{client.Name} -> {client.Name.Length}");
}

That’s all for the demo. Now you can make standard OData queries against this API.

http://localhost:52913/odata/clients
http://localhost:52913/odata/clients?$expand=Address
http://localhost:52913/odata/clients/105/address
http://localhost:52913/odata/clients/105/NameLength

Handling application lifetime events for a hosted service

The IApplicationLifetime interface allows hosted services to gracefully handle startup and shutdown events. It has three properties of type CancellationToken.

  • ApplicationStarted
  • ApplicationStopping
  • ApplicationStopped

You can register callbacks for one or more of those notifications.

public HostedService(
  ILogger<HostedService> logger, 
  IApplicationLifetime applicationLifetime)
{
    _logger = logger;
    _applicationLifetime = applicationLifetime;
}

public Task StartAsync(CancellationToken cancellationToken)
{
    _logger.LogInformation("Entering StartAsync");

    _applicationLifetime.ApplicationStarted.Register(OnStarted);
    _applicationLifetime.ApplicationStopping.Register(OnStopping);
    _applicationLifetime.ApplicationStopped.Register(OnStopped);

    _timer = new Timer(
        ScheduledTask,
        this,
        TimeSpan.FromSeconds(3),
        TimeSpan.FromSeconds(3));

    return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
    _logger.LogInformation("Entering StopAsync");

    return Task.CompletedTask;
}

private void OnStarted()
{
    _logger.LogInformation("OnStarted called.");
}

private void OnStopping()
{
    _logger.LogInformation("OnStopping called.");
}

private void OnStopped()
{
    _logger.LogInformation("OnStopped called.");
}

The IApplicationLifetime interface also has a StopApplication method which unsurprisingly does just that, which is to terminate the current application.

private void ScheduledTask(object state)
{
    var source = state as HostedService;

    _logger.LogInformation($"ScheduledTask invocation {source._count} via timer.");

    if (source._count++ == 3)
    {
        _logger.LogInformation("Shutting down...");
        source._applicationLifetime.StopApplication();
    };
}

The screenshot below shows the order in which these notifications are fired. It’s quite intuitive and just as you’d expect.

IHostedService application lifetime

Generic hosted services in .NET Core using IHost and IHostedService

An approach used to simulate a long running or permanently running background task for cloud services is to deploy a REST API, and to invoke end-points on the API from a cloud-native event handler (AWS Lambdas or Azure Functions). When migrating on-prem Windows services, people would take this approach when the other option would have been to host their service in a virtual machine (AWS EC2 or Azure VM). .NET Core 2.1 has introduced a simpler alternative via the IHost and IHostedService interfaces.

The core idea here is to use the ASP.NET Core framework but without the HTTP engine. The current long term goal is to make this the default (so in future, even web-apps will use the generic host mechanism). So from that perspective, it’s not a bad idea to start using it preemptively. Here’s a really simple fully functional ASP.NET Core generic host service. All it does it launch a timer proc every 5 seconds. Think of it as the equivalent of a scheduled task.

class MyHostedService : IHostedService
{
    public static async Task Main(string[] args)
    {
        await new HostBuilder()
            .ConfigureServices((context, services) =>
            {
                services.AddLogging();
                services.AddHostedService<MyHostedService>();
            })
            .ConfigureLogging((context, logging) =>
            {
                logging.AddConsole();
            })
            .UseConsoleLifetime()
            .Build()
            .RunAsync();
    }

    ILogger<HostedService> _logger;

    public MyHostedService(ILogger<HostedService> logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        new Timer(
            state => { _logger.LogInformation("Timer invoked"); },
            this,
            TimeSpan.FromSeconds(0),
            TimeSpan.FromSeconds(5));

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

VC++ 2015 – Lazy generators

If you have ever used iterators in C# or Visual Basic, then this is essentially the same thing. You would need to enable the new experimental /await compiler option, same as in the previous blog entry.

std::experimental::generator<int> evens(size_t count)
{
  using namespace std::chrono;

  for (size_t i = 1; i <= count; i++)
  {
    yield i * 2;
    std::cout << "yielding..." << std::endl;
    std::this_thread::sleep_for(1s);
  }
}

Calling this method would be fairly straightforward.

void main_yield()
{
  for (auto ev : evens(7))
  {
    std::cout << ev << std::endl;
  }
}

And here’s the expected output.

yield_demo_cpp

I added the console output to demonstrate the laziness of the generator.

VC++ 2015 – Using resumable functions

Visual C++ 2015 has a new compiler option called /await that can be used to write and call resumable functions. Do note that this is an experimental feature and even the associated headers are in an experimental sub-directory. There is no UI option to turn this compiler option in VS 2015, so you would need to manually add it via the Additional Options box under the C/C++ property page.

To compile the samples in this blog entry, you would need the following includes.

#include <thread>
#include <future>
#include <iostream>
#include <experimental\resumable>
#include <experimental\generator>

Here’s a simple example async method that sums two numbers. To demonstrate the async-nature of the call, I’ve added a sleep of 2 seconds to slow down the operation.

std::future<int> sum_async(int x, int y)
{
  using namespace std::chrono;

  return std::async([x, y] 
  {
    std::this_thread::sleep_for(2s);
    return x + y; 
  });
}

The async function will execute the function asynchronously, typically in a separate thread, and return a future that will hold the result of the call when it’s completed. Now, here’s how this method can be called.

std::future<void> call_sum_async(int x, int y)
{
  std::cout << "** async func - before call" << std::endl;
  auto sum = await sum_async(x, y);
  std::cout << "** async func - after call" << std::endl;
}

The await keyword calls that method asynchronously and waits until the method executes to completion. And then control goes to the next line of code. At least in the version of VC++ 2015 I tested this on, if you use await, the return type needs to be a future.

Now, here’s a calling method that invokes this caller method.

void main_async()
{
  std::cout << "<< main func - before call" << std::endl;
  auto result = call_sum_async(7, 9);
  std::cout << "<< main func - after call" << std::endl;

  {
    using namespace std::chrono;
    std::cout << "<< main func - before sleep" << std::endl;
    std::this_thread::sleep_for(3s);
    std::cout << "<< main func - after sleep" << std::endl;
  }

  result.get();
  std::cout << "<< main func - after get" << std::endl;
}

To show that the async method actually does execute asynchronously, I added a sleep block in the calling method. Here’s what the output looks like.

async_demo_cpp

Notice how the calling method continues to execute. And then once the async method has completed it returns control back to the main thread. The await keyword just makes it so easy to do this. You write your code just as you would write synchronous code, and all the async-ness is hidden from you. Well not really hidden, but handled for you smoothly.

Oh by the way, I am back to blogging after a bit of a hiatus. Expect to see me post far more frequently going forward.