I am trying to figure out the best solution for getting error messages in between my service layer and WebApi controllers.
I have a class ModelStateDictionaryWrapper that implements an interface IValidationDictionary
ModelStateDictionaryWrapper
public class ModelStateDictionaryWrapper : IValidationDictionary
{
private readonly ModelStateDictionary modelStateDictionary;
public bool IsValid
{
get
{
return this.modelStateDictionary.IsValid;
}
}
public ModelStateDictionaryWrapper(ModelStateDictionary modelStateDictionary)
{
Enforce.ArgumentNotNull(modelStateDictionary, "modelStateDictionary");
this.modelStateDictionary = modelStateDictionary;
}
public void AddError(string key, string message)
{
this.modelStateDictionary.AddModelError(key, message);
}
}
IValidationDictionary
public interface IValidationDictionary
{
bool IsValid { get; }
void AddError(string key, string message);
}
In my api controller, I am doing this:
public class CategoryController : ControllerBase<ICategoryService>
{
private ICategoryService categoryService;
public CategoryController(ICategoryService categoryService)
{
this.categoryService = categoryService;
this.categoryService.ValidationDictionary =
new ModelStateDictionaryWrapper(this.ModelState);
}
public IEnumerable<CategoryViewModel> Get()
{
return Mapper.Map<CategoryViewModel[]>(this.Service.GetCategories());
}
}
The problem I have with this is I am making a new ModelStateDictionaryWrapper in the constructor of the service and I dont like that.
So I was thinking of changing this to take a factory like so:
public interface IModelStateWrapperFactory
{
IValidationDictionary GetModelStateWrapper(ModelStateDictionary modelStateDictionary);
}
public class ModelStateWrapperFactory : IModelStateWrapperFactory
{
public IValidationDictionary GetModelStateWrapper(
ModelStateDictionary modelStateDictionary)
{
return new ModelStateDictionaryWrapper(modelStateDictionary);
}
}
And then the api controller would look like this (constructor):
public CategoryController(ICategoryService categoryService,
IModelStateWrapperFactory modelStateWrapperFactory)
{
this.categoryService = categoryService;
this.categoryService.ValidationDictionary =
modelStateWrapperFactory.GetModelStateWrapper(this.ModelState);
}
I think I have removed the tight coupling. Does this look like a good solution?
Yes,
You have broken the dependencies between the classes, so you can mock the services during Unit Testing.
I don't know if you have used data annotations and a validation filter or not yet. If not, I would suggest you use them. More details from here http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api
An even better approach would be to completely remove this part out of the controller. It should be moved out of the controller, because:
This is effectively a cross-cutting concern and your controller should not be concerned with it; you are violating the Single Responsibility Principle.
Most (if not all) of your controllers will need this construct, which means that you have to repeat it all over the place, making it easy to forget it at some places; you are violating the Don't Repeat Yourself (DRY) principle.
This construct is only possible in the case that the class that needs validation is directly injected into the controller, which might not always be the case. Sometimes you'll need to do validation deeper down the object graph, or you might want to wrap the service with a decorator or interceptor, rendering this approach useless -or at least- extremely troublesome.
There are several solutions to this approach. The first that comes to my mind is to move the setting of the ModelState up, out of the CategoryController's constructor, for instance:
public IHttpController Create(HttpRequestMessage request,
HttpControllerDescriptor descriptor, Type type)
{
var wrapper = new ModelStateDictionaryWrapper();
var controller = new CategoryController(new CategoryService(wrapper));
wrapper.ModelState = controller.ModelState;
return controller;
}
Another -completely different- approach is to to not use the ModelState property at all, but to let your business layer throw specific validation exceptions and catch them higher up the call stack and transform them to Web API status codes.
Throwing exceptions would be a much better approach for the business layer, since this prevents validation errors to go unnoticed. Besides, a design where you fill a dictionary with validation errors is related to Web API and MVC, and is not something that your business layer should be concerned with.
You can do the following in your controller when your BL throws validation exceptions:
public class CategoryController : ControllerBase<ICategoryService>
{
private ICategoryService categoryService;
public CategoryController(ICategoryService categoryService)
{
this.categoryService = categoryService;
}
public HttpResponseMessage Update(CategoryViewModel model)
{
try
{
this.categoryService.Update(model.Category);
}
catch (ValidationException ex)
{
return WebApiValidationHelper.ToResponseCode(ex);
}
}
}
Downside here is of course that your try-catch statements with the calls to the WebApiValidationHelper.ToResponseCode will be duplicated in all your controllers; you'll be violating DRY.
So what you can do instead is extract this code into an DelegatingHandler. My preference would always be to use decorators, but unfortunately Web API makes it impossible to decorate ApiControllers, due to a quirk in its design. So you can inject the following DelegatingHandler into the Web API pipeline:
public class ValidationDelegationHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
return await base.SendAsync(request, cancellationToken);
}
catch (ValidationException ex)
{
return WebApiValidationHelper.ToResponseCode(ex);
}
}
}
This handler can be injected as follows:
config.MessageHandlers.Add(new ValidationDelegationHandler());
Related
We have an ASP.Net MVC application for our online store. User has to choose from multiple payment methods in order to buy something. For this we have implemented an abstract factory pattern:
public interface IPaymentServiceFactory
{
IPaymentService GetPaymentService(PaymentServiceEnum paymentServiceType);
}
public interface IPaymentService
{
PaymentSettingsModel GetPaymentSettingsModel();
}
It is used in our Action:
public ActionResult ProcessCart(PaymentDataModel paymentData)
{
var paymentService = _paymentServiceFactory.GetPaymentService(paymentData.PaymentServiceType);
var paymentSettings = paymentService.GetPaymentSettingsModel();
}
The problem occurs when we understand that some payment methods require async calls inside. For example 3rd party online payment service method must be asynchronously called through http for creating payment object on their side. The implemetation:
public class OnlinePaymentService : IPaymentService
{
private readonly IOnlinePaymentServiceApiClient _client;
public async Task<PaymentSettingsModel> GetPaymentSettings()
{
var result = await _client.CreatePaymentAsync();
return result;
}
}
So we come up with a question: How to handle async and sync scenario for different payment methods. We`v decided to make everything async. Updated code:
public interface IPaymentService
{
Task<PaymentSettingsModel> GetPaymentSettings();
}
public async Task<ActionResult> ProcessCart(PaymentDataModel paymentData)
{
var paymentService = _paymentServiceFactory.GetPaymentService(paymentData.PaymentServiceType);
var paymentSettings = await paymentService.GetPaymentSettingsModel();
}
So far so good, but for implementing this for all other payment methods we were forced to use Task.Run:
public class CashPaymentService : IPaymentService
{
public async Task<PaymentSettingsModel> GetPaymentSettings()
{
return await Task.Run(() => new PaymentSettingsModel());;
}
}
As i can understand this creates two different threads for processing Action, which can cause performance issue.
Is there way to avoid such consequences? Is it really so bad to use Task.Run in particular case?
Is it really so bad to use Task.Run in particular case?
Yes, mainly because it's unnecessarily complicating things.
You can return a completed task whose result is a given value using Task.FromResult.
This is completely synchronous:
public class CashPaymentService : IPaymentService
{
public Task<PaymentSettingsModel> GetPaymentSettings()
{
return Task.FromResult( new PaymentSettingsModel() );
}
}
Note that async is missing here - that's possible because it is an implementation detail and not part of the definition of IPaymentService.
I have a Controllerclass which makes use of a TodoRepositoryclass via DI that implements an interface ITodoRepository.
The interface:
public interface ITodoRepository
{
public bool ValidateTodo(Todo todo);
}
The repository class:
public class TodoRepository : ITodoRepository
{
public bool ValidateTodo(Todo todo)
{
//some validation
return false;
}
}
The Controller:
public TodoController : BaseController
{
private readonly ITodoRepository _todoRepository;
private const string INVALID_TODO_MESSAGE = "Invalid todo.";
public TodoController(ITodoRepository todoRepository)
{
_todoRepository = todoRepository;
}
public IActionResult Post(Todo todo)
{
if(!_todoRepository.ValidateTodo(todo))
{
return new JsonResult(INVALID_TODO_MESSAGE);
}
}
}
To be able to use INVALID_TODO_MESSAGE in every Controller that makes use of TodoRepository I want to move it to TodoRepository, but that doesn't seem to be possible. Some other solution is to create a readonly property in ITodoRepository, and implement it in TodoRepository, but I am not sure if that is the best way to go.
You could create a helper class, next to the interface:
public static class TodoConstants
{
public const string INVALID_TODO_MESSAGE = "Invalid todo.";
}
You can use it everywhere as TodoConstants.INVALID_TODO_MESSAGE.
DISCLAIMER: This is not a direct answer to the question, but rather a design suggestion for error handling in web applications.
I would implement such a validation using Exceptions. Typically you could throw business exceptions from your application layer (possibly including repository too). Then at the web layer (aka. controllers, filters, etc.) you could implement an ExceptionFilter to handle business exceptions, for example turning them into a special JsonResult.
This way you'll have a good separation of concerns and centralized error handling through the application.
See this documentation for ASP.NET WebApi (it's almost the same for MVC too) https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling
Some untested pseudo code would look like this.
public class TodoRepository : ITodoRepository
{
public bool ValidateTodo(Todo todo)
{
//some validation
throw new ValidationException("Invalid todo");
}
}
public class MyBusinessExceptionFilterAttribute : ExceptionFilterAttribute
{
public void OnException(ExceptionContext filterContext)
{
...
filterContext.Result = new JsonResult(filterContext.Exception.Message);
...
}
}
public static void ApplicationStartup() {
...
GlobalFilters.Filters.Add(new MyBusinessExceptionFilterAttribute());
...
}
public IActionResult Post(Todo todo)
{
// Just let the exception to be thrown in case of business errors
_todoRepository.ValidateTodo(todo);
}
I have two controllers that have few same methods:
public class Controller1 : Controller
{
private readonly ITestBL bl;
public Controller1(ITestBL bl)
{
this.bl= bl;
}
[HttpGet]
public ActionResult Method1(string data)
{
using (bl)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
[HttpGet]
public ActionResult Method2(string data, int data2)
{
using (bl)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
// other methods
}
And the second controller also has those two methods.
Should I create some common controller to keep those methods? So, it will look like this:
public abstract class CommonController: Controller
{
private readonly ITestBL bl;
protected Controller1(ITestBL bl)
{
this.bl= bl;
}
[HttpGet]
public ActionResult Method1(string data)
{
using (bl)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
[HttpGet]
public ActionResult Method2(string data, int data2)
{
using (bl)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
}
And my Controller1 and Controller2 will be:
public class Controller1 : CommonController
{
private readonly ITestBL bl;
public Controller1(ITestBL bl)
:base(bl)
{
}
// methods
}
Is that the proper way to do that? Do I miss anything or is there a better way?
Should same methods from different controllers be moved to a CommonController?
Yes and you should not use Inheritance. I'm sure there are plenty of people who may disagree, however your example is extremely generic and gives very poor context there is no good reason all controllers need the same code (Inheritance or not). Your questions context has no reason for it to be the case in the OOP realm of Has A vs Is A (Excerpt below).
A House is a Building (inheritance);
A House has a Room (composition);
What it appears you are doing is neither of these.
If your interface was IVehicleEngine and your controllers were FerarriVehicleController and FordVehicleController now it makes sense in a context. In this case each controller should use inheritance, it makes sense.
In my own humble opinions, inheriting from your own controller can be quite difficult in multiple terms. First, it's not intuitive; that is it will become tribal knowledge because it replaces a normal convention that most programmers adhere to (that is deriving from the base MVC controller). Secondly, I've seen it become the one place that everyone decides to add code to (God Object) even though it may not be applicable from some controllers. Thirdly, it makes it difficult to reuse url's that make sense for the derived type but not the base type (/search?searchFor=). There are a number of other considerations that are very specific to MVC because of it's exposure to the web (security etc etc).
Depending on implementation you may also experience difficulty in determining which URL to use under what circumstances.
Is /Controller1/Method1/Data/1 the same as /Controller2/Method1/Data/1 but different from /Controller3/Method1/Data/1? If they are all the same or some are the same and some are different then there is most likely something wrong with the architecture.
Nothing wrong with inheriting from a base controller. As it adheres to the DRY principle. I would go with :
public abstract class CommonController: Controller
{
protected readonly ITestBL bl;
protected Controller1(ITestBL bl)
{
this.bl= bl;
}
[HttpGet]
public virtual ActionResult Method1(string data)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public virtual ActionResult Method2(string data, int data2)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
Main differences being.
I removed "using". Like the others have said it's best to let your DI framework decide when to dispose of the injected class.
Make the action results virtual. The inheriting controllers may have the same requirements now, but there is no guarantee that they will stay that way in the future. So declaring them as virtual allows for future changes in scope/requirements as they can be overridden.
I made the injected class "protected" rather then "private" as other methods in both inheriting controllers may need it too.
Our existing database deployment has a single 'master' and a read-only replica. Using ASP.NET's Web API2 and an IoC container I want to create controller actions whose attribute (or lack there of) indicate which database connection is to be used for that request (See Controller and Services usage below)...
public MyController : ApiController
{
public MyController(IService1 service1, IService2 service2) { ... }
// this action just needs the read only connection
// so no special attribute is present
public Foo GetFoo(int id)
{
var foo = this.service1.GetFoo(id);
this.service2.GetSubFoo(foo);
return foo;
}
// This attribute indicates a readwrite db connection is needed
[ReadWrteNeeded]
public Foo PostFoo(Foo foo)
{
var newFoo = this.service1.CreateFoo(foo);
return newFoo;
}
}
public Service1 : IService1
{
// The dbSession instance injected here will be
// based off of the action invoked for this request
public Service1(IDbSession dbSession) { ... }
public Foo GetFoo(int id)
{
return this.dbSession.Query<Foo>(...);
}
public Foo CreateFoo(Foo newFoo)
{
this.dbSession.Insert<Foo>(newFoo);
return newFoo;
}
}
I know how to setup my IoC (structuremap or Autofac) to handle per request IDbSession instances.
However, I'm not sure how I would go about making the type of IDbSession instance for the request to key off the indicator attribute (or lack there of) on the matching controller's action. I assume I will need to create an ActionFilter that will look for the indicator attribute and with that information identify, or create, the correct type of IDbSession (read-only or read-write). But how do I make sure that the created IDbSession's lifecycle is managed by the container? You don't inject instances into the container at runtime, that would be silly. I know Filters are created once at startup (making them singleton-ish) so I can't inject a value into the Filter's ctor.
I thought about creating an IDbSessionFactory that would have 'CreateReadOnlyDbSession' and 'CreateReadWriteDbSession' interfaces, but don't I need the IoC container (and its framework) to create the instance otherwise it can't manage its lifecycle (call dispose when the http request is complete).
Thoughts?
PS During development, I have just been creating a ReadWrite connection for every action, but I really want to avoid that long-term. I could also split out the Services methods into separate read-only and read-write classes, but I'd like to avoid that as well as placing GetFoo and WriteFoo in two different Service implementations just seems a bit wonky.
UPDATE:
I started to use Steven's suggestion of making a DbSessionProxy. That worked, but I was really looking for a pure IoC solution. Having to use HttpContext and/or (in my case) Request.Properties just felt a bit dirty to me. So, if I had to get dirty, I might as well go all the way, right?
For IoC I used Structuremap and WebApi.Structuremap. The latter package sets up a nested container per Http Request plus it allows you to inject the current HttpRequestMessage into a Service (this is important). Here's what I did...
IoC Container Setup:
For<IDbSession>().Use(() => DbSession.ReadOnly()).Named("ReadOnly");
For<IDbSession>().Use(() => DbSession.ReadWrite()).Named("ReadWrite");
For<ISampleService>().Use<SampleService>();
DbAccessAttribute (ActionFilter):
public class DbAccessAttribute : ActionFilterAttribute
{
private readonly DbSessionType dbType;
public DbAccessAttribute(DbSessionType dbType)
{
this.dbType = dbType;
}
public override bool AllowMultiple => false;
public override void OnActionExecuting(HttpActionContext actionContext)
{
var container = (IContainer)actionContext.GetService<IContainer>();
var dbSession = this.dbType == DbSessionType.ReadOnly ?
container.GetInstance<IDbSession>("ReadOnly") :
container.GetInstance<IDbSession>("ReadWrite");
// if this is a ReadWrite HttpRequest start an Request long
// database transaction
if (this.dbType == DbSessionType.ReadWrite)
{
dbSession.Begin();
}
actionContext.Request.Properties["DbSession"] = dbSession;
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var dbSession = (IDbSession)actionExecutedContext.Request.Properties["DbSession"];
if (this.dbType == DbSessionType.ReadWrite)
{
// if we are responding with 'success' commit otherwise rollback
if (actionExecutedContext.Response != null &&
actionExecutedContext.Response.IsSuccessStatusCode &&
actionExecutedContext.Exception == null)
{
dbSession.Commit();
}
else
{
dbSession.Rollback();
}
}
}
}
Updated Service1:
public class Service1: IService1
{
private readonly HttpRequestMessage request;
private IDbSession dbSession;
public SampleService(HttpRequestMessage request)
{
// WARNING: Never attempt to access request.Properties[Constants.RequestProperty.DbSession]
// in the ctor, it won't be set yet.
this.request = request;
}
private IDbSession Db => (IDbSession)request.Properties["DbSession"];
public Foo GetFoo(int id)
{
return this.Db.Query<Foo>(...);
}
public Foo CreateFoo(Foo newFoo)
{
this.Db.Insert<Foo>(newFoo);
return newFoo;
}
}
I assume I will need to create an ActionFilter that will look for the indicator attribute and with that information identify, or create, the correct type of IDbSession (read-only or read-write).
With your current design, I would say an ActionFilter is the way to go. I do think however that a different design would serve you better, which is one where business operations are more explicitly modelled behind a generic abstraction, since you can in that case place the attribute in the business operation, and when you explicitly separate read operations from write operations (CQS/CQRS), you might not even need this attribute at all. But I'll consider this out of scope of your question right now, so that means an ActionFilter is the the way to go for you.
But how do I make sure that the created IDbSession's lifecycle is managed by the container?
The trick is let the ActionFilter store information about which database to use in a request-global value. This allows you to create a proxy implementation for IDbSession that is able to switch between a readable and writable implementation internally, based on this setting.
For instance:
public class ReadWriteSwitchableDbSessionProxy : IDbSession
{
private readonly IDbSession reader;
private readonly IDbSession writer;
public ReadWriteSwitchableDbSessionProxy(
IDbSession reader, IDbSession writer) { ... }
// Session operations
public IQueryable<T> Set<T>() => this.CurrentSession.Set<T>();
private IDbSession CurrentSession
{
get
{
var write = (bool)HttpContext.Current.Items["WritableSession"];
return write ? this.writer : this.reader;
}
}
}
In my current MVC application, I have architected a series of command objects to handle business actions. These business actions would be wrapped around service endpoints. These endpoints would also be consumed by an MVC frond-end & a windows app. Every business action will call into a DAO action, which in turn, calls into the required data access repositories to successfully perform the business action. I have listed an example action below.
Busines Action
public class CreateProjectAction
{
IInsertProjectDAOAction InsertProjectDAOAction { get; set; }
public void Execute()
{
// Does some business validation & other logic before
// calling the DAO action
InsertProjectDAOAction.Execute();
}
}
DAO Action
public interface IInsertProjectDAOAction
{
void Execute();
}
public class InsertProjectDAOAction
{
IProjectRepository ProjectRepository { get; set; }
public void Execute()
{
ProjectRepository.Insert();
}
}
Project Repository
public interface IProjectRepository
{
void Insert(Project proj);
// other db methods would be listed here
}
public class ProjectRepository
{
public void Insert(Project proj)
{
// Insert into the data store
}
}
Controller
[HttpPost]
public IHttpActionResult Create(NewProjectModel newProjectModel)
{
var cmdArgs = Mapper.Map<CreateProjectCommand.CreateProjectCommandArgs>(newProjectModel);
var action = new CreateProjectCommand(UserId, cmdArgs);
action.Execute();
if(action.IsSuccessful)
return Ok(project)
else
return InternalServerError(action.Exception);
}
Unit Test
public void InsertWith_ExistingProjectName_Returns_ServerError()
{
var arg = new CreateProjectCommandArgs(){ .... };
var cmd = CreateProjectAction(args);
action.Execute();
Assert.That(action.IsSuccessful, Is.False);
Assert.That(action.Exception, Is.TypeOf<UniqueNameExcepton>());
}
I am using Ninject to assist with the dependency injection between layers. I have a bunch of unit tests around the business 'CreateProjectAction' to test out expected behavior of that object. The business actions are wrapped around a series of Web API service endpoints. I would also like to write tests around my MVC controllers so that I can be sure they work as planned.
I like the architecure so far, but having trouble figuring out how to mock the DAO action properties in the business action when writing unit tests for the mvc controller. I'd love to hear suggestions, other viewpoints, etc ...
Your question is still a bit unclear. It seems likely for example that InsertProjectDAOAction implements the interface IInsertProjectDAOAction, even though your sample code doesn't indicate that it does. It's also unclear what CreateProjectCommand in your controller example is, since it isn't one of your example elements above it.
That said, one approach that you can take is to defer the creation of your commands out to a factory and inject the factory into your controller (through Ninject in your code and as a Mock in your unit tests). This allows you setup a mock chain. You mock the factory and have it return a mock of your action that you're interested in, which you can then setup to do whatever you want. At a very basic level, this might look like this:
public interface ICommandFactory {
IInsertProjectDAOAction CreateInsertProjectAction(int userId);
}
public class CommandFactory : ICommandFactory{
public IInsertProjectDAOAction CreateInsertProjectAction(int userId) {
return new InsertProjectDAOAction(/* userId???? */);
}
}
The controller would do something like this to use the factory:
public IHttpActionResult Create(/* ... */) {
var action = _commandFactory.CreateInsertProjectAction(1234);
action.Execute();
// ...
}
With a test looking something like:
[Test]
public void MyTest() {
var factoryMock = new Mock<ICommandFactory>();
var commandMock = new Mock<IInsertProjectDAOAction>();
factoryMock.Setup(x => x.CreateInsertProjectAction(It.IsAny<int>())).Returns(commandMock.Object);
commandMock.Setup(x => x.Execute()).Throws(new InvalidOperationException("Random failure"));
var controller = new MyController(factoryMock.Object);
try {
controller.Create(/* ... */);
Assert.Fail();
}
catch (InvalidOperationException ex) {
Assert.AreEqual("Random failure", ex.Message);
}
}
This is a general approach that you could take. However, as I've said, that might not be right for your situation, because your question is unclear. I've also ignored other issues about how you create / test your controller in general since that doesn't seem to be what your question is about...