In this video I show how to consume OData from Microsoft Dynamics 365 Business Central with .NET and C#. Using the OData Connected Service.

In this video, Erik demonstrates a cleaner, more structured approach to consuming Business Central OData feeds from .NET and C# — using the OData Connected Service extension for Visual Studio instead of raw HTTP calls. Rather than manually parsing JSON responses, this method auto-generates strongly-typed proxy classes that make your BC data feel like native C# objects.
The Problem with Raw HTTP
Business Central exposes OData feeds over HTTPS, and you can easily view the data as JSON right in your browser. If you need to consume that data programmatically, you could use HttpClient, WebClient, or HttpWebRequest to grab the raw JSON. But there’s a better way — one that gives you IntelliSense, strong typing, and auto-generated plumbing code.
Setting Up the Project
Erik starts with a brand new .NET 5.0 console application in Visual Studio — just the default “Hello World” template. The project file shows the key NuGet packages needed:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.OData.Client" Version="7.9.0" />
<PackageReference Include="Microsoft.OData.Core" Version="7.9.0" />
<PackageReference Include="Microsoft.OData.Edm" Version="7.9.0" />
<PackageReference Include="Microsoft.Spatial" Version="7.9.0" />
<PackageReference Include="System.Text.Json" Version="5.0.2" />
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Connected Services" />
</ItemGroup>
</Project>
Installing the OData Connected Service Extension
Just as you can extend Business Central with AL extensions, you can extend Visual Studio with its own extensions. Go to Manage Extensions in Visual Studio and search for “OData Connected Service” on the marketplace. Install it (requires a Visual Studio restart), and you’ll have a new option available under your project’s dependencies.
Generating the Proxy Classes
Once the extension is installed, right-click on Dependencies in your project and select Add Connected Service. You’ll see the OData Connected Service listed as an option. Here’s where it gets interesting — and where a few gotchas come up.
Finding the Right URL
The connected service wizard needs the metadata endpoint, not a specific entity URL. If your Business Central OData URL looks like this:
http://bc18:7048/BC/ODataV4/Company('Hougaard')/Chart_of_Accounts
You need to strip it down to the service root and append $metadata:
http://bc18:7048/BC/ODataV4/$metadata
This returns an XML document describing all exposed endpoints, their fields, available functions, and actions — everything the tool needs to generate your proxy classes.
Handling Authentication in the Wizard
The wizard doesn’t have built-in username/password fields — there are simply too many authentication methods to cover in a single UI. Instead, it lets you add custom HTTP headers. For basic authentication, add a header like:
Authorization: Basic ZGVtbzpEZW1vMjAwNCE=
The value after “Basic” is your username:password Base64-encoded (in this case, demo:Demo2004!).
Configuring the Generation
After authenticating, the wizard shows all discovered services (85 in Erik’s case), functions, and actions. You can select all of them. A few important settings:
- File name: Give the generated file a meaningful name (e.g., “bc18”)
- Namespace: By default, the generated code uses the namespace “NAV” with a container also called “NAV” — which is confusing. Erik overrides this to use “BC” as the namespace to avoid
NAV.NAVreferences.
Click Finish, and the tool generates a complete C# proxy file with strongly-typed classes for every exposed entity.
Writing the Code
With the proxy generated, consuming OData becomes remarkably clean. Here’s the final program:
using System;
namespace ODataFromCSharp
{
class Program
{
static void Main(string[] args)
{
var serviceRoot = "http://bc18:7048/BC/api/v2.0/companies(d00aced7-6ec1-eb11-b388-da215ec232af)/";
var context = new BCAPI.NAV(new Uri(serviceRoot));
context.BuildingRequest += Context_BuildingRequest;
var data = context.Vendors.Execute();
foreach (var vendor in data)
Console.WriteLine("{0} {1}", vendor.Number, vendor.DisplayName);
var serviceRoot2 = "http://bc18:7048/BC/ODataV4/Company('Hougaard')/";
var context2 = new BC18.NAV(new Uri(serviceRoot2));
context2.BuildingRequest += Context_BuildingRequest;
var data2 = context2.Chart_of_Accounts.AddQueryOption("$filter", "Net_Change gt 0");
foreach (var account in data2)
Console.WriteLine("{0} {1} {2}", account.No, account.Name, account.Net_Change);
}
private static void Context_BuildingRequest(object sender, Microsoft.OData.Client.BuildingRequestEventArgs e)
{
e.Headers.Add("Authorization", "Basic ZGVtbzpEZW1vMjAwNCE=");
}
}
}
Step-by-Step Breakdown
- Define the service root URL — This is the base OData endpoint, including the company identifier.
- Create the context — The auto-generated
NAVclass (or whatever you named it) acts as your connection object. You instantiate it with aUripointing to the service root. - Subscribe to the BuildingRequest event — This is a C# event on the context that fires before every request. It’s the perfect place to inject authentication headers.
- Execute queries — Call
.Execute()on an entity set likeChart_of_AccountsorVendors, and iterate over the results with a simpleforeach.
Handling the Company Name
One gotcha Erik encountered: Business Central may return an error saying “default company cannot be found.” You need to specify the company name in your URL. The cleanest approach shown in the video is to include it directly in the service root URL:
var serviceRoot = "http://bc18:7048/BC/ODataV4/Company('Hougaard')/";
Alternatively, you can inject it in the BuildingRequest event by modifying the request URI — replacing part of the URL string to include the company path segment. It’s a bit of a hack, but it works and only needs to be done once:
e.RequestUri = new Uri(e.RequestUri.ToString().Replace("ODataV4/", "ODataV4/Company('Hougaard')/"));
Adding Query Options
The proxy also supports OData query options. Notice how the Chart of Accounts query adds a filter:
var data2 = context2.Chart_of_Accounts.AddQueryOption("$filter", "Net_Change gt 0");
This translates to the standard OData $filter query parameter, so you only get accounts with a positive net change.
Why This Approach is Better
The beauty of this approach is in the developer experience. Once you’ve done the plumbing (authentication, company name injection), the actual business logic is just a few lines of clean, readable code:
var data = context.Chart_of_Accounts.Execute();
foreach (var account in data)
Console.WriteLine("{0} {1} {2}", account.No, account.Name, account.Net_Change);
You get full IntelliSense on property names like account.No, account.Name, and account.Net_Change. No funky JSON parsing, no manual deserialization, no guessing at field names. The proxy classes give you a strongly-typed, discoverable API that feels native to C#.
Summary
To consume Business Central OData from C# the “better way”:
- Install the OData Connected Service extension in Visual Studio
- Point it at your BC OData
$metadataendpoint with proper authentication headers - Generate the proxy classes (give them a sensible namespace)
- Create a context using the generated container class and your service root URI
- Subscribe to BuildingRequest to handle authentication and company name injection
- Query your data with strongly-typed, readable C# code
From here, the possibilities extend to updating data, calling bound and unbound functions/actions, and implementing OAuth authentication — all building on this same foundation.