Integrating Dynamics 365 with a .NET MAUI application brings enterprise-grade CRM capabilities directly into your cross-platform app. Whether it is tracking sales, managing customer service, or automating marketing workflows, you can easily work with Dynamics 365 data in .NET MAUI. Besides, using dotConnect for Dynamics 365 simplifies the process and unlocks powerful, real-time interactions with your business data.
This tutorial will walk you through the steps to connect a .NET MAUI application to Dynamics 365 with the help of dotConnect for Dynamics 365.
dotConnect for Dynamics 365 enables direct access to Dynamics 365 data via standard ADO.NET and Entity Framework Core interfaces. Key benefits of this approach include:
What you will need for this tutorial:
Download and install dotConnect for Dynamics 365 directly on your machine, or install the Devart.Data.Dynamics NuGet package. No license key is required, and you can start exploring the product immediately.
After purchasing the full version, go to your profile's Licenses page. Choose your product and click Details. Here, you'll find the license details and the Activation Key.

To activate a connection in your application, add the License Key to your connection string.
Or, you can run the following command in the terminal:
dotnet add package Devart.Data.Dynamics
To connect to Dynamics 365 using the built-in Data Explorer, right-click Data connections and choose Add connection.

Select Dynamics 365 as the data source, choose Web Login to get credentials, and click Test Connection.

If the test connection is successful, click OK.
Once connected, you can browse tables, execute queries, and manage data directly within the Data Explorer.

