Skip to main content
All docs
V22.1

Transfer Data via REST API

  • 7 minutes to read

You can use a REST API to transfer data over the web. A REST API service is directly connected to a database and exchanges data records with client applications.

To transfer data over the web, use the following XPO components:

On the backend side, the web API controller must accept POST requests to the following actions:

  • /updateschema?doNotCreateIfFirstTableNotExist={true|false}
  • /selectdata
  • /modifydata

Tip

You can download a complete example here:

Create a Web API Controller (ASP.NET Core Web API)

The steps below describe how to add an XPO web API controller to an ASP.NET Core Web API application.

  1. Register WebApiDataStoreService in a service collection on startup. Add XML Serializer formatters to MVC services.

    // Startup.cs
    using DevExpress.Xpo;
    using DevExpress.Xpo.DB;
    // ...
    public void ConfigureServices(IServiceCollection services) {
        // ...
    
        // configure WebApiDataStoreService
        string connectionString = Configuration.GetConnectionString("MyConnectionString");
        IDataStore dataStore = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.SchemaAlreadyExists);            
        services.AddSingleton(new WebApiDataStoreService(dataStore));
    
        // add XML Serializer formatters
        services.AddMvc()
            .AddXmlSerializerFormatters();
    
        // ...
    }
    

    Tip

    More information: Dependency Injection.

  2. Add a controller with three POST methods: UpdateSchema, SelectData, and ModifyData.

    using System.Threading.Tasks;
    
    using DevExpress.Xpo.DB;
    
    using Microsoft.AspNetCore.Mvc;
    // ...
    // In this example, the controller URI is `[Your Service Address]/xpo`. 
    [ApiController]
    [Route("[controller]/[action]")]
    public class XpoController : ControllerBase {
        readonly WebApiDataStoreService DataStoreService;
        public XpoController(WebApiDataStoreService dataStoreService) {
            this.DataStoreService = dataStoreService;
        }
            [HttpPost]
            public Task<OperationResult<UpdateSchemaResult>> UpdateSchema([FromQuery] bool doNotCreateIfFirstTableNotExist, [FromBody] DBTable[] tables) {
                return DataStoreService.UpdateSchemaAsync(doNotCreateIfFirstTableNotExist, tables);
            }
            [HttpPost]
            public Task<OperationResult<SelectedData>> SelectData([FromBody] SelectStatement[] selects) {
                return DataStoreService.SelectDataAsync(selects);
            }
            [HttpPost]
            public Task<OperationResult<ModificationResult>> ModifyData([FromBody] ModificationStatement[] dmlStatements) {
                return DataStoreService.ModifyDataAsync(dmlStatements);
            }
    }
    

Support Cached Data Store

On the client side, create a WebApiDataStoreClient object and pass it as a parameter to the DataCacheNode constructor. Use this DataCacheNode instance as IDataStore. For example, pass it as a parameter to the SimpleDataLayer or ThreadSafeDataLayer constructor.

ICacheToCacheCommunicationCore cacheRoot = new WebApiDataStoreClient(httpClient, AutoCreateOption.SchemaAlreadyExists);
IDataStore dataStore = new DataCacheNode(cacheRoot);

