How can I pass service layer validation messages back to the caller? - c#

I've done alot of research, including here on SO, and I can't seem to find clear direction. I currently have an ASP.NET MVC3 application, with a service layer that sits on top of a repository.
In my service layer, I have functions such as:
public class MyService{
public void CreateDebitRequest(int userId, int cardId, decimal Amount, .... )
{
//perform some sort of validation on parameters, save to database
}
public void CreateCreditRequest(.....)
}
//perform some sort of validation on parameters, save to database
}
public void CreateBatchFile()
{
//construct a file using a semi-complex process which could fail
//write the file to the server, which could fail
}
public PaymentTransaction ChargePaymentCard(int paymentCardId, decimal amount)
{
//validate customer is eligible for amount, call 3rd party payments api call,
//...save to database, other potential failures, etc.
}
}
I've seen people say that parameter validation isn't very exceptional, and so throwing an exception is not very fitting. I also don't love the idea of passing in an out paramater, such as a string, and checking for an empty value. I've considered implementing a ValidationDictionary class, and making it a property of any given service class (it would contain an IsValid boolean, and a List of error messages, and could be checked after any given function call in the service layer to see how things went). I could check the ValidationDictionary status after running any given function:
var svc = new MyService();
svc.CreateBatchFile();
if (svc.ValidationDictionary.IsValid)
//proceed
else
//display values from svc.ValidationDictionary.Messages...
The thing I don't like about this is that I would have to update it for every service layer function call, to avoid having it retain old values (if I chose not to use it for many or most functions, one would still expect it to have a meaningful or null value after running any given function). Another thing I've considered is passing in the ValidationDictionary for each function call that might have detailed validation information, but then I am back to using an out parameter...
Do any of you have recommendations? I can't seem to figure out any clean way of doing this. Sometimes returning null for a function is enough information, but sometimes I'd like a little more validation information passed back to the caller. Any advice would be appreciated!
Edit to clarify:
My service layer is not aware that it is an MVC application that is consuming it. The service layer just has certain public functions such as CreateBatchFile() or AddDebitRequest(). Sometimes returning null is enough for the consumer (in this case a controller, but could be something else) to know what happened, and sometimes the consumer would like some more information from the service layer (maybe to pass along to ModelState if the consumer is a controller). How do I bubble this up from the service layer itself?

This is what I do. Have a class for your validation, and instead of passing parameters pass a view model. So in your case something like this, where ValidationResult is just a simple class w/ MemberName and ErrorMessage properties:
public class DebitRequestValidator{
public IEnumerable<ValidationResult> Validate(DebitRequestModel model){
//do some validation
yield return new ValidationResult {
MemberName = "cardId",
ErrorMessage = "Invalid CardId."
}
}
}
Then create a controller extension method to copy these validation results to the model state.
public static class ControllerExtensions
{
public static void AddModelErrors(this ModelStateDictionary modelState, IEnumerable<ValidationResult> validationResults)
{
if (validationResults == null) return;
foreach (var validationResult in validationResults)
{
modelState.AddModelError(validationResult.MemberName, validationResult.ErrorMessage);
}
}
}
Then in your controller do something like
[HttpPost]
public ActionResult DebitRequest(DebitRequestModel model) {
var validator = new DebitRequestValidator();
var results = validator.Validate(model);
ModelState.AddModelErrors(results);
if (!ModelState.IsValid)
return View(model)
//else do other stuff here
}
Then in your view you can display errors like normal.
#Html.ValidationMessageFor(m => m.CardId)

I used a system where it was passing an array of messages (or collection of classes), each element had codes, descriptions, friendly messages. We used to simply check if anything was there. It worked great between UI and another "service" layer, all exception were caught nicely, they were translated into these validation rules...just an idea

