Connecting to Dataverse: Modern .NET Authentication with ServiceClient

In the previous post, we mapped the authentication landscape. Now we move from architecture to implementation. This post shows you how to connect to Dataverse using modern .NET patterns with the recommended client:

Microsoft.PowerPlatform.Dataverse.Client.ServiceClient

If you’re building:

  • Console apps
  • Migration utilities
  • Admin tools
  • DevOps automation
  • Internal utilities
  • XrmToolBox-style plugins

This is your starting point.


Why ServiceClient?

Microsoft’s recommended .NET SDK for Dataverse is provided via:

Microsoft.PowerPlatform.Dataverse.Client

At its core:


Step 1: App Registration (Entra ID)

Before writing code, you must register an application in Microsoft Entra ID.

Required configuration

  1. Create an App Registration.
  2. Add a redirect URI:
    • http://localhost (for console tools)
  3. Under API permissions:
    • Add delegated permission to Dataverse:
      user_impersonation
  4. Grant admin consent (if required).

Keep note of:

  • Application (Client) ID
  • Tenant ID
  • Redirect URI

Step 2: Install NuGet Package

dotnet add package Microsoft.PowerPlatform.Dataverse.Client

Step 3: Basic OAuth Delegated Connection

This is the most common developer scenario — interactive login.

using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;

class Program
{
    static void Main()
    {
        string connectionString =
            "AuthType=OAuth;" +
            "Url=https://yourorg.crm.dynamics.com/;" +
            "ClientId=YOUR-APP-ID;" +
            "RedirectUri=http://localhost;" +
            "LoginPrompt=Auto;";

        using var service = new ServiceClient(connectionString);

        if (!service.IsReady)
        {
            Console.WriteLine(service.LastError);
            return;
        }

        Console.WriteLine("Connected successfully.");

        QueryExpression query = new QueryExpression("account")
        {
            ColumnSet = new ColumnSet("accountid", "name"),
            TopCount = 5
        };

        var results = service.RetrieveMultiple(query);

        foreach (var entity in results.Entities)
        {
            Console.WriteLine($"{entity.Id} - {entity.GetAttributeValue<string>("name")}");
        }
    }
}

What’s Actually Happening Behind the Scenes?

When this runs:

  1. ServiceClient detects AuthType=OAuth.
  2. It launches OAuth flow.
  3. User signs in.
  4. Access token is issued for Dataverse.
  5. Token is cached.
  6. Token refresh is handled automatically.
  7. SDK operations call Web API endpoints internally.

Important:

You are not using SOAP.
You are not manually managing tokens.
You are not crafting HTTP headers.

The SDK wraps all of that.


Understanding the Connection String

Let’s break down what matters:

AuthType=OAuth;
Url=https://yourorg.crm.dynamics.com/;
ClientId=YOUR-APP-ID;
RedirectUri=http://localhost;
LoginPrompt=Auto;

Step 4: Creating Records

Let’s move beyond read operations.

Entity account = new Entity("account");
account["name"] = "ServiceClient Test Account";

Guid accountId = service.Create(account);

Console.WriteLine($"Created account: {accountId}");

Dataverse security is enforced exactly as if the user performed this inside the UI.


Step 5: Updating Records

Entity updateAccount = new Entity("account", accountId);
updateAccount["name"] = "Updated via ServiceClient";

service.Update(updateAccount);

Step 6: Deleting Records

service.Delete("account", accountId);

Token Lifecycle (What You Don’t See)

ServiceClient handles:

  • Access token acquisition
  • Refresh token storage
  • Expiry detection
  • Silent renewal
  • Thread-safe reuse

This is why you should avoid manually calling Web API unless necessary.


Using Client Secret Instead (Non-Interactive)

If you want to eliminate login prompts, use Client Secret authentication. This becomes Server-to-Server, which we’ll fully cover in Part 3.

Example:

string connectionString =
"AuthType=ClientSecret;" +
"Url=https://yourorg.crm.dynamics.com/;" +
"ClientId=YOUR-APP-ID;" +
"ClientSecret=YOUR-SECRET;";

This requires:

  • Application permissions
  • Application User created in Dataverse
  • Security roles assigned

No login prompt. No user context.

Leave a comment