expressappframework-403719-backend-web-api-service-use-odata-to-send-requests-customize-odata-options.md
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.
To specify the ODataValidationSettings.MaxExpansionDepth option, follow the steps below:
Implement a custom ODataQueryValidator service to set MaxExpansionDepth:
Replace the default ODataQueryValidator service with the custom service in the ConfigureServices method:
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:
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 of WebApiOptions.ConfigureDataControllers for this object.
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:
Use predefined bit flags:
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 |
EntityActions | Equivalent to GetEntity |
ReferenceActions | Equivalent to GetReference |
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.
Use one of the following techniques to customize the OData entity data model structure.
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; }
//...
}
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>());
services.AddXaf(Configuration, builder => {
//...
builder.AddXafWebApi(webApiBuilder => { /* ... */ });
});
services.TryAddEnumerable(ServiceDescriptor.Transient<IEdmModelCustomizer, CustomEdmModelCustomizer>());
For more information, refer to the following resources:
IEdmModelCustomizer implementers (DateTimeNullableEdmModelCustomizer, PersistentAliasEdmModelCustomizer, etc.) within the DevExpress.ExpressApp.WebApi library sourcesThe 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);