Use ViewModel objects that are passed between the Views and the Controller action methods. The ViewModel objects can handle Validation by a Validate(ValidationDictionary validationDictionary) method.
The controller will have to call the Validate method on ViewModel object before calling any method in the service layer. This should only be necessary for http POST actions.
Your views will then have to display validation messages.
This solution requires that the viewmodel objects are passed between the controller action and the view, but nowadays that is mostly handled by the ModelBinder in MVC.
Your controller (http post) actions will look something like this:
[HttpPost]
public ActionResult Foo(BarViewModel viewModel)
{
viewModel.Validate(ValidationDictionary);
if (!ModelState.IsValid)
{
return View(viewModel);
}
// Calls to servicelayer
}
Your Validate method in your ViewModel will look like this:
public void Validate(ValidationDictionary validationDictionary)
{
if (SomeProperty.Length > 30)
{
validationDictionary.AddError("SomeProperty", "Max length is 30 chars");
}
}

If you're just doing ViewModel Validation, FluentValidation is an excellent library.
If you're wanting to include business validation as feedback to the user, you could use the adapter pattern, it'll give you what you want.
Create an interface (IValidationDictionary or something similar). This interface would define an AddError method and would be passed to your service in order to add error messages.
public interface IValidationDictionary
{
void AddError(string key, string errorMessage);
}
Create a ModelStateAdapter for your mvc application.
public class ModelStateAdapter : IValidationDictionary
{
private ModelStateDictionary _modelState;
public ModelStateAdapter(ModelStateDictionary modelState)
{
_modelState = modelState;
}
public void AddError(string key, string errorMessage)
{
_modelState.AddModelError(key, errorMessage);
}
}
Your service calls that need validation would require the IValidationDictionary
public class MyService
{
public void CreateDebitRequest(int userId, int cardId, decimal Amount, .... , IValidationDictionary validationDictionary)
{
if(userId == 0)
validationDictionary.AddError("UserId", "UserId cannot be 0");
}
}
You would then have a dependency on IValidationDictionary but not on MVC which would also make your solution testable.
If you needed to implement the services in an app that didn't have a ModelStateDictionary, you would just implement the IValidationDictionary interface on a class used for holding your errors.
Controller example:
public ActionResult Test(ViewModel viewModel)
{
var modelStateAdapter = new ModelStateAdapter(ModelState);
_serviceName.CreateDebitRequest(viewModel.UserId, viewModel.CardId, ... , modelStateAdapter);
if(ModelState.IsValid)
return View("Success")
return View(viewModel);
}
Pro's of this approach:
No dependency on the calling libraries
It's possible to mock the IValidationDictionary for tests.
Con's of this approach:
You need to pass IValidationDictionary to every method that you want to do validation on that's going to be returned to the user.
Or
you need to initialise the service's validation dictionary (if you decide to have IValidationDictionary as a private field), in each controller action you want to validate against.

Related

FluentValidation using validator on wrong viewmodel

