If you read my previous post and you thought to yourself: “but that’s a violation of DRY!”, then please stop whining right away!
If you – like me – like to make your dependencies explicit, you will probably think that it is perfectly fine to have to do the following things every time you want to have a piece of data automatically supplied for you:
- Apply the attribute to the action method (can be omitted if it has already been applied at the controller level)
- Change the signature of the action method
- Make sure the data makes its way into the view model handed to your view
In my opinion this is a fair compromise between avoiding writing code and still being explicit about what is going on.
If you, however, feel that there are too many steps above, and you want stuff to happen more automagically, you can do it in another way, that I find almost as attractive… first, create an interface to apply to all view models, that are capable of holding this piece of data – e.g. like this:
1 2 3 4 |
public interface IHasCurrentUserViewModel { CurrentUserViewModel CurrentUser { set; get; } } |
Then, make your page level view model implement this interface – from my previous example:
1 2 3 4 5 |
public class HomeScreenViewModel : IHasCurrentUserViewModel { public CurrentUserViewModel CurrentUser { set; get; } //... } |
And then modify the action filter to fetch the data in OnActionExecuting and insert the view model for you in OnActionExecuted – like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class ProvideCurrentUserViewModelAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext context) { var result = context.Result as ViewResult; if (result == null) return; var model = result.ViewData.Model as IHasCurrentUserViewModel; if (model == null) return; var sessionContext = ServiceLocator.Resolve<ISessionContext>(); var currentUser = sessionContext.CurrentUser; var model = new CurrentUserViewModel(currentUser); model.CurrentUser = model; } } |
This way, at most two things need to be done to automatically provide a piece of view data:
- Make the page level view model implement an appropriate interface (can be omitted if it already implements it)
- Apply the attribute to the action method (can be omitted if it has already been applied at the controller level)
It is definitely more DRY, but its lack of verbosity makes it harder to understand. And here, I don’t mean “understand” as in “haha, Mogens is too stupid to understand what is going on” – my point is that I will be wasting a few more brain cycles the next time I look at this action method while trying to figure out how it worked.