To configure the Data Cache Root on the server side:

  1. Wrap an IDataStore instance in a DataCacheRoot object.

    // Startup.cs
    using DevExpress.Xpo;
    using DevExpress.Xpo.DB;
    // ...
    public void ConfigureServices(IServiceCollection services) {
        // ...
    
        // configure a WebApiDataStoreService
        string connectionString = Configuration.GetConnectionString("MyConnectionString");
        IDataStore dataStore = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.SchemaAlreadyExists);            
        dataStore = new DataCacheRoot(dataStore);
        services.AddSingleton(new WebApiDataStoreService(dataStore));
    
        // add XML Serializer formatters
        services.AddMvc()
            .AddXmlSerializerFormatters();
    
        // ...
    }
    
  2. Add the following POST methods to the controller:

    • ModifyDataWithCookie
    • NotifyDirtyTables
    • ProcessCookie
    • SelectDataWithCookie
    • UpdateSchemaWithCookie
    using System.Threading.Tasks;
    
    using DevExpress.Xpo.DB;
    
    using Microsoft.AspNetCore.Mvc;
    // ...
    // In this example, the controller URI is `[Your Service Address]/xpo`. 
    [ApiController]
    [Route("[controller]/[action]")]
    public class XpoController : ControllerBase {
        readonly WebApiDataStoreService DataStoreService;
        public XpoController(WebApiDataStoreService dataStoreService) {
            this.DataStoreService = dataStoreService;
        }
    
        // ...
    
        [HttpPost]
        public OperationResult<DataCacheModificationResult> ModifyDataWithCookie([FromBody] WebApiDataContainer<ModificationStatement[]> data) {
            return DataStoreService.ModifyDataWithCookie(data);
        }
        [HttpPost]
        public OperationResult<DataCacheResult> NotifyDirtyTables([FromBody] WebApiDataContainer<string[]> data) {
            return DataStoreService.NotifyDirtyTables(data);
        }
        [HttpPost]
        public OperationResult<DataCacheResult> ProcessCookie([FromBody] DataCacheCookie data) {
            return DataStoreService.ProcessCookie(data);
        }
        [HttpPost]
        public OperationResult<DataCacheSelectDataResult> SelectDataWithCookie([FromBody] WebApiDataContainer<SelectStatement[]> data) {
            return DataStoreService.SelectDataWithCookie(data);
        }
        [HttpPost]
        public OperationResult<DataCacheUpdateSchemaResult> UpdateSchemaWithCookie([FromQuery] bool doNotCreateIfFirstTableNotExist, [FromBody] WebApiDataContainer<DBTable[]> data) {
            return DataStoreService.UpdateSchemaWithCookie(doNotCreateIfFirstTableNotExist, data);
        }        
    }
    

Create a Web API Controller (ASP.NET MVC Web API)

The steps below describe how to add an XPO web API controller to an ASP.NET MVC Web API application.

  1. Create WebApiDataStoreService in the Global.asax file and share it via a static field.

    // Startup.cs
    // ...
    using DevExpress.Xpo;
    using DevExpress.Xpo.DB;
    
    namespace DxSample.Server {
        public class WebApiApplication : System.Web.HttpApplication {
            static WebApiDataStoreService fDataStoreService;
    
            public static WebApiDataStoreService DataStoreService => fDataStoreService;
    
            protected void Application_Start() {
                // ...
                string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
                connectionString = XpoDefault.GetConnectionPoolString(connectionString);
                IDataStore dataStore = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.DatabaseAndSchema);
                fDataStoreService = new WebApiDataStoreService(dataStore);
            }
        }
    }
    

    Tip

    More information: Dependency Injection.

  2. Set an XML Serializer for all required types in the App_Start\WebApiConfig.cs file.

    // ...
    using DevExpress.Xpo.DB;
    
    namespace DxSample.Server {
        public static class WebApiConfig {
            public static void Register(HttpConfiguration config) {
                // ...
                // Web API routes
                config.MapHttpAttributeRoutes();
                // NEW CODE
                config.Formatters.XmlFormatter.SetSerializer<OperationResult<UpdateSchemaResult>>(new XmlSerializer(typeof(OperationResult<UpdateSchemaResult>)));
                config.Formatters.XmlFormatter.SetSerializer<OperationResult<SelectedData>>(new XmlSerializer(typeof(OperationResult<SelectedData>)));
                config.Formatters.XmlFormatter.SetSerializer<OperationResult<ModificationResult>>(new XmlSerializer(typeof(OperationResult<ModificationResult>)));
                config.Formatters.XmlFormatter.SetSerializer<DBTable[]>(new XmlSerializer(typeof(DBTable[])));
                config.Formatters.XmlFormatter.SetSerializer<SelectStatement[]>(new XmlSerializer(typeof(SelectStatement[])));
                config.Formatters.XmlFormatter.SetSerializer<ModificationStatement[]>(new XmlSerializer(typeof(ModificationStatement[])));
                // ...
            }
        }
    }
    
  3. Add a controller with three POST methods: UpdateSchema, SelectData, and ModifyData.

    using DevExpress.Xpo.DB;
    
    using System.Threading.Tasks;
    using System.Web.Http;
    
    namespace DxSample.Server.Controllers {
        [RoutePrefix("xpo")]
        public class XpoController : ApiController {
            [HttpPost]
            [Route("UpdateSchema")]
            public Task<OperationResult<UpdateSchemaResult>> UpdateSchema(bool doNotCreateIfFirstTableNotExist, [FromBody] DBTable[] tables) {
                return WebApiApplication.DataStoreService.UpdateSchemaAsync(doNotCreateIfFirstTableNotExist, tables);
            }
            [HttpPost]
            [Route("SelectData")]
            public Task<OperationResult<SelectedData>> SelectData([FromBody] SelectStatement[] selects) {
                return WebApiApplication.DataStoreService.SelectDataAsync(selects);
            }
            [HttpPost]
            [Route("ModifyData")]
            public Task<OperationResult<ModificationResult>> ModifyData([FromBody] ModificationStatement[] dmlStatements) {
                return WebApiApplication.DataStoreService.ModifyDataAsync(dmlStatements);
            }
        }
    }
    