I'm using FluentValidation for the first time. I had some basic validation working, but then I realized I would need to do some database retrieval for some more complicated validation. That required doing Dependency Injection so I could use the database service, and that leads me to my current state: Stuck. I cannot get this to work.
To simplify things, I'll pretend that my application is dealing with sports leagues and teams, because I figure that's an easier mental model than contracts, invoices, funding sources, vendors, and subcontractors. :-)
So, suppose I have a viewmodel for a sports league. Within that viewmodel there is a collection of viewmodels for the teams that are in that league.
I have a screen to edit a league. That same screen allows changes to some of the information about the teams that are in that league.
LeagueViewModel
The viewmodel for the league contains a List of viewmodels for the teams.
[FluentValidation.Attributes.Validator(typeof(LeagueValidator))]
public class LeagueViewModel
{
public string LeagueName { get; set; }
public DateTime SeasonBeginDate { get; set; }
public DateTime SeasonEndDate { get; set; }
public List<TeamViewModel> TeamViewModels { get; set; }
}
I've created a validator for the LeagueViewModel. Unfortunately, when I edit the league and click the submit button, I get this error message:
InvalidCastException: Unable to cast object of type 'TeamViewModel' to type 'LeagueViewModel'. at FluentValidation.ValidationContext.ToGenericT
Apparently it is attempting to validate the TeamViewModel using the LeagueValidator.
I have gone through many variations trying to figure out how to get this to work. Here's what I have at the moment.
Validator
public class LeagueValidator : AbstractValidator<LeagueViewModel>
{
private readonly ILeagueService _leagueService;
public LeagueValidator(ILeagueService leagueService)
{
_leagueService = leagueService;
RuleFor(x => x.SeasonEndDate)
.NotNull()
.GreaterThan(x => x.SeasonBeginDate)
.WithMessage("Season End Date must be later than Season Begin Date.");
}
}
(The LeagueService bit is in there because in the real code it needs to check against some database values, which it uses the service to retrieve.)
Note that the LeagueValidator doesn't have any validation rules for any fields in the List of TeamViewModels.
League Validator Factory
public class LeagueValidatorFactory : ValidatorFactoryBase
{
private readonly Container _container;
public LeagueValidatorFactory(Container container)
{
_container = container;
}
public override IValidator CreateInstance(Type validatorType)
{
return _container.GetInstance<LeagueValidator>();
}
}
Dependency Injector
We're using SimpleInjector for DI. As part of that existing setup, it is calling a method to register the services. Within that method I've added a call to this:
private static void RegisterValidators(Container container)
{
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
var leagueValidatorProvider =
new FluentValidationModelValidatorProvider(new LeagueValidatorFactory(container));
leagueValidatorProvider.AddImplicitRequiredValidator = false;
ModelValidatorProviders.Providers.Add(leagueValidatorProvider);
container.Register<LeagueValidator>();
}
Questions
How do I get this to work properly?
Why is it trying to use the LeagueValidator to validate the TeamViewModel?
Do I need to have a separate validator and validator factory for every view model?
Even those that don't have any validation rules?
How do I tell it which validator to use for which viewmodel?
I figure I must be misunderstanding something basic.
Edit
Steven's response below got me pointed in the right direction! After I made his suggested changes, I encountered another error. Once I got that fixed, it works! Here are the changes I made to get the code above working properly.
LeagueViewModel
I removed this line, as it isn't necessary.
[FluentValidation.Attributes.Validator(typeof(LeagueValidator))]
LeagueValidatorFactory
I renamed it to "ValidatorFactory", because it turns out there will only be one validator factory, regardless of how many validators I create. I then changed the CreateInstance method to this:
public override IValidator CreateInstance(Type validatorType)
{
if (_container.GetRegistration(validatorType) == null)
{
return null;
}
return (IValidator)_container.GetInstance(validatorType);
}
This no longer explicitly specifies the type of validator to get (which is why only one factory will be needed). To determine whether a validator for the given type is available, it does a call to GetRegistration, returning null if none is found.
This was important! For every viewmodel, it is going to try to find a validator. Without this null check, an InvalidCastException gets thrown.
Dependency Injector
Following Steven's suggestion, I replaced the container.Register line with this:
container.Register(typeof(IValidator<>), new[] { typeof(SimpleInjectorInitializer).Assembly });
That avoids the need to explicitly list each validator every time a new one is added.
And now it all works! Thanks very much for your help, Steven!
I'm unfamiliar with FluentValidation, but it seems your LeagueValidatorFactory is requesting the wrong type from the container, considering it is supplied with the type to validate.
Because of this, I'd expect your validation factory to look something like this:
public class LeagueValidatorFactory : ValidatorFactoryBase
{
private readonly Container _container;
public LeagueValidatorFactory(Container container) =>
_container = container;
public override IValidator CreateInstance(Type validatorType) =>
(IValidator)_container.GetInstance(validatorType);
}
What I can see from the FluentValidator source code, is that the validatorType is a closed-generic version of the IValidator<T> type, with the T being the actual type being validated. This means, that you will have to register the validators by their IValidator<T> interface. For instance:
container.Register<IValidator<LeagueViewModel>, LeagueValidator>();
This Configuration as Code (or explicit-register) model, where you register every validator explicitly using a line of code, might work fine if you have just a few validators, but this will typically result in a Composition Root that has to be updated frequently.
A better model, therefore, is to use Auto-Registration, where you register all IValidator<T> implementations, using reflection. Fortunately, you don't have to implement this yourself; Simple Injector has your back:
var validatorAssemblies = new[] { typeof(LeagueValidator).Assembly };
container.Register(typeof(IValidator<>), validatorAssemblies);
This makes sure that you never have to change your Composition Root when you just added a new validator (in that particular assembly).
With this setup, I see no reason why you should mark your view model with the FluentValidation.Attributes.ValidatorAttribute. If you can, please remove it, as it only causes untight coupling between your view model and the validator.

