From 31710ff40b90b57f54ba2c942664def1bcbc5c44 Mon Sep 17 00:00:00 2001 From: "nikulina.svetlana" Date: Tue, 9 Sep 2025 13:29:43 +0400 Subject: [PATCH 1/2] fix --- .../Controllers/ActionsController.cs | 13 ++- .../Controllers/ContractController.cs | 6 +- .../Controllers/CustomerController.cs | 6 +- .../Controllers/DocumentsController.cs | 5 +- .../Controllers/OrderController.cs | 9 ++- .../Controllers/OrderDetailController.cs | 6 +- .../Controllers/ProductController.cs | 6 +- CS/ODataService/Helpers/ApiHelper.cs | 2 +- CS/ODataService/Helpers/UriHelper.cs | 5 -- CS/ODataService/Models/SingletonEdmModel.cs | 4 +- CS/ODataService/ODataService.csproj | 8 +- CS/ODataService/Startup.cs | 33 ++++---- CS/Tests/ODataTestsBase.cs | 2 +- CS/Tests/Tests.csproj | 2 +- README.md | 80 ++++++++++--------- 15 files changed, 101 insertions(+), 86 deletions(-) diff --git a/CS/ODataService/Controllers/ActionsController.cs b/CS/ODataService/Controllers/ActionsController.cs index e29b6a9..d54cf39 100644 --- a/CS/ODataService/Controllers/ActionsController.cs +++ b/CS/ODataService/Controllers/ActionsController.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Linq; using DevExpress.Xpo; -using Microsoft.AspNet.OData; -using Microsoft.AspNet.OData.Routing; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Routing.Controllers; +using Microsoft.AspNetCore.OData.Routing.Attributes; using ODataService.Helpers; using ODataService.Models; @@ -16,7 +13,7 @@ public class ActionsController : ODataController { public ActionsController(UnitOfWork uow) { this.Session = uow; } - [ODataRoute("InitializeDatabase")] + [Route("odata/InitializeDatabase")] public IActionResult InitializeDatabase() { DemoDataHelper.CleanupDatabase(Session); DemoDataHelper.CreateDemoData(Session); @@ -24,7 +21,7 @@ public IActionResult InitializeDatabase() { } [HttpGet] - [ODataRoute("TotalSalesByYear(year={year})")] + [Route("odata/TotalSalesByYear(year={year})")] public IActionResult TotalSalesByYear(int year) { decimal result = Session.Query() .Where(o => o.Date.Value.Year == year) diff --git a/CS/ODataService/Controllers/ContractController.cs b/CS/ODataService/Controllers/ContractController.cs index 7eea4d3..83436ca 100644 --- a/CS/ODataService/Controllers/ContractController.cs +++ b/CS/ODataService/Controllers/ContractController.cs @@ -1,8 +1,12 @@ using System; using System.Linq; using DevExpress.Xpo; -using Microsoft.AspNet.OData; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Deltas; +using Microsoft.AspNetCore.OData.Formatter; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Results; +using Microsoft.AspNetCore.OData.Routing.Controllers; using ODataService.Helpers; using ODataService.Models; diff --git a/CS/ODataService/Controllers/CustomerController.cs b/CS/ODataService/Controllers/CustomerController.cs index c0bbc53..4cee6c7 100644 --- a/CS/ODataService/Controllers/CustomerController.cs +++ b/CS/ODataService/Controllers/CustomerController.cs @@ -1,8 +1,12 @@ using System; using System.Linq; using DevExpress.Xpo; -using Microsoft.AspNet.OData; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Deltas; +using Microsoft.AspNetCore.OData.Formatter; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Results; +using Microsoft.AspNetCore.OData.Routing.Controllers; using ODataService.Helpers; using ODataService.Models; diff --git a/CS/ODataService/Controllers/DocumentsController.cs b/CS/ODataService/Controllers/DocumentsController.cs index 776b234..20b6e62 100644 --- a/CS/ODataService/Controllers/DocumentsController.cs +++ b/CS/ODataService/Controllers/DocumentsController.cs @@ -1,8 +1,11 @@ using System; using System.Linq; using DevExpress.Xpo; -using Microsoft.AspNet.OData; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Formatter; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Results; +using Microsoft.AspNetCore.OData.Routing.Controllers; using ODataService.Helpers; using ODataService.Models; diff --git a/CS/ODataService/Controllers/OrderController.cs b/CS/ODataService/Controllers/OrderController.cs index 3cd1489..8d8f7df 100644 --- a/CS/ODataService/Controllers/OrderController.cs +++ b/CS/ODataService/Controllers/OrderController.cs @@ -1,9 +1,12 @@ using System; using System.Linq; using DevExpress.Xpo; -using Microsoft.AspNet.OData; -using Microsoft.AspNet.OData.Routing; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Deltas; +using Microsoft.AspNetCore.OData.Formatter; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Results; +using Microsoft.AspNetCore.OData.Routing.Controllers; using ODataService.Helpers; using ODataService.Models; @@ -100,7 +103,7 @@ public IActionResult Patch([FromODataUri] int key, Delta order) { [HttpPost] [HttpPut] - [ODataRoute("Order({key})/OrderDetails")] + [Route("Order({key})/OrderDetails")] public IActionResult AddToOrderDetails([FromODataUri] int key, OrderDetail orderDetail) { Order order = Session.GetObjectByKey(key); if(order == null) { diff --git a/CS/ODataService/Controllers/OrderDetailController.cs b/CS/ODataService/Controllers/OrderDetailController.cs index 072b8bf..fa91d75 100644 --- a/CS/ODataService/Controllers/OrderDetailController.cs +++ b/CS/ODataService/Controllers/OrderDetailController.cs @@ -1,7 +1,11 @@ using System.Linq; using DevExpress.Xpo; -using Microsoft.AspNet.OData; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Deltas; +using Microsoft.AspNetCore.OData.Formatter; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Results; +using Microsoft.AspNetCore.OData.Routing.Controllers; using ODataService.Helpers; using ODataService.Models; diff --git a/CS/ODataService/Controllers/ProductController.cs b/CS/ODataService/Controllers/ProductController.cs index 661eff6..e505a66 100644 --- a/CS/ODataService/Controllers/ProductController.cs +++ b/CS/ODataService/Controllers/ProductController.cs @@ -1,8 +1,12 @@ using System; using System.Linq; using DevExpress.Xpo; -using Microsoft.AspNet.OData; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Deltas; +using Microsoft.AspNetCore.OData.Formatter; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Results; +using Microsoft.AspNetCore.OData.Routing.Controllers; using ODataService.Helpers; using ODataService.Models; diff --git a/CS/ODataService/Helpers/ApiHelper.cs b/CS/ODataService/Helpers/ApiHelper.cs index bd6ee75..6ccf713 100644 --- a/CS/ODataService/Helpers/ApiHelper.cs +++ b/CS/ODataService/Helpers/ApiHelper.cs @@ -2,8 +2,8 @@ using System.Collections; using System.Net; using DevExpress.Xpo; -using Microsoft.AspNet.OData; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.OData.Deltas; namespace ODataService.Helpers { public static class ApiHelper { diff --git a/CS/ODataService/Helpers/UriHelper.cs b/CS/ODataService/Helpers/UriHelper.cs index 61ceb40..984fc3c 100644 --- a/CS/ODataService/Helpers/UriHelper.cs +++ b/CS/ODataService/Helpers/UriHelper.cs @@ -1,11 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; -using Microsoft.AspNet.OData; -using Microsoft.AspNet.OData.Extensions; -using Microsoft.AspNet.OData.Routing; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.OData.UriParser; using ODataService.Models; diff --git a/CS/ODataService/Models/SingletonEdmModel.cs b/CS/ODataService/Models/SingletonEdmModel.cs index 1a67ce4..fd12bdf 100644 --- a/CS/ODataService/Models/SingletonEdmModel.cs +++ b/CS/ODataService/Models/SingletonEdmModel.cs @@ -1,11 +1,9 @@ using DevExpress.Data.Filtering; using DevExpress.Xpo; using DevExpress.Xpo.Metadata; -using Microsoft.AspNet.OData.Builder; using Microsoft.OData.Edm; +using Microsoft.OData.ModelBuilder; using ODataService.Helpers; -using System; -using System.Collections.Generic; using System.Linq; namespace ODataService.Models { diff --git a/CS/ODataService/ODataService.csproj b/CS/ODataService/ODataService.csproj index 87c533c..49ca191 100644 --- a/CS/ODataService/ODataService.csproj +++ b/CS/ODataService/ODataService.csproj @@ -4,11 +4,11 @@ - + - - - + + + diff --git a/CS/ODataService/Startup.cs b/CS/ODataService/Startup.cs index 642f734..02c33e2 100644 --- a/CS/ODataService/Startup.cs +++ b/CS/ODataService/Startup.cs @@ -1,17 +1,16 @@ -using System; -using System.Linq; -using DevExpress.Xpo.DB; -using Microsoft.AspNet.OData.Batch; -using Microsoft.AspNet.OData.Extensions; +using DevExpress.Xpo.DB; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.AspNetCore.OData; +using Microsoft.AspNetCore.OData.Batch; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using ODataService.Helpers; using ODataService.Models; +using System; +using System.Linq; namespace ODataService { @@ -27,12 +26,15 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddOData(); - services.AddODataQueryFilter(); - services.AddMvc(options => { - options.EnableEndpointRouting = false; - options.ModelValidatorProviders.Clear(); - }); + services.AddControllers() + .AddOData(opt => opt + .Select() + .Filter() + .OrderBy() + .Expand() + .Count() + .SetMaxTop(null) + .AddRouteComponents("odata", SingletonEdmModel.GetEdmModel())); services.AddSingleton(); @@ -50,12 +52,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - app.UseODataBatching(); + app.UseRouting(); - app.UseMvc(b => + app.UseEndpoints(endpoints => { - b.Count().Filter().OrderBy().Expand().Select().MaxTop(null); - b.MapODataServiceRoute("odata", "odata", SingletonEdmModel.GetEdmModel(), new DefaultODataBatchHandler()); + endpoints.MapControllers(); }); } } diff --git a/CS/Tests/ODataTestsBase.cs b/CS/Tests/ODataTestsBase.cs index d9a61de..101883e 100644 --- a/CS/Tests/ODataTestsBase.cs +++ b/CS/Tests/ODataTestsBase.cs @@ -21,7 +21,7 @@ protected Container GetODataContainer() { [OneTimeSetUp] public void OneTimeSetup() { string appPath = Path.GetDirectoryName(this.GetType().Assembly.Location); - appPath = Path.GetFullPath(Path.Combine(appPath, "..", "..", "..", "..", "ODataService", "bin", "debug", "netcoreapp3.1")); + appPath = Path.GetFullPath(Path.Combine(appPath, "..", "..", "..", "..", "ODataService", "bin", "debug", "net8")); dotnetProcess = Process.Start(new ProcessStartInfo("dotnet") { WorkingDirectory = appPath, Arguments = "ODataService.dll" }); } diff --git a/CS/Tests/Tests.csproj b/CS/Tests/Tests.csproj index 95a5998..a66de10 100644 --- a/CS/Tests/Tests.csproj +++ b/CS/Tests/Tests.csproj @@ -1,6 +1,6 @@ - netcoreapp3.1 + net8 diff --git a/README.md b/README.md index 3d2fa06..aea623a 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,20 @@ [![](https://img.shields.io/badge/📖_How_to_use_DevExpress_Examples-e9f6fc?style=flat-square)](https://docs.devexpress.com/GeneralInformation/403183) [![](https://img.shields.io/badge/💬_Leave_Feedback-feecdd?style=flat-square)](#does-this-example-address-your-development-requirementsobjectives) -# How to Implement OData v4 Service with XPO (.NET Core 3.1) +# How to Implement OData v4 Service with XPO (.NET 8) > **Note**: It is much easier to use the **[Web API Service](https://docs.devexpress.com/eXpressAppFramework/403394/backend-web-api-service)** with integrated authorization & CRUD operations based on ASP.NET Core OData 8.0 (OData v4) powered by EF Core and XPO ORM library instead. For more information, see [A 1-Click Solution for CRUD Web API Services with Role-based Access Control via EF Core & XPO (FREE)](https://community.devexpress.com/blogs/news/archive/2022/06/20/a-one-click-solution-for-role-based-access-control-asp-net-core-web-api-services-via-entity-framework-core-and-xpo-v22-1.aspx). ---------------- -This example demonstrates how to create **an ASP.NET Core 3.1 Web API** project and provide a simple REST API using the XPO ORM for data access. For the .NET Framework-based example, refer to [How to Implement OData v4 Service with XPO (.NET Framework)](https://github.com/DevExpress-Examples/XPO_how-to-implement-odata4-service-with-xpo). +This example demonstrates how to create **an ASP.NET 8** project and provide a simple REST API using the XPO ORM for data access. For the .NET Framework-based example, refer to [How to Implement OData v4 Service with XPO (.NET Framework)](https://github.com/DevExpress-Examples/XPO_how-to-implement-odata4-service-with-xpo). ## Prerequisites -* [Visual Studio 2019](https://visualstudio.microsoft.com/vs/) with the following workloads: +* [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) with the following workloads: * **ASP.NET and web development** - * **.NET Core cross-platform development** -* [.NET Core SDK 3.1 or later](https://www.microsoft.com/net/download/all) + * **.NET cross-platform development** +* [.NET 8.0 SDK or later](https://www.microsoft.com/net/download/all) ## Steps To Implement @@ -41,38 +41,38 @@ This example demonstrates how to create **an ASP.NET Core 3.1 Web API** project ``` - Modify the `ConfigureServices()` method in the *Startup.cs* file to initialize the data layer and register XPO UnitOfWork and OData services in Dependency Injection. ```cs - public void ConfigureServices(IServiceCollection services) { - services.AddOData(); - services.AddODataQueryFilter(); - services.AddMvc(options => { - options.EnableEndpointRouting = false; - options.ModelValidatorProviders.Clear(); - }); - - services.AddSingleton(); - - services.AddXpoDefaultUnitOfWork(true, (DataLayerOptionsBuilder options) => - options.UseConnectionString(Configuration.GetConnectionString("MSSqlServer")) - .UseAutoCreationOption(AutoCreateOption.DatabaseAndSchema) // debug only - .UseEntityTypes(ConnectionHelper.GetPersistentTypes())); - } +public void ConfigureServices(IServiceCollection services) { + services.AddControllers() + .AddOData(opt => opt + .Select() + .Filter() + .OrderBy() + .Expand() + .Count() + .SetMaxTop(null) + .AddRouteComponents("odata", SingletonEdmModel.GetEdmModel())); + + services.AddSingleton(); + + services.AddXpoDefaultUnitOfWork(true, (DataLayerOptionsBuilder options) => + options.UseConnectionString(Configuration.GetConnectionString("MSSqlServer")) + .UseAutoCreationOption(AutoCreateOption.DatabaseAndSchema) // debug only + .UseEntityTypes(ConnectionHelper.GetPersistentTypes())); +} ``` -- Modify the `Configure()` method in the *Startup.cs* file to add middleware for OData services and specify mapping for the service route. Note that we will pass the EDM model defined on the second step as a parameter (`SingletonEdmModel.GetEdmModel()`). +- Modify the Configure() method in the Startup.cs file to configure routing and map controllers: ```cs - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseODataBatching(); - - app.UseMvc(b => - { - b.Count().Filter().OrderBy().Expand().Select().MaxTop(null); - b.MapODataServiceRoute("odata", "odata", SingletonEdmModel.GetEdmModel(), new DefaultODataBatchHandler()); - }); - } +public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + if (env.IsDevelopment()) { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => { + endpoints.MapControllers(); + }); +} ``` ### Step 4: Implement OData Controllers for CRUD and Actions/Functions @@ -80,9 +80,11 @@ This example demonstrates how to create **an ASP.NET Core 3.1 Web API** project - Implement the required methods in OData controllers (e.g., `Get`, `Post`, `Put`, `Patch`, `Delete`, etc.) as shown in this example (for instance, **CS\ODataService\Controllers\CustomersController.cs**). - Implement methods in an OData Controller for required OData Actions and Functions as shown in **CS\ODataService\Controllers\ActionsController.cs**. -## Does this example address your development requirements/objectives? - -[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=XPO_how-to-implement-odata4-service-with-xpo-netcore&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=XPO_how-to-implement-odata4-service-with-xpo-netcore&~~~was_helpful=no) - +## Does this example address your development requirements/objectives? + +[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=XPO_how-to-implement-odata4-service-with-xpo-netcore&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=XPO_how-to-implement-odata4-service-with-xpo-netcore&~~~was_helpful=no) + (you will be redirected to DevExpress.com to submit your response) + + From 1e2c006c1c97cf72e8ab5cf61575762950929471 Mon Sep 17 00:00:00 2001 From: DevExpressExampleBot Date: Tue, 9 Sep 2025 13:35:50 +0400 Subject: [PATCH 2/2] README auto update [skip ci] --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index aea623a..8c49146 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -![](https://img.shields.io/endpoint?url=https://codecentral.devexpress.com/api/v1/VersionRange/223364607/23.2.1%2B) [![](https://img.shields.io/badge/Open_in_DevExpress_Support_Center-FF7200?style=flat-square&logo=DevExpress&logoColor=white)](https://supportcenter.devexpress.com/ticket/details/T835143) [![](https://img.shields.io/badge/📖_How_to_use_DevExpress_Examples-e9f6fc?style=flat-square)](https://docs.devexpress.com/GeneralInformation/403183) [![](https://img.shields.io/badge/💬_Leave_Feedback-feecdd?style=flat-square)](#does-this-example-address-your-development-requirementsobjectives) @@ -80,10 +79,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - Implement the required methods in OData controllers (e.g., `Get`, `Post`, `Put`, `Patch`, `Delete`, etc.) as shown in this example (for instance, **CS\ODataService\Controllers\CustomersController.cs**). - Implement methods in an OData Controller for required OData Actions and Functions as shown in **CS\ODataService\Controllers\ActionsController.cs**. -## Does this example address your development requirements/objectives? - -[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=XPO_how-to-implement-odata4-service-with-xpo-netcore&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=XPO_how-to-implement-odata4-service-with-xpo-netcore&~~~was_helpful=no) - +## Does this example address your development requirements/objectives? + +[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=XPO_how-to-implement-odata4-service-with-xpo-netcore&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=XPO_how-to-implement-odata4-service-with-xpo-netcore&~~~was_helpful=no) + (you will be redirected to DevExpress.com to submit your response)