I got a generic repository - should the repository be able to throw exceptions or should I keep it dumb? If I chose to throw exceptions from it, should the service layer then catch it and throw an exception with a more friendly message before it goes to the Controller?
Pos1: Repository (Throw) => Service(Catch, Throw new) => Controller(Catch)
Pos2: Repository => Service(Throw) => Controller(Catch)
Definitely Option 1.
I would also replace the term "dumb" with "separation of concerns" in your thinking. There is no reason for a Repository to be dumb. It has a job to do and that will involve exceptions.
It will also involve throwing them for two reasons:
To package a real error that has happened for the consuming code.
To throw an exception under given conditions that violate what you want this class to do. These conditions might not involve an exception thrown by the framework and can be solely related to the "intelligence" you want your Repository to have.
The Repository has to involve encapsulating ALL of this intelligence, leaving the calling code to simply need to know how to deal with a limited set of exceptions. Otherwise, your calling code needs to deal with, for example, the full panoply of LINQ exceptions, coupling it to a technology that should be the exclusive domain of the Repository.
So part of the Repositiory's intelligence has to be throwing a well known, but limited set of exceptions related to its specific purpose.
The same reasoning applies to the Service Layer, if you have one. It needs to deal with exceptions in exactly the same way: encapsulating the "intelligence" that is specific to its task. And again, the same happens with the controller. It should interpret the exceptions it receives from the Service Layer (if there is one) according to its own purposes and concerns.
So separation of concerns, but never dumb. Not even Mute: each layer needs to squeal when it has to.
Should the repository be able to throw
exceptions or should I keep it dumb?
Yes - the repository should be able to throw exceptions. Keeping something 'dumb' doesn't mean it's not comletely self-aware :)
The caveat that 'exceptions should be exceptional' still applies - you might find this "Creating More Exceptional Exceptions" article of interest and it's also of relevance to your question.
If I chose to throw exceptions from
it, should the service layer then
catch it and throw an exception with a
more friendly message before it goes
to the Controller?
Generally, I've not re-thrown exceptions although there's merit in doing that. A pragmatic half-way-house would be to do that for exception types which you want to handle gracefully for some reason.
If you're using something like the Ms Ent Libs Application Logging Block you can set a policy (via config) that allows you to control what happens when exceptions occur - re-throwing them or otherwise; so that would be a useful approach to stop hard-coding yourself into a specific result.
Also this might be of interest: Are exceptions really for exceptional errors?
Generally speaking, you should simply return null from your repository if your query returns no data. You can then test for the null in the repository, if you wish, before sending the data to your view.
public ActionResult Details(int id) {
Dinner dinner = dinnerRepository.FindDinner(id);
if (dinner == null)
return View("NotFound");
else
return View("Details", dinner);
}
http://nerddinnerbook.s3.amazonaws.com/Part4.htm
For edits I would let your Data Access Layer or your repository throw an exception, and catch it in the controller, like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
Dinner dinner = dinnerRepository.GetDinner(id);
try {
UpdateModel(dinner);
dinnerRepository.Save();
// Success!
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
catch {
// Old-school data validation from ASP.NET MVC 1.0
foreach (var issue in dinner.GetRuleViolations()) {
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
// Return original view, so user can correct errors.
return View(dinner);
}
}
http://nerddinnerbook.s3.amazonaws.com/Part5.htm
Related
I got an API with a controller and a service, when invoking one of the action in the controller I must apply a validation, this validation need to check the data in DB to validate that is correct.
As far as I can see there are two ways of handling this
1- Validate before calling the "update" to prevent the error
public IActionResult UpdateCustomer(CustomerDto customer)
{
if (!customerService.Validate(customer))
{
return Send400BadRequest();
}
customerService.Update(customer);
return Send200Ok();
}
2- Call the update validate inside and throw an Exception.
public IActionResult UpdateCustomer(CustomerDto customer)
{
customerService.Update(customer);
return Send200Ok();
}
In customer service
public void Update(CustomerDto customer)
{
if (!Validate(customer)
throws new CustomValidationException("Customer is not valid");
//Apply update
}
We already have an ActionFilter to handle the CustomValidationException so it will return a BadRequest.
1)
Pros
+Don't use Exception to use the running flow
Cons
-Controller is more fat, has more decision on each case it will decide which is the output
2)
Pros
+Operation is more atomic, all logic is inside the service.
+Easier to test
+Every use of the method will be validated.
Cons
-Use exception to manage the flow.
Which is a better solution?
I really need arguments to defend one or the other.
If you have a Business Logic Layer and a Service Layer, I prefer to keep all Business Logic Rules including Business Logic Validations in Business Logic Layer and use Service Layer as a wrapper around Business Logic Layer and throw Exception in Business methods.
When deciding about whether to use Exception for Business Validation rules or not, you can consider:
1) It's better that your Business methods be a Unit of Work. They should perform a complete task. So it's better they contain also validation rules. This way you can reuse such Business Logic Layer across different Service Layers or use the same Unit of Work in different methods of the same Service Layer. If you throw a Business Validation Exception in Business Logic Layer, you will not face with risk of forgetting validation or using another validation rule by mistake and each service method / action will perform a single task for you and will be as lightweight as possible.
Think about when you may need to expose a WCF service for some clients or for example if you may use ASP.NET MVC without using WebAPI or iff you want to use the same logic in another Action method in the same WebAPI.
If you put Business Logic validations in Web API controller, when creating WCF service methods or creating MVC Actions or other service methods, you may forget to apply validations or may apply wrong validations with different rules in the new Service Layer.
2) Considering the first benefit, can you return a meaningful value from methods that shows success, failure, or contain suitable information about failure reason in output?
I believe it's not suitable to use out put of method for all these goals. The method output is method output, it should be data in such business application. It should not be sometimes a status, sometimes data or some times message. Throwing exception will solve this problem.
I am going against the other opinions on here and saying that the first method both clearly illustrates what the intent of your method is and if you ever decide to not return a 400 error, its a bit easier to pull that off in scenario #1.
Additionally, some thoughts on exceptions. Exceptions should be exceptional, meaning unexpected events that occur in your code. A company not passing a validation check is not an exception-al event, it either does pass or does not pass. Passing a User object into the ValidateCompany() method should throw an exception.
Here is a good answer on the similar topic of exception throwing. It uses an easy example problem to determine when in that case an exception should be thrown or not.
In regards to "Easier to test" - I don't see how. Your controller will have two tests with any option you choose, a valid company and an invalid company. Your service will have two tests with any option you choose, a valid company and an invalid company (obviously simplifying your service layer a bit here). In any case you want to ensure both your controller action and your service layer can handle an invalid and valid company object.
I would prefer 2 :)
Because I think service might be called from another node not only the asp.net controller so it would be nice for me if all validation logic is handled in the single layer like Service layer.
i think handling exception by using httpresponse message is much better then any one else. Atleast you got the proper error response with proper http response message as output.
public HttpResponseMessage TestException()
{
try
{
//if your code works well
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.ExpectationFailed, ex.ToString());
}
}
I would do it this way:
public IActionResult UpdateCustomer(CustomerDto customer)
{
try
{
consumerService.Update (customer);
}
catch (CustomValidationException)
{
return Send400BadRequest ();
}
return Send200Ok ();
}
And in your CustomerService:
public void Update(CustomerDto customer)
{
if (!Validate(customer))
throw new CustomValidationException("Customer is not valid");
}
This way your service has the validation logic. So any other callers to your service will also have their inputs validated before attempting an update and hence will get proper error messages.
Your controller will not have any validation code. And moreover, having the try-catch block means you can gracefully handle any other errors the Update call might throw. Hope that helps.
Receive a validatable customer model, pass this model to the data service layer, and perform exception
handling in the controller if the data service layer fails.
Customer model
Implement IValidatableObject to trap business logic related errors.
public class CustomerModel : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// example validation
if ( Balance < 100 )
yield return new ValidationResult("Balance >= 100 req.", new [] { nameof(Balance) });
}
public string Name { get; set; }
public double Balance { get; set; }
}
API Controller
Receive a CustomerModel in your public facing UpdateCustomer API method, then reference ModelState to determine if the customer is valid.
public IActionResult UpdateCustomer(CustomerModel customer)
{
if(ModelState.IsValid) // model validation succeeded
{
try
{
customerService.Update(customer);
}
catch (ServiceUpdateException svcEx)
{
// handled failure at the service layer
return Conflict();
}
catch (Exception ex)
{
// unhandled error occured
return InternalServerError(ex);
}
// update succeeded
return Ok();
}
// invalid model state
return BadRequest();
}
Service (Data Layer)
public void Update(Customer customer)
{
//Apply update
try
{
database.Update(customer);
}
catch (DataStorageException dbEx)
{
throw new ServiceUpdateException(dbEx);
}
catch
{
throw;//unknown exception
}
}
I think that you should validate in both the controller and the service, but validate slightly different things. Especially if what starts off as just an api turns into an api, with a mvc site and a wpf admin interface as often happens.
Using Validation Attributes and Model Binding gives the data a quick "once over" check. Did they supply the id for the customer to update? Did they submit foreign key value for a related entity? Is the price between 0 and 1,000,000? If you have a website then you also get client validation out of the box. Crucially there no need to connect to the database at all, as the data is "obviously" wrong.
Validation in the service is also required as you may end up with 3 or 4 applications calling this service, and you want to write the business logic once. Some validation eg. referencing foreign keys can only be done in the database, but there is nothing wrong in throwing an exception. Consumers of your api should have access to the range of valid values, users of the website should be choosing from a drop down etc, so this circumstance should be exceptional
Is there a way to handle 404 (resource not found) generically, or do I have to write logic for each action? A simple example of what I am currently doing:
//single-read
public HttpResponseMessage Get(Guid guid)
{
School school = new School(guid);
if (school == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
//bonus-question: would it be better to instead: throw new HttpResponseException(HttpStatusCode.NotFound);
}
//bonus-bonus-question: what is the benefit of using a typed response?
return Request.CreateResponse<School>(HttpStatusCode.OK, school);
}
If possible, I would like the "not found" logic to be handled elsewhere so that I didn't need to repeat those few lines of checking for each action. For example, I'd like it if it could be reduced to:
//single-read
public HttpResponseMessage Get(Guid guid)
{
School school = new School(guid);
return Request.CreateResponse<School>(HttpStatusCode.OK, school);
}
You can implement an IActionFilter on your controller which will be called everytime an action is about to be executed and also when an action has finished execution. You can then perform your checking logic there. See documentation. You would annotate the controller class itself with your filter and it would be called for all actions.
In terms of the error handling, if you don't throw an exception, then you won't pay the cost of exceptions (which I'd like to pretend to be negligible), but more importantly, you won't allow any exception handlers to kick in and that may be something you actually want. Take a look at IExceptionFilter for example that gets called whenever an exception is thrown. If there is a part of your application that relies on that (for example logging errors), that component won't know that there was an error when you don't throw an exception, so it's a design call.
I have a problem with NHibernate ISession. When I try to persist something wrong into Database (e.g saving an entity with duplicate key on XYZ col) and rolling back Transaction; ISession instance goes to a BROKEN/INVALID state which doesn't persist any record after that, and each time NHibernat throws another exception that tells me about first time issue.
I've used ISession methods like Flush, Clear, Close but my problem exists. Another approach is to request another ISession instance from ISessionFactory but when I use this, another strange problem: illegal attempt to associate a collection with two open sessions.
How to recover an ISession instance without re-requesting another from ISessionFactory?
Thanks in advance :)
You cannot recover the ISession. From documentation:
If the ISession throws an exception you should immediately rollback
the transaction, call ISession.Close() and discard the ISession
instance. Certain methods of ISession will not leave the session in a
consistent state.
Also creating ISession is cheap so there is no reason to try to reuse it. You probably want to have ISession per request if you have MVC application.
You can't, and you shouldn't.
If a transaction fails, you must abort the request and show an error.
The logical corollary is that session exceptions should not be part of your regular flow.
Now, since you're using MVC, here's an example of how error handling might work if you allow errors:
public ActionResult CreateFoo(FooModel model)
{
if (ModelState.IsValid)
{
try
{
SaveThe(model);
TheTransaction.Commit();
return RedirectToAction("Whatever");
}
catch (WhateverTheDuplicateKeyExceptionIs)
{
ModelState.AddModelError("", "Duplicate XYZ");
}
}
return View();
}
Just curious on your thoughts or experiences around service layer validation.
I have to process fairly standard validation such as "object with name property doesn't already exist", but I wasn't sure how to return these validation failures back to the controller.
My initial thought was to implement a standard List<ValidationError> but I've seen it done each and every way so was curious the pros/cons of each.
Thanks for any input.
If you go with System.ComponentModel.DataAnnotations entries you can (as you seem to know) decorate your properties with required and many more tags
public class Person
{
[Required(ErrorMessage="object with name property doesn't already exist")]
public string Name { get; set; }
}
although I personally use ViewModels rather than exposing domain mdoels to the view, your controller action can now do something like:
[HttpPost]
public ActionResult SavePerson(Person model)
{
if (ModelState.IsValid)
{
// your model validates - do things
return RedirectToAction("success view here");
}
return View(model);
}
This is one of the standard 'post' handler patterns in MVC. This is the simplest path to getting your object model validating in my opinion.
From there, there are a few other options - your domain object can implement IValidatedableObject and you can yield return the errors (see http://buildstarted.com/2010/09/20/mvc-3s-ivalidatableobject/ as an example).
I'd recommend not mixing the two though, as if you are using dataannotations and have even a single invalid property, the IsValid method on IValidatableObject will not be called.
From there, there's lots you can do with custom validation attributes (the extended version of IsValid seems to give you more flexibility http://msdn.microsoft.com/en-us/library/gg480674%28v=vs.98%29.aspx)
Hope some of the above helps - once you get past the basics there's a lot you can do with it and things like client validation of custom attributes etc. are all fun.
Cheers,
Terry
[edit to add:
After re-reading your post, it may be that you want to only validate at the service layer? If so, I've used the following approach:
public void Setname(string newName)
{
Validator.ValidateProperty(newName, new ValidationContext(this, null, null) { MemberName = "Name" });
Name = newName;
}
obviously your Name property would need a { get; private set; } for this, though you could always add the Validator.ValidateProperty into an extended setter for the public property either.
]
On a new project I'm working on (first time mvc) I've been using the ms code contracts (which throw exceptions) and do all the validation on my domain objects themselves. For things that can't be validated there (such as validations that require database access) I validate in my services and throw exceptions. Additionally like the poster above I have whole separate view models for everything that have data annotations validators on them. The exceptions bubble up and I catch them in the controller and append to the ModelState. There's a lot of overlap with those and the view model validation but it's not much extra effort and allows me to vary the validation per view and yet still have the "core" validations be required.
The book pro asp mvc 2 has another nice way - write a class that inherits Exception and contains a collection of errors. Then you do your validations, add to the collection then throw the exception then he catches it in the controller and copies over to ModelState. This method will let you catch ALL the errors in one exception instead of just one at the service layer.
I have a website where I allow users to create new Part records. I'm trying to figure out the best way to validate specific fields for uniqueness. I want to make sure that somebody doesn't try to add a Part with PartNumber 1234 if that PartNumber already exists on a different Part.
The Web Application is using Asp.net MVC with fluent nHibernate for mapping my objects to the database. I'm using Castle validation on my view models for things like ValidateNonEmpty, ValidateRange, etc. Should I use the ValidateSelf method to query the repository to see if that part number already exists? Something doesn't feel right about using my Repository on the ViewModel.
Would it be better for me to place that logic on the controller action? That doesn't seem right because I expect my ViewModel to already be Validated at the point (during ModelBind).
Or maybe its none of the above. Thanks for any help on this one.
UPDATE
Ok, not sure if this will help, but here is what my Save action looks like for a typical Create Action in my project:
public ActionResult Create(PartViewModel viewModel)
{
//I think I'd like to know if its Valid by this point, not on _repository.Save
if(ModelState.IsValid)
{
try
{
var part = _partCreateViewModelMap.MapToEntity(viewModel);
_repository.Save(part);
return Redirect("~/Part/Details/" + part.Id);
}
catch (Exception e)
{
// skip on down...
}
}
// return view to edit
return View(viewModel);
}
I have been asked this question many times. My friends were worried about whether they can perform data access from the validator code. The answer is simple. If you need to do this, you should do it. Usually we need to do such checks at each level of abstraction. And after all checks you should be ready to catch an exception, caused by unique constraint violation.
If you define a unique constraint within the database, then why not delegate the responsibility to check for whether a unique value already exists to the database? Using NHibernate, you can use the NHibernate.Exceptions.ISQLExceptionConverter interface to capture and transform known errors relating to constraint violations. You can also use NHibernate.Exceptions.IViolatedConstraintNameExtracter implementers (see NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter) to get at the grubby details of your database exception, and transform it into a user-friendly message, repackage as a validation exception of your chosing and catch it in the relevant controller.
Example of a quick, very specific quick and dirty exception converter from one of my projects:
Imports NHibernate
Imports NHibernate.Exceptions
Imports System.Data.SqlClient
Imports System.Data.Common
Namespace NHibernate
Public Class ConstraintViolationExceptionConverter
Implements ISQLExceptionConverter
Public Function Convert(ByVal adoExceptionContextInfo As Global.NHibernate.Exceptions.AdoExceptionContextInfo) As System.Exception Implements Global.NHibernate.Exceptions.ISQLExceptionConverter.Convert
Dim dbEx As DbException = ADOExceptionHelper.ExtractDbException(adoExceptionContextInfo.SqlException)
If TypeOf dbEx Is SqlException Then
Dim sqlError As SqlException = DirectCast(dbEx, SqlException)
Select Case sqlError.Number
Case 547
Return New ConstraintViolationException(adoExceptionContextInfo.Message, adoExceptionContextInfo.SqlException)
End Select
End If
Return SQLStateConverter.HandledNonSpecificException(adoExceptionContextInfo.SqlException, adoExceptionContextInfo.Message, adoExceptionContextInfo.Sql)
End Function
End Class
End Namespace
Configured through the web.config/nhibernate-configuration/session-factory property element:
<property name="sql_exception_converter">csl.NHibernate.ConstraintViolationExceptionConverter, csl</property>
Edit: Should probably mention that the converter interface has changed in recent versions of NHibernate, the interface from this example is from NHibernate.dll v2.1.0.4000
I typically put a Service layer between my controllers and repositories.
The service layer would then handle the validation and calls to the repository.
Then, if there's a validation error in the service layer, I throw a custom exception, catch it in the controller, and inject the errors in to the model state.
I have no answer for your question but you can check sharparchitecture.net site. It contains some best practives for asp.net mvc and nhibernate. Also I can recommend you to check xval project and tutorials about Validating with Data Annotation Validators
I have found the solution that works for me is to
1.) Ask if the entity is valid to execute your validation work.
2.) After this is complete you should have something on your object to show it's valid or not (in my case I use a CSLA like concept of "broken rules").
3.) If you have something like this you can verify the object is valid before NHibernate tries to persist it as shown below.
The only issue with this approach is that you do need to implement an interface on each entity requiring validation. If you can live with this it will stop NHibernate from persisting the changes of an object that's not valid according to your rules.
using System;
using NHibernate;
using NHibernate.Event;
using Validation.Entities.Interfaces;
using Persistence.SessionBuilder;
namespace Persistence.Validation
{
public class ValidationEventListener : IPreInsertEventListener, IPreUpdateEventListener
{
public bool OnPreInsert(NHibernate.Event.PreInsertEvent #event)
{
var entityToInsert = #event.Entity as IBusinessBase;
if (entityToInsert != null)
{
if (entityToInsert.BrokenRules != null)
{
RollbackTransactionBecauseTheEntityHasBrokenRules();
}
}
return false;
}
public bool OnPreUpdate(NHibernate.Event.PreUpdateEvent #event)
{
var entityToUpdate = #event.Entity as IBusinessBase;
if (entityToUpdate != null)
{
if (entityToUpdate.BrokenRules != null)
{
RollbackTransactionBecauseTheEntityHasBrokenRules();
}
}
return false;
}
private void RollbackTransactionBecauseTheEntityHasBrokenRules()
{
try
{
ISession session = SessionBuilderFactory.GetBuilder().CurrentSession;
if (session != null)
{
session.Transaction.Rollback();
}
}
catch (Exception ex)
{
//this will force a rollback if we don't have a session bound to the current context
throw new NotImplementedException();
}
}
}
}
I would say this matters on your architecture. With MVC apps that I have done in the past we abstract away the domain stuff away from the web stuff and naturally we use dependency injection to avoid hard dependencies.
When it comes to validating the model when you are in the act of binding it, yes you could easily use the service, repository, or whatever you have next in your architecture in a ValidateSelf method. I think the question rises of what about that dependency.
If I remember correctly you can create your own custom binder that will use your dependency injection framework to plug-in any services your model needs for validation when you create it, call MVC's default binder to fill in the object, then call into Castle Validation's framework to do the validation. This isn't a fully thought solution, but hopefully it provokes some ideas.