Using delegates and events with DI in a Controller

I have a system which fundamentally is used to resolve exceptions and output a CSV on demand which details every resolved item. Each day, there will be new exceptions which need to be dealt with. I have a POST method for this in my controller:
[HttpPost]
private ActionResult Resolve(ExceptionViewModel modifiedExceptionViewModel, string currentFilter)
{
// resolve database records...
return RedirectToAction("Index", "Exceptions");
}
I have had a new requirement however, the user wants the system to identify when the last outstanding has been resolved and then automatically output the CSV to the file share, rather than having to go and do this manually.
I firstly created a method for checking whether or not that was the last exception, and called this WasLastException(); I knew that I could just wrap this in an IF statement and on true call a method I have called OutputMasterFileCsv(); but before doing this I thought I would try out delegates/events for the first time which has led me to a similar result but has also raised a few questions.
Some background to my application
This is an Entity Framework Code First MVC web application that is making use of using Unity DI, I have wrapped all my repository calls in an ProcessDataService class in my core layer, which has an interface IProcessDataService that is being registered with Unity.
This is how I have tried to add my event:
Controller's constructor
public ExceptionsController(IProcessDataService service)
{
_service = service; //publisher
//event for delegate
OutputService outputService = new OutputService(_service); //subscriber
_service.LastException += outputService.OnLastException;
}
Output Service
public void OnLastException(object source, EventArgs e)
{
// output the CSV
}
Process Data Service
public delegate void LastExceptionEventHandler(object source, EventArgs args);
public class ProcessDataService : IProcessDataService
{
private readonly IExceptionRepository _exceptionRepository;
public ProcessDataService(IExceptionRepository evpRepo)
{
_exceptionRepository = evpRepo;
}
public event LastExceptionEventHandler LastException;
public void OnLastException()
{
if (LastException != null)
LastException(this, EventArgs.Empty);
}
}
New Resolve method in the Controller
[HttpPost]
private ActionResult Resolve(ExceptionViewModel modifiedExceptionViewModel, string currentFilter)
{
// resolve database records...
if(_service.WasLastException())
{
//raise the event
_service.OnLastException();
}
return RedirectToAction("Index", "Exceptions");
}
This all works well, however I feel like I am not using delagates and events in the right place here somehow, Instead of calling the OnLastException() above and making use of the event, why wouldn't I just simply call _service.OutputMasterFileCsv(); which is already located in my ProcessDataService class?
I believe this has something to do with loose coupling but I dont fully understand what the benefits of this actually are, or am I completely off the mark with all this...?
I thought I would give it ago anyway while I had the chance and hopefully learn something new. If anyone with abit more experience could step in and provide some guidance it would be greatly appreciated as I am a little lost now.
As you are correctly pointing out, using events in this way does not make much sense:
if(_service.WasLastException())
{
//raise the event
_service.OnLastException();
}
You can fix this by making IProcessDataService expose a ResolveException action, and moving the resolving logic from the controller to the service:
[HttpPost]
private ActionResult Resolve(ExceptionViewModel modifiedExceptionViewModel, string currentFilter)
{
// make needed preparations...
_service.ResolveException(...prepared parameters...);
return RedirectToAction("Index", "Exceptions");
}
Then, inside the ProcessDataService.ResolveException method check
if you are currently processing the last exception, and raise the LastException event.
public class ProcessDataService : IProcessDataService
{
//...
public ResolveException(...prepared parameters...) {
// resolve an exception and set lastException
if(lastException) {
this.OnLastException();
}
}
// notice the private modifier
private void OnLastException()
{
if (LastException != null)
LastException(this, EventArgs.Empty);
}
}
This way the data processing service simply notifies the outside world when the last exception is processed. The service has no idea if anyone cares or does something when this happens. The controller knows even less. Only the output service contains processing logic for last exceptions.
With that said, the real power of events lies in the fact that there can be many subscribers, with each subscriber performing its own tasks without knowing anything about the other subscribers. So, you could for instance add another event handler to say, send an email to a supervisor saying that all the exceptions for the day have been resolved.
What matters is that in this case you would not need to modify the controller or other services to account for this newly introduced email sending functionality.
You have decoupled the controller from the service and service from storage. That is fine. But I don't really understand the sense of the event LastException in the ProcessDataService. This is already decoupled by interface IProcessDataService, why to use event?
Another think I don't understand is where is the last exception?
If you want to decouple outputService from ProcessDataService, you can do it like:
public ProcessDataService(IExceptionRepository evpRepo, IOutputService outputService)
{
_exceptionRepository = evpRepo;
_outputService = _outputService;
}
public void ProcessLastException()
{
_outputService.Command() //or whatever suitable name you for your method
}
And in controller:
if(_service.WasLastException())
{
//call service
_service.ProcessLastException();
}
Or even more simple add some method for processing last exception to IProcessDataService.
There are more ways how to inject dependency. You have injected the dependency into constructor and that is why, you don't need the event for decoupling.

