1
votes

I have a requirement to encapsulate pieces of business logic within a transaction in an OData Web API service. Some of these pieces will need to accept one or more entities.

An example use case might be StockProduct and this might accept a Product entity and a Location entity. It would create the product and update stock records for the Location.

The approach I've taken is to create an unbound OData action that accepts these entities so that both of these can be operated on in a single transaction. Unfortunately neither can Entities be used as an ODataActionParameter nor can they be part of a class and used as a complex parameter.

I can think of a two ways around this:

  • Create a DTO class that is not an entity that is a mirror of each of my mirror classes and convert from DTO to Model within my action. The problem here is that I already have a DTO for each Model eg. Product.cs and ProductDTO.cs and don't really want to have to create a third class. (Currently, the ProductDTO.cs is used for Posts, Puts, Patches and Deletes and the Product.cs is used for Gets).

  • Abandon OData actions and create a simple end point that accepts whatever I like. I'm not keen on going down the second route as I'd like to use OData exclusively.

Any thoughts or suggestions?

2

2 Answers

1
votes

You can use the ActionConfiguration.EntityParameter() method to bind an entity as a parameter to your OData action method.

Here is an example:

ActionConfiguration validate = ModelBuilder.EntityType<TEntity>()
    .Collection.Action("Validate");
validate.Namespace = "Importation";
validate.EntityParameter<TEntity>(typeof(TEntity).Name);
validate.CollectionParameter<string>("UniqueFields");
validate.Returns<ValidationResult>();

However, note that the ModelState will not check against the content of the supplied Entity and will set any missing properties to null and properties exceeding the StringLength(x) annotation in your model will still pass. If you wish to validate the entity itself after, use this bit of code in your action method:

[HttpPost]
public virtual IHttpActionResult Validate(ODataActionParameters parameters)
{
//First we check if the parameters are correct for the entire action method
    if (!ModelState.IsValid)
    {
         return BadRequest(ModelState);
    }
    else
    {
         //Then we cast our entity parameter in our entity object and validate
         //it through the controller's Validate<TEntity> method
         TEntity Entity = (TEntity)parameters[typeof(TEntity).Name];
         Validate(Entity, typeof(TEntity).Name);
         if (!ModelState.IsValid)
         {
              return BadRequest(ModelState);
         }
         IEnumerable<string> uniqueFields = parameters["UniqueFields"] as IEnumerable<string>;
         bool result = Importer.Validate(Entity, uniqueFields);
         return Ok(result);
    }
}

As for your StockProductDTO, it seems to me that this is an new Business Entity by itself and should be treated as such.

0
votes

You can use a batch request to perform multiple operations within a single request. This allows you to use your existing controllers for inserting your two objects.

https://aspnetwebstack.codeplex.com/wikipage?title=Web+API+Request+Batching