🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
PersistenceMapperWEBAPIClient
This page was created by Hans.karlsen on 2018-12-14. Last edited by Wikiadmin on 2026-07-29.

The PersistenceMapperWEBAPIClient connects a client application to a remote persistence mapper, including MDrivenServer, over Web API transport; use it when you need a Web API alternative to PersistenceMapperWCFClient.

It provides the same remote-persistence role as the former WCF client, but uses Web API communication. This is relevant for applications targeting platforms where WCF is no longer the preferred client communication mechanism.

When to use this client

Use PersistenceMapperWEBAPIClient when your application has a remote persistence layer exposed as an MDriven Web API endpoint.

For example, a client can connect to an MDrivenServer Web API endpoint at https://YourServerMDrivenServer/api/A0_WebApi, authenticate with its MDrivenServer user credentials, and assign the client as the EcoSpace persistence mapper.

The server must expose a compatible MDriven Web API persistence controller. See Documentation:WebApi for server setup and the controller base class.

Switch from WCF to Web API

Replace the WCF client instance and its WCF-specific properties with a PersistenceMapperWEBAPIClient. The endpoint changes from a .svc URL to the Web API endpoint.

WCF client Web API client
PersistenceMapperWCFClient PersistenceMapperWEBAPIClient
https://YourServerMDrivenServer/APP_A0/A0_PMPWCF.svc https://YourServerMDrivenServer/api/A0_WebApi
WCFServerUserName and WCFServerPassword ServerUserName and ServerPassword

Configure the client

  1. Add the assemblies required for the Web API persistence client, including MDriven.Net.Http.dll.
  2. Create a PersistenceMapperWEBAPIClient.
  3. Set Uri to the Web API persistence endpoint.
  4. Set ServerUserName and ServerPassword.
  5. Assign the configured client to the application's persistence mapper.
public MDrivenProject9PMP() : base()
{
    InitializeComponent();

    var pm = new MDriven.WebApi.Client.PersistenceMapperWEBAPIClient();
    pm.Uri = "https://YourServerMDrivenServer/api/A0_WebApi";
    pm.ServerUserName = "SomeMDrivenServerUser";
    pm.ServerPassword = "'''*''''''*'''";

    PersistenceMapper = pm;
}

Use the URL and credentials configured for your server. Do not retain the sample password in deployed code.

Check that client and server use the same model

A client with different model content than the server must not save objects to that server. Check the model checksum early in the application lifecycle, before the user starts work that could be saved.

The Web API endpoint CompareChecksumReturnDiffData accepts the client's checksum. It returns no meaningful content when the checksums match. When it returns checksum data, the client and server differ.

The following example performs the request on a background worker, retries up to three times for no more than one minute, and dispatches UI handling back to the main thread.

public static void CheckIfOkToStartWithChecksum(
    PersistenceMapperWEBAPIClient pmapperWebClient,
    int checksumForThisES,
    IEcoTypeSystem typesystem,
    IAsyncSupportService asy)
{
    var worker = new BackgroundWorker();
    worker.DoWork += (sender, e) =>
        CheckInBackgroundThread(pmapperWebClient, checksumForThisES, typesystem, asy);
    worker.RunWorkerAsync();
}

private static void CheckInBackgroundThread(
    PersistenceMapperWEBAPIClient pmapperWebClient,
    int checksumForThisES,
    IEcoTypeSystem typesystem,
    IAsyncSupportService asy)
{
    int tries = 0;
    DateTime started = DateTime.Now;
    bool completed = false;

    while (!completed && tries < 3 && DateTime.Now - started < TimeSpan.FromMinutes(1))
    {
        try
        {
            tries++;
            string result = pmapperWebClient.UsedHttpClient
                .GetStringAsync("CompareChecksumReturnDiffData?checksum=" + checksumForThisES)
                .Result;

            if (!string.IsNullOrEmpty(result.Replace('\"', ' ').Trim()))
            {
                asy.DispatchTaskToMainThread(() =>
                    HandleUserNotifications(result, typesystem));
            }

            completed = true;
        }
        catch (Exception)
        {
            System.Threading.Thread.Sleep(200);
        }
    }
}

Show the mismatch and stop the application

The example below compares the returned server checksum data with the local type-system checksum data. It shows the first differing area, informs the user that the server should be evolved, and then shuts down the application.