Connect a Client Application to a Web API Service

You can connect a client application to a web service in any of the following ways:

Note

Since WebApiDataStoreClient uses the HttpClient component, HttpClient.BaseAddress should point to a controller route. Note that HttpClient merges BaseAddress with a relative URI according to section [5.2.3. Merge Paths] of RFC 3986. This means that HttpClient excludes the last segment of the BaseAddress. To prevent this, add a slash / to the end of the BaseAddress.

This URI is correct: https://example.com/xpo/

This URI is not correct: https://example.com/xpo

Configure HttpClient options

To use the advanced HttpClient features (for example, the automatic HTTP content response decompression), connect the application to a Web API service using Option 2. Note that users will lose the ability to dynamically set the data store type using the XpoProvider connection string parameter.

using System;
using System.Net;
using System.Net.Http;
using DevExpress.Xpo.DB;
using DevExpress.Xpo.DB.Helpers;
// ...
var handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.Brotli };
var httpClient = new HttpClient(handler, true);
httpClient.BaseAddress = new Uri("https://localhost:5001/xpo/");
httpClient.DefaultRequestHeaders.Add("Accept-Encoding", "br");
IDataStore dataStore = new WebApiDataStoreClient(httpClient, autoCreateOption);

To allow users to specify the data store type with the XpoProvider parameter, register a new provider name associated with a method that creates a provider instance.

using System;
using System.Net;
using System.Net.Http;
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Xpo.DB.Helpers;
// ...
DataStoreBase.RegisterDataStoreProvider("WebApiWithBrotli", CreateDataStoreFromString);
string connectionString = @"XpoProvider=WebApiWithBrotli;uri=https://localhost:5001/xpo/";
IDataStore dataStore = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.DatabaseAndSchema);
// ...
static IDataStore CreateDataStoreFromString(string connectionString, AutoCreateOption autoCreateOption, out IDisposable[] toDispose) {
    var handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.Brotli };
    var httpClient = new HttpClient(handler, true);
    var parser = new ConnectionStringParser(connectionString);
    httpClient.BaseAddress = new Uri(parser.GetPartByName("uri"));
    httpClient.DefaultRequestHeaders.Add("Accept-Encoding", "br");
    toDispose = new IDisposable[] { httpClient };
    return new WebApiDataStoreClient(httpClient, autoCreateOption);
}

Connect a Xamarin App

You can connect a Xamarin app to an XPO in the same way as described in the previous section, but you need to follow extra steps to run the application in simulators. For detailed information, refer to the following help topic: Connect to local web services from iOS simulators and Android emulators.

An Android emulator uses its own network and cannot connect to a web service running on IIS Express, because IIS Express does not allow remote access. The solutions are as follows:

  1. For the ASP.NET Core Web API application: run a web service project using the “Project” profile instead of “IIS Express”.

  2. For the ASP.NET Web API application: use an open-source iisexpress-proxy tool.

Transfer Data via REST API using JSON Format

If you need a Web API service that returns XPO objects in the JSON format, manually implement the required controllers and the client API. The following examples illustrate how to implement such a service:

A 1-Click Solution for Role-based Access Control with Web API Services via XPO (FREE)

XAF’s Solution Wizard scaffolds a Web API Service with integrated authorization & CRUD operations powered by XPO ORM library. You can use OAuth2, JWT or custom strategies for authentication alongside tools like Postman or Swagger for API testing. The built-in security system also filters out secured server data based on permissions granted to users. These basic functions are available free-of-charge: https://www.devexpress.com/security-api-free.