Removing boilerplate from ASP.NET MVC actions

I have something like this in almost every action:
public ActionResult Show(int object_id)
{
Object obj = ObjectRepository.ById(object_id);
if (obj == null)
{
throw new HttpException(404);
}
if (obj.SomeCheck)
{
throw new HttpException(403);
}
// processing
}
Question is how to move object getting (and throwing http exceptions) away from action and have something like this:
public ActionResult Show(Object obj)
{
// processing
}
UPD: Can't change ObjectRepository and model itself, it's used not only with ASP.NET but in other parts of the project.
One option is to refactor your boilerplate into a private method:
private object GetItem(object obj) {
Object obj = ObjectRepository.ById(object_id);
if (obj == null) {
throw new HttpException(404);
}
if (obj.SomeCheck()) {
throw new HttpException(403);
}
return obj;
}
Then:
public ActionResult Show(int object_id) {
object obj = GetItem(object_id);
// processing
}
As others have suggested you could write filters or invoke an AOP framework like PostSharp.
However, that might be a tall order for some. You might want to consider writing something simple, maintainable and fairly readable, that everyone on the team can immediately understand:
public ActionResult Show(int object_id)
{
SomeClass obj = Require<SomeClass>(object_id, assumption: o => o.SomeCheck);
// processing
}
//Perhaps: put this in a base controller or other common class
private object Require<T>(int id, Func<object, bool> assumption) where T : class
{
var o = ObjectRepository.ById(object_id) as T;
//Result is required
if (o == null) {
throw new HttpException(404);
}
//Verify assumption
if (!assumption(o)) {
throw new HttpException(403);
}
return o;
}
You might look at
Writing a custom filter attribute such as an AuthorizationAttribute or ValidateInputAttribute. They can be applied to the entire controller or to specify methods (actions). See http://msdn.microsoft.com/en-us/magazine/dd695917.aspx#id0070026 for an overview.
You might want to look at using PostSharp (Aspect-Oriented Programming framework) to inject suitable aspects into your methods.
Others have provided some good answers. Among them, I like the idea of using an action filter but unfortunately I don't believe it will work very well as you'll be forced to use property injection (or explicit instantiation - yuck!) for the repository, which I'd prefer to avoid. For this reason, I think a dedicated controller method is a better option.
I will throw one more idea in the hat, though.
You mentioned that you can't change ObjectRepository but you can always abstract it:
public class HttpObjectService /*: IObjectService */
{
private readonly /*I*/ObjectRepository _repository;
public HttpObjectService(/*I*/ObjectRepository repository)
{
if (repository == null) throw new ArgumentNullException("repository");
_repository = repository;
}
public Object ById(int id)
{
var obj = _repository.ById(id);
/* Check and throw HttpExceptions */
}
}
Then inject this into your controller. Whether or not you like the idea of a "web-specific" service that throws HttpExceptionsdirectly is a matter of taste but it is extremely reusable and, in this case, I believe it provides a cleaner separation of concerns than private validation methods in the controller.
The downside, of course, is that you're allowing your controller to delegate its responses (in the event of an error) directly to a third party. You may prefer your controller to have absolute control over this - that would be a reasonable criticism. In that case, you could always pass an ObjectValidator object into the constructor from the controller, which takes care of the validation. This would let your controller maintain control of what exceptions are thrown regardless of the service implementation.
You would have to be considerate of where this fits into your project architecture - it would probably belong in a separate assembly to your MVC project but one that is dedicated to web-based UIs.