private static void HandleUserNotifications(
    string serverChecksumData,
    IEcoTypeSystem typesystem)
{
    string text = "WARNING - THE MDrivenServer SERVER HAS A DIFFERENT MODEL THAN YOU!!!\r\n\r\n" +
                  "The safe thing to do is to evolve the server...\r\n";

    string checksumData = (typesystem as Eco.UmlRt.Impl.EcoTypeSystem).GetCheckSumData();

    if (serverChecksumData != null && checksumData != null)
    {
        int chunks = serverChecksumData.Length / 200;
        string diffs = "";

        for (int i = 0; i < chunks; i++)
        {
            string serverChunk = serverChecksumData.Substring(i * 200, 200);
            string clientChunk = checksumData.Substring(i * 200, 200);

            if (serverChunk != clientChunk)
            {
                serverChunk = serverChecksumData.Substring(
                    Math.Max(0, (i * 200) - 50), 300);
                clientChunk = checksumData.Substring(
                    Math.Max(0, (i * 200) - 50), 300);

                diffs += i + " SERVER:" + serverChunk + "\r\n";
                diffs += i + " CLIENT:" + clientChunk + "\r\n\r\n";
                break;
            }
        }

        MessageBox.Show(text + diffs);
        Application.Current.Shutdown();
    }
}

This sample is application UI code. Adapt MessageBox.Show and Application.Current.Shutdown to the client technology you use. Ensure that the checksum data is long enough before taking the surrounding 300-character diagnostic substring.

Notify users about scheduled maintenance

MDrivenServer can expose a scheduled closing through the GetScheduledClosing endpoint. Your client can poll this endpoint and notify users before maintenance or a deployment window.

The response used by the example is XML containing time and message elements. The client stores a scheduled closing only when its time is later than the application start time. This prevents a closing notice from an earlier application session from being treated as a new closing.

Retrieve a scheduled closing

Call UpdateScheduledClosing periodically or before starting work that should not continue through a maintenance window. The sample permits only one background request at a time.

private static BackgroundWorker _bw;

public static DateTime? ScheduledShutdown { get; set; }
public static string ScheduledShutdownMessage { get; set; }

public static void UpdateScheduledClosing(
    PersistenceMapperWEBAPIClient pmapperWebClient,
    DateTime appStartTime)
{
    if (_bw != null)
        return;

    _bw = new BackgroundWorker();
    _bw.DoWork += (sender, e) => GetScheduledShutdown(pmapperWebClient, appStartTime);
    _bw.RunWorkerCompleted += (sender, e) => _bw = null;
    _bw.RunWorkerAsync();
}

private static void GetScheduledShutdown(
    PersistenceMapperWEBAPIClient pmapperWebClient,
    DateTime appStartTime)
{
    try
    {
        string result = pmapperWebClient.UsedHttpClient
            .GetStringAsync("GetScheduledClosing")
            .Result;

        if (string.IsNullOrEmpty(result))
            return;

        var document = XDocument.Parse(result.Replace('\"', ' '));
        if (document.Root == null)
            return;

        var time = document.Root.Element(XName.Get("time"));
        var message = document.Root.Element(XName.Get("message"));
        DateTime timeDT;

        if (time != null && DateTime.TryParse(time.Value, out timeDT) && timeDT > appStartTime)
        {
            ScheduledShutdown = timeDT;
            ScheduledShutdownMessage = message == null ? "" : message.Value;
        }
    }
    catch
    {
        // A maintenance notice is optional; a failed check does not stop the client.
    }
}

Act on the notice

When a scheduled closing exists, compare it with the current UTC time. If the closing time has passed during the current application session, stop further work and shut down. Otherwise, show the maintenance message and remaining time.

private void CheckScheduledClosing(out bool avoidFurtherWork)
{
    avoidFurtherWork = false;

    MDrivenServerCheckModelChecksum.UpdateScheduledClosing(
        _ecospace.MainDataAccessWEBAPIClient,
        _AppStartTime);

    if (!MDrivenServerCheckModelChecksum.ScheduledShutdown.HasValue)
        return;

    DateTime scheduledShutdown = MDrivenServerCheckModelChecksum.ScheduledShutdown.Value;

    if (scheduledShutdown < DateTime.UtcNow && scheduledShutdown > _AppStartTime)
    {
        avoidFurtherWork = true;
        Shutdown();
        return;
    }

    TimeSpan remaining = scheduledShutdown - DateTime.UtcNow;
    string info = Math.Round(remaining.TotalMinutes, 2) + " minutes left";

    DisplayAdminMessage(
        "Scheduled Shutdown:" + scheduledShutdown.ToLocalTime().ToLongTimeString() + " " +
        MDrivenServerCheckModelChecksum.ScheduledShutdownMessage + " " + info,
        false);
}

For example, if the server announces a closing at 14:00 UTC with the message Deploying the updated model, a running client can display the local closing time and the remaining minutes. After that time, the application should avoid additional work and close.

Related concepts

A persistence mapper is the component that lets modeled objects be stored and retrieved through a persistence layer. For the broader persistence concepts, see Documentation:Persistence and Documentation:Persistence mappers. For Web API server configuration and the direct WCF-to-Web-API migration example, see Documentation:WebApi.

See also