Here is the sample code that will help you connect to Dynamics 365. Complete it with the following credentials: Server, User ID, Password, and License key.
Create a new class named AppConfig to store your connection string and other configuration settings.
namespace DynamicsMAUI {
public static class AppConfig {
public static string ConnectionString {
get;
} =
"Server=******;User ID=******;Password=******;License Key=**********;";
}
}
Now, let us illustrate the READ, INSERT, and DELETE operations.
In the example below, you can see how to retrieve and display data from Dynamics 365.
Have a look at the code fragment from the MainPage.xaml.cs file. Update the file as shown below to enable it to display the connection status and use the new SQL query.
using System;
using System.Collections.Generic;
using Devart.Data.Dynamics;
using Microsoft.Maui.Controls;
namespace DynamicsMAUI {
public partial class MainPage: ContentPage {
public MainPage() {
InitializeComponent();
CheckConnectionAndFetchData();
}
private async void CheckConnectionAndFetchData() {
try {
using(var connection = new DynamicsConnection(AppConfig.ConnectionString)) {
await connection.OpenAsync();
ConnectionStatusLabel.Text = "Connection Status: Connected";
ConnectionStatusLabel.TextColor = Colors.Green;
string query = "SELECT roleid, name, businessunitid, overriddencreatedon FROM Role LIMIT 10";
using(var command = new DynamicsCommand(query, connection)) {
using(var reader = await command.ExecuteReaderAsync()) {
var roles = new List < Role > ();
while (await reader.ReadAsync()) {
roles.Add(new Role {
RoleId = reader["roleid"].ToString(),
Name = reader["name"].ToString(),
BusinessUnitId = reader["businessunitid"].ToString(),
OverriddenCreatedOn = reader["overriddencreatedon"].ToString()
});
}
RolesListView.ItemsSource = roles;
}
}
}
} catch (Exception ex) {
ConnectionStatusLabel.Text = $"Connection Status: Error - {ex.Message}";
ConnectionStatusLabel.TextColor = Colors.Red;
await DisplayAlert("Error", ex.Message, "OK");
}
}
}
public class Role {
public string RoleId {
get;
set;
}
public string Name {
get;
set;
}
public string BusinessUnitId {
get;
set;
}
public string OverriddenCreatedOn {
get;
set;
}
}
The MainPage.xaml file should be updated as shown in the example to include a Label for displaying the connection status.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DynamicsMAUI.MainPage">
<StackLayout>
<Label x:Name="ConnectionStatusLabel" HorizontalOptions="Center" VerticalOptions="Center" FontSize="Medium" Margin="0,10,0,10"/>
<ListView x:Name="RolesListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="{Binding Name}" FontAttributes="Bold"/>
<Label Grid.Column="1" Text="{Binding BusinessUnitId}"/>
<Label Grid.Column="2" Text="{Binding OverriddenCreatedOn}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
*, meaning they share the available space equally.
Let us see how to insert new data into Dynamics 365 from the application.
At this stage, we insert a predefined set of test data on clicking the Submit button.
private async void OnSubmitRole(object sender, EventArgs e) {
try {
using(var connection = new DynamicsConnection(AppConfig.ConnectionString)) {
await connection.OpenAsync();
string query = "INSERT INTO role (name, businessunitid, overriddencreatedon, importsequencenumber, isinherited, isautoassigned, description, summaryofcoretablepermissions, appliesto) " +
"VALUES (:name, :businessunitid, NULL, NULL, :isinherited, :isautoassigned, :description, :summaryofcoretablepermissions, :appliesto)";
using(var command = new DynamicsCommand(query, connection)) {
// Predefined test data
command.Parameters.AddWithValue("name", "13");
command.Parameters.AddWithValue("businessunitid", "{You business unit ID}");
command.Parameters.AddWithValue("isinherited", "Only the rights of the working group");
command.Parameters.AddWithValue("isautoassigned", "No");
command.Parameters.AddWithValue("description", "1");
command.Parameters.AddWithValue("summaryofcoretablepermissions", "1");
command.Parameters.AddWithValue("appliesto", "1");
int rowsAffected = await command.ExecuteNonQueryAsync();
await DisplayAlert("Success", $"Role added successfully! Rows affected: {rowsAffected}", "OK");
}
}
} catch (Exception ex) {
await DisplayAlert("Error", ex.Message, "OK");
}
}
The fields in the SQL INSERT statement follow the standard structure of a Dynamics 365 Role entity. Each field plays a specific role in defining the entity's purpose and function within the system.
Add a button to your MainPage.xaml to trigger the insertion of a new role:
<Button Text="Add New Role" Clicked="OnSubmitRole" Margin="0,10,0,0"/>
Let's test it.

Click Add New Role.

To add a method for removing a record and include a red Delete button in each row of the ListView, do the following:
Let us add a method to handle the deletion of a role:
private async void OnDeleteRole(object sender, EventArgs e) {
var button = (Button) sender;
var role = (Role) button.BindingContext;
try {
using(var connection = new DynamicsConnection(AppConfig.ConnectionString)) {
await connection.OpenAsync();
string query = "DELETE FROM role WHERE roleid = :roleid";
using(var command = new DynamicsCommand(query, connection)) {
command.Parameters.AddWithValue("roleid", role.RoleId);
int rowsAffected = await command.ExecuteNonQueryAsync();
await DisplayAlert("Success", $"Role '{role.Name}' removed successfully! Rows affected: {rowsAffected}", "OK");
}
}
} catch (Exception ex) {
if (ex.Message.Contains("iscomponentdeletionenabled")) {
await DisplayAlert("Error", "Deletion is not enabled for this component. Please check the managed properties.", "OK");
} else {
await DisplayAlert("Error", ex.Message, "OK");
}
}
}
The OnDeleteRole method retrieves the RoleID from the BindingContext and executes a SQL query to delete the specified role.
Add a method to handle the deletion of a role:
<ListView x:Name="RolesListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="{Binding Name}" FontAttributes="Bold"/>
<Label Grid.Column="1" Text="{Binding BusinessUnitId}"/>
<Label Grid.Column="2" Text="{Binding OverriddenCreatedOn}"/>
<Button Grid.Column="3" Text="Delete" Clicked="OnDeleteRole" BindingContext="{Binding .}" BackgroundColor="Red"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
It adds a button control to the grid within the DataTemplate for each record. The clicked event gets bound to the OnDeleteRole method. The BindingContext of the button is set to the current item in the ListView, enabling the OnDeleteRole method to access the corresponding role's details.
Let's run our application.

We can delete the role successfully.

We have successfully connected a .NET MAUI application to Dynamics 365 with the help of dotConnect for Dynamics 365. This integration allows us to access the data directly and perform CRUD operations for enterprise CRMs directly from the application. You can try it yourself with a fully functional 30-day free trial and see how it can improve your work.