Servicestack - Order of Operations, Validation and Request Filters

I detected a problem in the RequestFilter execution order.
The ValidationFeature in ServiceStack is a Plugin that just registers a Global Request Filter. The Order of Operations points out that Global Request Filters are executed after Filter Attributes with a Priority <0 and before Filter Attributes with a Priority >=0
My BasicAuth filter has -100 priority, and in fact everything goes well if the Service is annotated at class level, but it fails when the annotation is at method level, with the authentication filter being executed after.
I am using 3.9.70
Is there any quick fix for this? Thanks
When you add the annotation at method level then you are creating an Action Request Filter (because you are adding the annotation to an action method) which in the Order of Operations is operation 8, after the other filters have run.
5: Request Filter Attributes with Priority < 0 gets executed
6: Then any Global Request Filters get executed
7: Followed by Request Filter Attributes with Priority >= 0
8: Action Request Filters (New API only)
The best workaround I can suggest is to reconsider your service structure. I imagine you are having these difficulties because you are adding unauthenticated api methods alongside your secure api methods, and thus are using method level attributes to control authentication. So you are presumably doing something like this Your classes and attributes will be different, this is just exemplar:
public class MyService : Service
{
// Unauthenticated API method
public object Get(GetPublicData request)
{
return {};
}
// Secure API method
[MyBasicAuth] // <- Checks user has permission to run this method
public object Get(GetSecureData request)
{
return {};
}
}
I would do this differently, and separate your insecure and secure methods into 2 services. So I use this:
// Wrap in an outer class, then you can still register AppHost with `typeof(MyService).Assembly`
public partial class MyService
{
public class MyPublicService : Service
{
public object Get(GetPublicData request)
{
return {};
}
}
[MyBasicAuth] // <- Check is now class level, can run as expected before Validation
public class MySecureService : Service
{
public object Get(GetSecureData request)
{
return {};
}
}
}
Solution - Deferred Validation:
You can solve your execution order problem by creating your own custom validation feature, which will allow you to defer the validation process. I have created a fully functional self hosted ServiceStack v3 application that demonstrates this.
Full source code here.
Essentially instead of adding the standard ValidationFeature plugin we implement a slightly modified version:
public class MyValidationFeature : IPlugin
{
static readonly ILog Log = LogManager.GetLogger(typeof(MyValidationFeature));
public void Register(IAppHost appHost)
{
// Registers to use your custom validation filter instead of the standard one.
if(!appHost.RequestFilters.Contains(MyValidationFilters.RequestFilter))
appHost.RequestFilters.Add(MyValidationFilters.RequestFilter);
}
}
public static class MyValidationFilters
{
public static void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)
{
// Determine if the Request DTO type has a MyRoleAttribute.
// If it does not, run the validation normally. Otherwise defer doing that, it will happen after MyRoleAttribute.
if(!requestDto.GetType().HasAttribute<MyRoleAttribute>()){
Console.WriteLine("Running Validation");
ValidationFilters.RequestFilter(req, res, requestDto);
return;
}
Console.WriteLine("Deferring Validation until Roles are checked");
}
}
Configure to use our plugin:
// Configure to use our custom Validation Feature (MyValidationFeature)
Plugins.Add(new MyValidationFeature());
Then we need to create our custom attribute. Your attribute will be different of course. The key thing you need to do is call ValidationFilters.RequestFilter(req, res, requestDto); if you are satisfied the user has the required role and meets your conditions.
public class MyRoleAttribute : RequestFilterAttribute
{
readonly string[] _roles;
public MyRoleAttribute(params string[] roles)
{
_roles = roles;
}
#region implemented abstract members of RequestFilterAttribute
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
Console.WriteLine("Checking for required role");
// Replace with your actual role checking code
var role = req.GetParam("role");
if(role == null || !_roles.Contains(role))
throw HttpError.Unauthorized("You don't have the correct role");
Console.WriteLine("Has required role");
// Perform the deferred validation
Console.WriteLine("Running Validation");
ValidationFilters.RequestFilter(req, res, requestDto);
}
#endregion
}
For this to work we need to apply our custom attribute on the DTO route not the action method. So this will be slightly different to how you are doing it now, but should still be flexible.
[Route("/HaveChristmas", "GET")]
[MyRole("Santa","Rudolph","MrsClaus")] // Notice our custom MyRole attribute.
public class HaveChristmasRequest {}
[Route("/EasterEgg", "GET")]
[MyRole("Easterbunny")]
public class GetEasterEggRequest {}
[Route("/EinsteinsBirthday", "GET")]
public class EinsteinsBirthdayRequest {}
Then your service would look something like this:
public class TestController : Service
{
// Roles: Santa, Rudolph, MrsClaus
public object Get(HaveChristmasRequest request)
{
return new { Presents = "Toy Car, Teddy Bear, Xbox" };
}
// Roles: Easterbunny
public object Get(GetEasterEggRequest request)
{
return new { EasterEgg = "Chocolate" };
}
// No roles required
public object Get(EinsteinsBirthdayRequest request)
{
return new { Birthdate = new DateTime(1879, 3, 14) };
}
}
So when we call the route /EinsteinsBirthday which does not have a MyRole attribute the validation will be called normally, as if using the standard ValidationFeature.
If we call the route /HaveChristmas?role=Santa then our validation plugin will determine that the DTO has our attribute and not run. Then our attribute filter triggers and it will trigger the validation to run. Thus the order is correct.

