Table of Contents

OhData.Client

A typed .NET client for OData 4.0 services. Provides a fluent, LINQ-style API for querying and mutating entity sets - no code generation required.

This guide is split across several pages:

  • Overview & setup (this page) — installation, constructing the client, IHttpClientFactory, entity set name resolution, and client options.
  • Querying$filter, $select, $expand, $orderby, $top/$skip, IncludeCount.
  • Terminal operationsToListAsync, ToPageAsync, FirstOrDefaultAsync, CountAsync, AnyAsync, and the rest.
  • Single-entity operations — get, insert, replace, partial update, delete, ETags, and conditional GET.
  • Error handling & literal typesODataClientException and the CLR-to-OData literal mapping.

Installation

dotnet add package EnGen.OhData.Client

Setup

// Create directly (owns the HttpClient - dispose when done)
var client = new OhDataClient("https://api.example.com/odata");

// Or wrap a caller-supplied HttpClient (recommended for IHttpClientFactory)
var client = new OhDataClient(httpClient);

// With custom JSON options — e.g. opt into camelCase for a camelCase-configured server
// (the default is PascalCase, matching the OhData server's default $metadata/responses)
var client = new OhDataClient(httpClient, new OhDataClientOptions
{
    JsonOptions = new JsonSerializerOptions
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        // ...
    }
});

With IHttpClientFactory (ASP.NET Core)

// Program.cs
builder.Services.AddHttpClient("products", c =>
    c.BaseAddress = new Uri("https://api.example.com/odata/"));

// In a service:
public class ProductService(IHttpClientFactory factory)
{
    private readonly OhDataClient _client = new(factory.CreateClient("products"));
}

Entity set name resolution

The client resolves the entity set name automatically via:

  1. [ODataEntitySet("CustomName")] attribute on the model class, or
  2. Simple pluralisation of the class name (ProductProducts, CategoryCategories)
[ODataEntitySet("MyCategories")]  // overrides pluralisation
public class Category { ... }

Pass an explicit name to For<T> if needed:

client.For<Category>("MyCategories")

Client options

OhDataClientOptions is passed to the OhDataClient constructor to customise behaviour.

JsonOptions

Applied to both request serialization and response deserialization. Defaults: PascalCase naming (PropertyNamingPolicy = null, matching the OhData server's PascalCase default), case-insensitive reads, null values omitted on write. Set PropertyNamingPolicy = JsonNamingPolicy.CamelCase to target a camelCase server.

var client = new OhDataClient(httpClient, new OhDataClientOptions
{
    JsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web)
    {
        Converters = { new JsonStringEnumConverter() }
    }
});

NotFoundBehavior

Controls how 404 Not Found responses are handled for single-entity GET operations (GetAsync, GetWithETagAsync). Default is NotFoundBehavior.ReturnNull.

Value Behaviour
ReturnNull (default) Returns null when the entity is not found
Throw Throws ODataClientException with status 404
var client = new OhDataClient(httpClient, new OhDataClientOptions
{
    NotFoundBehavior = NotFoundBehavior.Throw
});

Next: Querying →