Change the Entity Data Model Structure Exposed in OData
- 6 minutes to read
This article describes techniques that you can use to customize the Web API Service’s OData interface including the availability of particular endpoints and the structure of data written to the service’s responses.
Change the Expansion Depth for Related Business Objects
To specify the ODataValidationSettings.MaxExpansionDepth option, follow the steps below:
Implement a custom ODataQueryValidator service to set MaxExpansionDepth:
using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Query; // ... public class MyODataQueryValidator : ODataQueryValidator { public override void Validate(ODataQueryOptions options, ODataValidationSettings validationSettings){ validationSettings.MaxExpansionDepth = 3; base.Validate(options, validationSettings); } }
Replace the default ODataQueryValidator service with the custom service in the ConfigureServices method:
File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
public void ConfigureServices(IServiceCollection services) { // ... services.AddControllers().AddOData((options, serviceProvider) => { options .EnableQueryFeatures(100) .AddRouteComponents("api/odata", new EdmModelBuilder(serviceProvider).GetEdmModel(), odataServices => { odataServices.AddSingleton<ODataQueryValidator, MyODataQueryValidator>(); }); }); // ... }
Enable/Disable Web Actions for Business Objects
Use the techniques described below to enable or disable specific combinations of OData endpoints and HTTP verbs for business objects in your Web API Service application.
Important
We recommend that you configure Type, Object, and Member security permissions for your business objects. Ensure that your API controls data access or CRUD operations depending on the user role. In addition, you can use the techniques described in this section to prevent execution of specific web actions on a business object at the Swagger UI and OData query levels.
Use the
WebApiOptions.ConfigureDataControllers
extension method to enable/disable web actions globally for all business objects:File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
services.AddXafWebApi(builder => { builder.ConfigureOptions(options => { options.ConfigureDataControllers(b => { // Place your logic to customize the availability of web actions here. // You can use one of the following methods: // - b.WithActions(a => !a.ActionName.Contains("Ref")); // - b.WithActions(WebApiActions.PostEntity | WebApiActions.GetEntity); // - b.ReadOnly(); }); }); }, Configuration);
Use the
BusinessObjectConfigurator.ConfigureController
method to enable/disable web actions for a specific business object. If this method is called for a business object, it overrides all effects ofWebApiOptions.ConfigureDataControllers
for this object.File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
services.AddXafWebApi(builder => { builder.ConfigureOptions(options => { options.BusinessObject<MyEntity>().ConfigureController(b => { // Place your logic to customize the availability of web actions here. // You can use one of the following methods: // - b.WithActions(a => !a.ActionName.Contains("Ref")); // - b.WithActions(WebApiActions.PostEntity | WebApiActions.GetEntity); // - b.ReadOnly(); }); }); }, Configuration);
Both methods take a delegate as a parameter. The delegate’s argument exposes the WithActions
method, which has two overloads that you can use in the following ways:
Allow web actions based on an arbitrary predicate:
File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
options.BusinessObject<MyEntity>().ConfigureController(b => { b.WithActions(a => !a.ActionName.Contains("Ref")); });
Use predefined bit flags:
File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
options.BusinessObject<MyEntity>().ConfigureController(b => { b.WithActions(WebApiActions.PostEntity | WebApiActions.GetEntity); });
The WebApiActions
enumeration defines the following bit flags:
Flag | Description |
---|---|
GetEntity |
Gets the specified object. |
GetEntities |
Gets multiple objects. |
PostEntity |
Creates a new object. |
PutEntity |
Overwrites the specified object. |
PatchEntity |
Updates the specified object. |
DeleteEntity |
Deletes the specified object. |
GetReference |
Gets an object or a list of objects from the specified navigation or collection property. |
CreateReference |
Associates an object with a navigation property or adds an object to a list property. |
DeleteNavigation |
Unlinks an object from a navigation property. |
DeleteFromCollection |
Removes an object from a list property. |
ReadOnly |
Equivalent to GetEntity | GetEntities | GetReference |
EntityActions |
Equivalent to GetEntity | GetEntities | PostEntity | PutEntity | PatchEntity | DeleteEntity |
ReferenceActions |
Equivalent to GetReference | CreateReference | DeleteNavigation | DeleteFromCollection |
You can also use the ReadOnly
method to only allow read actions. This method has the same effect as the ReadOnly
bit flag.
File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
options.BusinessObject<MyEntity>().ConfigureController(b => {
b.ReadOnly();
// The above line is equivalent to:
// b.WithActions(WebApiActions.ReadOnly);
});
The AllActions
method allows you to enable all web actions for a specific business class when some of the actions are prohibited globally.
options.BusinessObject<MyEntity>().ConfigureController(b => {
b.AllActions();
});
For example, the image below demonstrates how the Swagger UI reflects a business object configured in read-only mode.
Important
Keep in mind that the hidden endpoints are not only invisible in Swagger, but are entirely unavailable through the OData interface.
Configure an OData Entity Type
Use one of the following techniques to customize the OData entity data model structure.
Use the OnCustomizeEdmModel property
The WebApiEvents.OnCustomizeEdmModel
property allows you to customize the OData entity data model structure at runtime (change types, properties, actions, and so on).
For example, you can implement custom logic that adds a property that was removed from the model by the IgnoreDataMemberAttribute.
File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
services.AddXaf(Configuration, builder => {
//...
builder.AddXafWebApi(webApiBuilder => {
webApiBuilder.ConfigureOptions(options => {
options.Events.OnCustomizeEdmModel = context => {
context.ODataModelBuilder.AddEntityType(typeof(MyEntity)).AddProperty(typeof(MyEntity).GetProperty(nameof(MyEntity.MyProperty)));
};
//...
});
});
});
public class MyEntity : BaseObject {
[IgnoreDataMember]
public virtual int MyProperty { get; set; }
//...
}
Use the ODataModelBuilder
Implement the DevExpress.ExpressApp.WebApi.Services.IEdmModelCustomizer
interface in a custom class. In your implementation of the class CustomizeEdmModel
method, use the ODataModelBuilder to customize the OData entity data model based on your requirements.
You can register your IEdmModelCustomizer
implementation after the services.AddXafWebApi
call in the ConfigureServices
method:
File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
services.AddXafWebApi(options => { /* ... */ }, Configuration);
services.TryAddEnumerable(ServiceDescriptor.Transient<IEdmModelCustomizer, CustomEdmModelCustomizer>());
For more information, refer to the following resources:
- Model builders overview
- Code of the built-in
IEdmModelCustomizer
implementers (DateTimeNullableEdmModelCustomizer
,PersistentAliasEdmModelCustomizer
, etc.) within theDevExpress.ExpressApp.WebApi
library sources - Support articles:
- Web API - The “400 Bad Request” error occurs when you use properties with NotMappedAttribute in EF Core-based apps
- Web API Service - How to return or serialize a business object in a custom method/endpoint of API controller?
- Web API - How to create a custom ODataController for each business object type to create own custom endpoints
Use the EntityTypeConfiguration
The EntityTypeConfigurator.ConfigureODataEntityType
method allows you to directly access an EntityTypeConfiguration<TEntityType> instance for your business class. Use the API exposed through the instance to configure the entity type.
For example, you can add a property that was hidden from the Entity Data Model by the IgnoreDataMemberAttribute as shown below.
File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)
services.AddXafWebApi(builder => {
builder.ConfigureOptions(options => {
options.BusinessObject<MyEntity>().ConfigureEntityType(b => {
// Add the current business class ignored property.
b.ConfigureODataEntityType(d => d.Property(o => o.IgnoredProperty));
// Add the base class ignored property.
b.ConfigureODataEntityType(
// You can access the `BaseType` properties sequentially to get to
// an arbitrary ancestor's configurator (`d.BaseType.BaseType ...`)
d => d.BaseType.AddProperty(
typeof(MyEntity)
.GetProperty(nameof(BusinessEntityBase.BaseClassIgnoredProperty))
)
);
});
});
}, Configuration);