When building APIs the amount of code inside of controller actions has a tendency to spin out of control, become repetitive and not so easy to reuse. For a recent client project I’m working on I tried a new approach: result filters combined with C#7’s value tuples. I was quite happy with how it turned out and tweeted about it. That resulted in a few people asking me for an example, so I promised to cook something up. You can find that on my GitHub.
- The repo contains examples of:
– mapping two objects into one with AutoMapper
– using a custom reusable ResultFilter to map those objects instead of writing the code in each action that requires it
– using the new ValueTuple (C#7) structure to pass the two objects to the filter
The particular use case that triggered looking into this: imagine that the data required for eventually constructing the resource (eg: api/person/1) is coming from two different sources. For example, imagine the people coming from an application-level database, and the competences coming from another API. The repository for accessing the people store doesn’t have a notion of what a competence is (it could, for example, work on the EF Code First entity classes where that “competence” notion doesn’t exist). The repository for accessing the competences service is thus separate from the people repository, yet when a consumer of the API gets a person, that person including the competences should be returned.
To achieve this, I used AutoMapper to combine a person and competences into one DTO, and I did this in a custom ResultFilter. To pass the person object + competences to the filter I used the new C#7 ValueTuple structure – I really like the syntax of that 😉
By using a custom filter I can simply apply that filter on any action that needs to return a person with competences. I’m quite happy with this approach, as I feel it keeps the controller action code clean, and I can reuse the custom filter.
Moreover, as you get full access to the ActionDescriptor + HttpContext from inside a ResultFilter this allows for some pretty advanced logic (eg: when working with custom media types, I could have the filter handle this).