Balsoft.Hive.D365FO.OData
1.0.0
Prefix Reserved
dotnet add package Balsoft.Hive.D365FO.OData --version 1.0.0
NuGet\Install-Package Balsoft.Hive.D365FO.OData -Version 1.0.0
<PackageReference Include="Balsoft.Hive.D365FO.OData" Version="1.0.0" />
<PackageVersion Include="Balsoft.Hive.D365FO.OData" Version="1.0.0" />
<PackageReference Include="Balsoft.Hive.D365FO.OData" />
paket add Balsoft.Hive.D365FO.OData --version 1.0.0
#r "nuget: Balsoft.Hive.D365FO.OData, 1.0.0"
#:package Balsoft.Hive.D365FO.OData@1.0.0
#addin nuget:?package=Balsoft.Hive.D365FO.OData&version=1.0.0
#tool nuget:?package=Balsoft.Hive.D365FO.OData&version=1.0.0
Balsoft.Hive.D365FO.OData
The reusable core of a typed OData client for Dynamics 365 Finance and Operations. It knows how D365FO wants to be talked to; the entity classes for a specific tenant come from your own generated proxy package.
- Entity-set aware CRUD on any generated
DataServiceContext:First,Query,QueryNoTracking,FetchAllNoTracking,QueryPaged,AddAndCommitAsync,UpdateAndCommitAsync,DeleteAndCommitAsync. The entity set comes from the[EntitySet]attribute the OData code generator emits, never from a string in your code. - Write policy. D365FO rejects request bodies that carry read-only, computed or create-only fields ("update not allowed for field 'Type'"). Declare per entity what may be sent, which fields are create-only, and which key fields the server assigns; the request pipeline trims every POST and PATCH accordingly.
- Authentication with a client secret or a managed identity (
DefaultAzureCredential), cached and refreshed before expiry. Tokens go out as bearer headers on every request. - API Management ready. Subscription key header,
Prefer: return=minimalso the client survives the backend's own@odata.context, optionalEnvironmentheader. - Cross-company queries by default, retry on transient GET failures, per-request timeout.
- Detach on failure. A rejected create, update or delete is detached from the context so one bad entity cannot poison every later commit in the same scope.
- A base class for generated entity services, so a customer package only adds typed
FindByKeymethods.
| Package | Targets | Dependencies |
|---|---|---|
Balsoft.Hive.D365FO.OData |
net8.0, net10.0 |
Microsoft.OData.Client 8, Azure.Identity, Microsoft.Extensions.Http, Microsoft.Extensions.Caching.Memory, Polly 8 |
Install
dotnet add package Balsoft.Hive.D365FO.OData
Register
Resources below is the context class your OData code generator produced from the tenant's
$metadata. It lives in your customer-specific package, not here.
using Balsoft.Hive.D365FO.OData;
using Microsoft.Dynamics.DataEntities;
services.Configure<D365ODataOptions>(configuration.GetSection("D365OData"));
services.Configure<D365AuthenticationOptions>(configuration.GetSection("D365Authentication"));
services.AddD365OData<Resources>(writePolicy: policy => policy
.Entity<Worker>("PersonnelNumber", "FirstName", "LastName", "Gender", "SeniorityDate")
.CreateOnly<Worker>("PartyNumber")
.Entity<EmploymentV2>("PersonnelNumber", "LegalEntityId", "EmploymentId", "EmploymentStartDate", "EmploymentEndDate", "DimensionDisplayValue")
.Entity<WorkerContactV3>("PersonnelNumber", "ElectronicAddressId", "Type", "Description", "Locator", "IsPrimary", "IsMobilePhone", "IsPrivate")
.CreateOnly<WorkerContactV3>("Type")
.ServerAssignedKeys("EmploymentId", "ElectronicAddressId", "AddressLocationId", "PartyNumber"));
{
"D365OData": {
"ServiceUrl": "https://my-apim.azure-api.net/d365fo-odata",
"ApimSubscriptionKey": "<from Key Vault>",
"Environment": "UAT"
},
"D365Authentication": {
"Resource": "https://tenant.sandbox.operations.eu.dynamics.com",
"TenantId": "…",
"ClientId": "…",
"ClientSecret": "<from Key Vault>"
}
}
For a managed identity set "UseManagedIdentity": true (and ManagedIdentityClientId for a
user-assigned one) instead of the client secret. The identity must be registered in D365FO under
System administration > Setup > Azure Active Directory applications like any app registration.
Use
public sealed class WorkerService : D365EntityService<Worker>
{
public WorkerService(ID365ODataClient client, ILogger<WorkerService> logger) : base(client, logger) { }
public Worker? FindByKey(string personnelNumber) => First(x => x.PersonnelNumber == personnelNumber);
}
var worker = workers.FindByKey("229");
if (worker is null)
{
await workers.UpsertAsync(new Worker { PersonnelNumber = "229", FirstName = "Julian", LastName = "Imhäuser" });
}
else
{
worker.LastName = "Imhaeuser";
await workers.UpdateAsync(worker); // PATCH of the tracked instance, body trimmed by the write policy
}
Register the client once per scope. The context tracks entities, and a PATCH needs the tracked
instance that a First or Query returned. Reads that only need data use the NoTracking
variants.
OData actions and anything context-specific are reachable through ID365ODataClient<Resources>:
await client.ExecuteAsync(ctx => ctx.SomeActionImport(...).ExecuteAsync());
What a customer package adds
- the generated entity classes and context (from the tenant's
$metadata, including custom fields and custom entities), - one
D365WritePolicyregistration for the entities it writes, - one
D365EntityService<T>subclass per entity it uses, - anything that needs generated types, such as financial dimension helpers.
That package changes whenever an entity or a custom field is added; this one does not.
Building
dotnet build
dotnet test
Releases are author-signed with the Balsoft GmbH certificate, whose key is held in Google Cloud
KMS, and published to nuget.org by the release workflow with trusted publishing. Two equivalent
release scripts exist: eng/release.ps1 for a Windows host with the KMS certificate provider,
and eng/release.sh for macOS or Linux, which signs through eng/kms-nuget-sign (a small tool
that calls KMS directly). Verify a package with dotnet nuget verify --all <package>.nupkg
(see SECURITY.md).
License
Apache-2.0. Copyright Balsoft GmbH.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Azure.Identity (>= 1.21.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.1)
- Microsoft.Extensions.Http (>= 10.0.1)
- Microsoft.OData.Client (>= 8.4.3)
- Polly (>= 8.6.5)
-
net8.0
- Azure.Identity (>= 1.21.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.1)
- Microsoft.Extensions.Http (>= 10.0.1)
- Microsoft.OData.Client (>= 8.4.3)
- Polly (>= 8.6.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 113 | 9/24/2026 |