MVC - How should a service layer to communicate with the controller

I have been using the following pattern for my controller actions:
public ActionResult Create(CreateViewModel model) {
if( !ModelState.IsValid ) {
return View(model);
}
var project = new Project {
Name = model.Name,
// ...
};
projectRepository.Add(project);
return RedirectToAction("Index");
}
This works for simple scenarios, but I have had a few situations where a repository is not enough. I created a service layer / class that will handle saving the project and any extra business logic (not normal validations with fluent validation or data annotations).
public class ProjectService : IProjectService {
void AddProject(Project project) {
// do business logic
// ...
repository.Add(project);
}
}
How can my service layer easily communicate with my controller?
These are the types of things I would like to communicate to the controller:
Business Logic / Validation errors
Database Failures (failed to save etc.)
How can I do this without just returning true/false or status codes from the service layer?
Be careful if you choose exceptions, these are expensive. It gives your controller code extra nesting too, depending on how many exceptions may be thrown. You should really only throw an exception for an exceptional condition, not something that should be handled by the normal flow of your application.
I would go with the other route Wouter de Kort suggested, use the return type of the service for a messaging object. You can key a return message object on a simple enum with the various cases the service may encounter. These look better in the controller because you can handle the enum with a switch/case rather than a try/catch.
Update
What a messaging object may look like:
public interface IServiceAbc
{
ServiceResponse InvokeMyService([params]);
}
public enum ResponseScenario
{
Success,
DatabaseFailed,
BusinessRuleViolated,
ValidationRuleViolated
}
public class ServiceResponse
{
public ResponseScenario Scenario { get; internal set; }
public string Message { get; internal set; }
}
If you want to return detailed messages when an error occurs you could always use Exceptions. Maybe define your own with specific details or reuse the ones that are already in the .NET Framework.
If that´s not an option you could always return a wrapper class which could contain more detailed error information and handle that in the Controller.

Categories