I'm in the middle of re-factoring the project I'm working on. In my existing controllers I do use the repository pattern, but I was still performing a little too much scaffolding than I felt comfortable with. That and some of my controllers could have 10+ repositories passed in (via Ninject). So, I decided to introduce a service layer where my intention is to have one service per controller and each service will instead have the multiple repositories injected into it and do the work I need. This works great so far, but I'm running into a confusion of sorts: How do I move the model validation away from the controller and into the service layer?
For example, take a look this Edit method on my OfficesController:
[HttpPost]
public async Task<RedirectToRouteResult> Edit(
short id,
FormCollection form,
[Bind(Prefix = "Office.Coordinates", Include = "Latitude,Longitude")] Coordinate[] coordinates) {
if (id > 0) {
Office office = await this.OfficesService.GetOfficeAsync(id);
if ((office != null)
&& base.TryUpdateModel(office, "Office", new string[2] {
"Name",
"RegionId"
}, form)
&& base.ModelState.IsValid) {
this.OfficesService.UpdateOfficeAsync(office, coordinates);
}
return base.RedirectToAction("Edit", new {
id = id
});
}
return base.RedirectToAction("Default");
}
The problem with it in comparison to the methods of the controller is that I still grab an Office object from the database, do the update, validate it, and then save it again. In this case the complexity increased rather than decrease. Before, I called the repository in the method, now I call the service which calls the repository to perform the same function. So far this increase in complexity has only show it self in my Edit methods, everywhere else the complexity decreased substantially, which is what I want.
So, what would be a proper way move validation, and now that I think about it, the model updating logic out of the controller and into the service? Recommendations are appreciated!
For reference, here's how my project is structured:
Data: Contains all of my model classes
Data.Google.Maps: Contains all classes I need to deserialize a specific Kml
Data.Models: Contains my DbContext, configurations, view models and partial view models
Data.Repositories: Contains all of my repositories that talk to the DbContext. Since EF is a pseudo repository on it's own, I'm leveraging my "repositories" as a more specific way of querying for data. For Example: FindTechnicians() or FindActive(), etc.
Data.Services: Contains all of the services I will use. The services will have one or more repository injected into them and perform all of logic I need done before I pass a completed view model back to the controller.
Identity: Contains my implementation of ASP.NET Identity.
Web.Private: Contains the actual MVC project.
Here are 2 articles you should read if you haven't already:
https://cuttingedge.it/blogs/steven/pivot/entry.php?id=91
https://cuttingedge.it/blogs/steven/pivot/entry.php?id=92
The answers to your problem are FluentValidation.NET and dependency decoration.
With it, you could do something like this:
private readonly IExecuteCommands _commands;
[HttpPost]
public async Task<RedirectToRouteResult> Edit(short id, UpdateOffice command) {
// with FV.NET plugged in, if your command validator fails,
// ModelState will already be invalid
if (!ModelState.IsValid) return View(command);
await _commands.Execute(command);
return RedirectToAction(orWhateverYouDoAfterSuccess);
}
The command is just a plain DTO, like a viewmodel. Might look something like this:
public class UpdateOffice
{
public int OfficeId { get; set; }
public int RegionId { get; set; }
public string Name { get; set; }
}
... and the magic validator:
public class ValidateUpdateOfficeCommand : AbstractValidator<UpdateOffice>
{
public ValidateUpdateOfficeCommand(DbContext dbContext)
{
RuleFor(x => x.OfficeId)
.MustFindOfficeById(dbContext);
RuleFor(x => x.RegionId)
.MustFindRegionById(dbContext);
RuleFor(x => x.Name)
.NotEmpty()
.Length(1, 200)
.MustBeUniqueOfficeName(dbContext, x => x.OfficeId);
}
}
Each of these validation rules will be run before your action method even gets executed, provided you have the validators set up for dependency injection and that you are using the FV MVC validation provider. If there is a validation error, ModelState.IsValid will be false.
You have also just solved the over injection problems in both your controller and (maybe) service layers. You can run any query, execute any command, or validate any object with only 3 interface dependencies.
Related
I've never worked with a .Net Core project before but have a history with .Net including MVC and entity framework. I'm working with a new .Net Core project which has five solution folders, EHA.PROJ.API, EHA.PROJ.DTO,EHA.PROJ.Repository, EHA.PROJ.Repository.Test and EHA.PROJ.Web. The EHA.PROJ.DTO folder has a number of files such as CategoryDTO.cs which looks like this
namespace EHA.PROJ.DTO
{
public class CategoryDescDTO
{
public int CategoryRef { get; set; }
public string CategoryName { get; set; }
}
}
I'm looking to set up a mapping arrangement to get the data from the EHA.PROJ.DTO files to the model files in my models folder in my EHA.PROJ.Web folder. I've been browsing as I've never done anything like this before as I've previously worked with data from a DAL folder using entity framework and connection done through connection strings. I'm guessing that there must be some process to map the data in my dbContext to connect the files in both folders. I did find some information on AutoMapper but was unsure how to implement it.
This arrangement with .Net Core is new to me so if anyone can help with any examples or point me in the right direction I would be grateful.
Your first problem is having your entities in your web project. Right off the bat, you have tight-coupling between the web project and your data layer, which then pretty much negates the point of all your other layers: DTO, repository, etc. You want to move out your entities and context into a true data layer (i.e. a class library project separate from your web project).
Then, you want to decide how far your data layer should extend. If the API is to feed the Website, then you want to actually remove all dependencies on the data layer from the web project. Your DTO project would be shared between the API and Web projects and your API would send/receive your DTOs, mapping back and forth from your entities under the hood.
However, if you're going to do that, then the repository project should just go away entirely. Just have your API work directly with EF and your entities. Your abstraction is the API itself; there is no need for another. The only reason to have the repository layer is if both the API and Web will both directly utilize the repositories, which isn't a very good pattern actually. You'll inevitably end up with a bunch of duplicated logic specific to each project.
Simply, the repository pattern is superfluous when using an ORM like EF. The ORM is your data layer. You're simply using a DAL provided by a third-party, rather than one you created yourself. The repository pattern only makes sense when working directly with SQL using something like ADO.NET directly. Otherwise, get rid of it.
Having an API is enough of an abstraction, if your goal is simply to hide the data layer. The website knows nothing of the underlying data source, and an API is really just a service layer that returns JSON over HTTP rather than object instances directly, i.e. the API is essentially your "repository" layer.
The situation can be improved even further by moving to a microservices-based architecture. With that, you essentially have multiple small, self-contained APIs that work with just one part of your domain or piece of functionality. Each can utilize EF directly, or an entirely different ORM, or even an entirely different stack. You could have APIs build on Node.js or python, etc. The website simply makes requests to the various services to get the data it needs and doesn't know or care how those services actually work.
I have been using Automapper for quite some time in .NET Core projects due to ease of use and built-in dependency injection.
Install from PM:
Install-Package AutoMapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
Register in the Startup.cs, ConfigureServices method:
services.AddAutoMapper(typeof(Startup));
Create a class to keep your mappings, e.g. MappingProfile.cs using Profile from automapper, you can define mappings.
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Operator, OperatorDto>().ReverseMap();
}
}
}
The above mapping tells automapper that Operator can be mapped to OperatorDto and OperatorDto can be mapped to Operator.
In your controller, you can inject an IMapper
private readonly IMapper _mapper;
public OperatorsController(IMapper mapper)
{
_mapper = mapper;
}
and map values like below:
var dto = _mapper.Map<OperatorDto>(op); // Map op object to dto
var op = _mapper.Map<Operator>(dto); // Map dto to op object
Automapper offers custom mappings, should you need it.
While it is very easy to perform mappings with Automapper, you need to learn the framework.
I believe it is worth the effort to learn it as it will save you a lot of time writing mapping code in the future.
This article is a good reference to start: https://buildplease.com/pages/repositories-dto/
My suggestion is to have a DTO assembler that maps your model to the DTO object. So, you start with your DTO class:
namespace EHA.PROJ.DTO
{
public class CategoryDescDTO
{
public int CategoryRef { get; set; }
public string CategoryName { get; set; }
}
}
Then build the assembler:
public class CategoryDescAssembler {
public CategoryDescDTO WriteDto(CategoryDesc categoryDesc) {
var categoryDescDto = new CategoryDescDTO();
categoryDescDto.CategoryRef = categoryDesc.CategoryRef;
categoryDescDto.CategoryName = categoryDesc.CategoryName;
return categoryDescDto;
}
}
Now you implement the service to do all the work required to get the DTO object:
public class CategoryDescService : ICategoryDescService {
private readonly IRepository<CategoryDesc> _categoryDescRepository;
private readonly CategoryDescAssembler _categoryDescAssembler;
public CategoryDescService(IRepository<CategoryDesc> categoryDescRepository, CategoryDescAssembler categoryDescAssembler) {
_categoryDescRepository= categoryDescRepository;
_categoryDescAssembler= categoryDescAssembler;
}
public CategoryDescDTO GetCategoryDesc(int categoryRef) {
var categDesc = _categoryDescRepository.Get(x => x.CategoryRef == categoryRef);
return _categoryDescAssembler.WriteDto(categDesc);
}
}
With the interface looking like this:
public interface ICategoryDescService
{
CategoryDescDTO GetCategoryDesc(int categoryRef);
}
You would then need to add the service to your Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<ICategoryDescService, CategoryDescService>();
}
Now you can call your service from you view controller.
i'm evaluating Breeze.Js for a large enterprise, data oriented, Angular 5 application in order to take advantage of the following features that are missing in the vanilla Angular framework:
client side data store
client side model state tracking
client side model validation rules
bulk data persistence (SaveChanges() method to persist all entities).
For test purposes i've written the following simple BreezeController in my ASP.NET WebApi + EntityFramework server side:
[EnableCors(origins: "*", headers: "*", methods: "*")]
[BreezeController]
public class PeopleController : ApiController
{
private AdventureWorksDbContext db = new AdventureWorksDbContext();
#region "Breeze"
readonly EFContextProvider<AdventureWorksDbContext> _contextProvider =
new EFContextProvider<AdventureWorksDbContext>();
// ~/breeze/todos/Metadata
[HttpGet]
public string Metadata()
{
return System.Text.Encoding.UTF8.GetString(AdventureWorks.WebApi.Properties.Resources.WebApiMetadata);
}
// ~/breeze/todos/Todos
// ~/breeze/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt
[HttpGet]
public IQueryable<PersonDTO> GetPeople()
{
return db.People.ProjectTo<PersonDTO>();
}
// ~/breeze/todos/SaveChanges
[HttpPost]
public SaveResult SaveChanges(Newtonsoft.Json.Linq.JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
#endregion
}
As you can see in my example (it uses AdventureWorks DB) i've done the following modifications:
1) "GetPeople()" endpoint returns a queryable of DTO ("ProjectTo" extension is provided by Automapper). I need to do this in order to shape the model in a usable way for the client, avoid recursions, deep dive in the schema, big fields serialization and so on.
2) "Metadata()" endpoint returns a string resource that represents metadata of the DTO class. I builded it using "PocoMetadata" tool of the "Breeze Tooling Suite" (https://github.com/Breeze/breeze.tooling). This is needed because i can't return the _contextProvider.Metadata() result as long as i'm using DTO's and not EF POCO class.
Now, if in my Angular 5 client i issue an ODATA query like the following i can see that executeQuery() method actually works:
export class BreezeDataStoreComponent implements OnInit {
private _em: EntityManager;
constructor() {
this._em = new EntityManager({
serviceName: 'http://localhost:31328/breeze/People'
});
}
ngOnInit() {
const query = EntityQuery.from('GetPeople')
.where('FirstName', FilterQueryOp.StartsWith, 'F')
.orderBy('LastName', true);
this._em.executeQuery(query).then(res => {
// Here i can get all People instances.
// Now i try to get the first, edit the name and saveChanges.
(res.results[0] as any).FirstName = 'Franklino';
this._em.saveChanges().then(saveResult => {
const test = saveResult.entities;
});
});
}
}
Unfortunately problems comes with SaveChanges().
When the Angular client calls that method, in my server side i get the following error:
System.InvalidOperationException: Sequence contains no matching
element
I think it's due to the fact that i'm calling SaveChanges() over an EF context provider passing a JObject bundle referred to DTO instead of POCO class.
So my question is:
Is it possible to use BreezeJs query and bulk persistence (SaveChanges() method) using DTO's? It's a pretty common need in big data-centric enterprise applications since i think it's a bad practice exposing EF POCOs on WebApi.
should i rely instead over a classic WebApi that respond to the POST\PUT\DELETE HTTP verbs? In that case, how to configure Breeze client in order to contact those endpoints instead of "SaveChanges" when persisting data?
If Breeze is not suitable for this needs are there other technolgies that provides the 4 abovementioned points?
Thank you very much.
To make SaveChanges work with your DTOs, you would need to either
Write your own method to unpack the JObject saveBundle, or
Use the BeforeSaveChanges method to modify the dictionary of DTOs and replace them with entities that EF understands.
Number 2 seems like the better choice. If you do not have a 1:1 match between entities and DTOs, some logic would be required when doing the mapping.
I am trying to inject the ModelState of the controller into my Service layer to add business logic related validation errors (e.g. entry already exists).
For this I created a new IValidationDictionary that gets injected into my service through an Initialize() function. Nothing new at all and judging by my google searches something quite some people do in MVC.
The Controller constructor looks like this:
public AccountController(IAccountService accountService)
{
_accountService = accountService;
_accountService.Initialize(new ValidationDictionary(ModelState));
}
This all works fine and I can add errors in my service. The issue comes when leaving the service again. At that point none of my errors are present in the controller ModelState. After some debugging I found out that the ModelState in the constructor of the controller is not the same as in the Action. At some point it seems to create a new ModelState.
One alternative seems to be to call the Initialize() to inject the ModelState at start of every Action. Before I do that, I wanted to ask if anyone has a more elegant way (as in less to type) of solving this.
Edit:
The IValidationDictionary:
On buisness layer:
public interface IValidationDictionary
{
void AddError(string key, string message);
bool IsValid { get; }
}
In controller:
public class ValidationDictionary : IValidationDictionary
{
private ModelStateDictionary _modelState;
public ValidationDictionary(ModelStateDictionary modelState)
{
_modelState = modelState;
}
public bool IsValid
{
get
{
return _modelState.IsValid;
}
}
public void AddError(string key, string message)
{
_modelState.AddModelError(key, message);
}
}
First and foremost, you shouldn't do that because you will be mixing two things and break separation of concerns and tightly-couple your application.
Tightly coupling
ModelState property is of type ModelStateDictioanry which is a ASP.NET Core specific class. If you use it in your business layer, you create a dependency on ASP.NET Core, making it impossible to reuse your logic anywhere outside of ASP.NET Core, i.e. Background Worker process which is a pure console application because you neither reference ASP.NET Core nor you'll have HttpContext or anything else there.
Separation of concerns
Business validation and input validation are two different things and should be handled differently. ModelStateDictionary is used for input validation, to validate the input passed to your controller. It is not meant to validate business logic or anything like that!
Business validation on the other side is more than just a rough validation of the fields and its patterns. It contains logic and validation may be complex and depend on multiple properties/values as well as of the state of the current object. So for example, values that may pass the input validation may fail in business validation.
So by using both together, you will violate separation of concerns and have a class do more than one thing. This is bad for maintaining code in the long run.
How to work around it?
Convert IDicitionary<string, ModelStateEntry> to a custom validation model/ValidationResult
ValidationResult is defined in System.Components.DataAnnotations` assembly which is not tied to ASP.NET Core but is port of .NET Core/full .NET Framework, so you don't get a dependency on ASP.NET Core and can reuse it in console applications etc. and pass it around in your validation service using i.e. a factory class
public interface IAccoutServiceFactory
{
IAccountService Create(List<ValidationResult> validationResults);
}
// in controller
List<ValidationResult> validationResults = ConvertToValidationResults(ModelState);
IAccountService accountService = accountServiceFactory.Create(
This solves the issue of dependencies, but you still violate separation of concerns, especially if you use the same model in your business layer as you use as controller parameter.
Custom validatior (completely separated)
Its bit more work at the beginning, but your validation will be completely independent and you can change one of it w/o affecting the other one.
For it you can use frameworks like Fluent Validations which may make the validation a bit easier and more managed
You write a custom validator, that will validate the logic of a certain class/model.
The custom validator can be from as simple as writing your own validators per model which may implement such an interface
public interface IValidator<T> where T : class
{
bool TryValidate(T model, out List<ValidationErrorModel> validationResults);
List<ValidationErrorModel> Validate(T model);
}
and wrap this around your validator class
public class ModelValidator : IModelValidator
{
public List<ValidationErrorModel> Validate<T>(T model)
{
// provider is a IServiceProvider
var validator = provider.RequireService(typeof(IValidator<T>));
return validator.Validate(model);
}
}
Custom validatior (validation attribute based)
An alternation of the above, but using the validation attributes as base and custom logic. You can use Validator.TryValidateObject from System.ComponentModel.DataAnnotations to validate a models ValidatorAttributes. Be aware though, that it only will validate the passed models attributes and not of child models.
List<ValidationResult> results = new List<ValidationResult>();
var validationContext = new ValidationContext(model);
if(!Validator.TryValidateObject(model, validateContext, results))
{
// validation failed
}
and then additionally perform custom logic. See this blog post on how to implement child model validation.
Imho the cleanest way to do is a custom validator, as its both separated and decoupled and easily allows you to change logic of a model w/o affecting the validation of other models.
If you are only validating messages (i.e. commands/queries in CQRS) you can use the second approach with the validation attributes.
Thanks to user Ruard, here is how I did it with .NET Core 5.0
In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
...
}
In Controller
AccountService _service;
public AccountController(IActionContextAccessor actionContextAccessor)
{
//this.actionContextAccessor = actionContextAccessor;
_service = new AccountService(actionContextAccessor);
}
In Service Layer class
public class AccountService
{
private readonly IActionContextAccessor _actionContextAccessor;
public AccountService(IActionContextAccessor actionContextAccessor)
{
_actionContextAccessor = actionContextAccessor;
}
public void Login(string emailAddress, string password)
{
_actionContextAccessor.ActionContext.ModelState.AddModelError("Email", "Your error message");
}
}
In Action, you use like
_service.Login(model.Email, model.Password);
if(!ModelState.IsValid)
return View(model);
There is an easier way to access the ModelState.
Configure IActionContextAccessor in startup:
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
And inject it in the service:
public class MyService : IMyService
{
private readonly IActionContextAccessor _actionContextAccessor;
public MyService(IActionContextAccessor actionContextAccessor)
{
_actionContextAccessor = actionContextAccessor;
}
public bool IsValid
{
get
{
return _actionContextAccessor.ActionContext.ModelState.IsValid;
}
}
}
Please note that it is important to keep a reference to IActionContextAccessor (for similar reasons as with IHttpContextAccessor).
A new controller gets created for every request. Depending whether you configured your service as transient or singleton, a new IAccountService is created with every request, or a single instance reused. Since you manage state per request, I assume you have a transient service, that is, a new instance with every request. Once the request is gone, those instances get unreferenced.
I don't know what you mean with leaving the service though. I hope this provides the right input to track down your problem.
I am developing ASP.NET Core application. To keep controllers lean, most of the data manipulation is done in ViewModels. Everything works fine - the two problems, however, are
ViewModels don't have access to ControllerContext information (or I can't figure out how to get it). For example, Session, User and whatever else Controller gets for free.
ViewModels don't accept Dependency Injection (again, or I can't figure out how to pass it along). For example, if I have constructor MyController(ApplicationDbContext db) I get db passed without any problems. However, if I have ComplexViewModel(ApplicationDbContext db) I get null passed in. Obviously, I have exactly the same services.AddDbContext<ApplicationDbContext>() in Startup
Right now I am passing whatever is required from Controller to ViewModel explicitly. But it feels that there should be a better way.
View models are supposed to be simple POCOs to transfer data between the views and action methods. I think it is a bad idea to mix all your business logic (or even data access) to view models. You may consider doing that in services. You can inject this services to your controllers.
For example.
Yo get a User information, you may consider creating a service
public interface IUserService
{
UserDto GetUser(int id);
}
public class UserService : IUserService
{
IUserDataAccess userDataAccess;
public UserService(IUserDataAccess userDataAccess)
{
this.userDataAccess=userDataAccess;
}
public UserDto GetUser(int id)
{
// with this.userDataAccess, get a User and map to UserDto
// to do : return something
}
}
So your controllers will stay lean
public class UserController : Controller
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
this.userService = userService;
}
public ActionResult Details(int id)
{
var userDto= this.userService.GetUser(id);
return View(userDto);
}
}
Now you can have a UserDataAccess which query your data and inject that to the UserService class.
With this approach your view model does not have any idea what data access technology you are using. Imagine tomorrow you decided to ditch EF for performance reason and want to switch to Dapper, you simply need to create a new implementation of your IUserDataAccess called "DapperUserDataAccess" and udpate your DI config registration to use that. No other code change :)
Hi there i am new to the repository pattern. I would like to have feedback on the approach i am following.
Requirement : Build the menu for the user that is currently logged in.
My Solution :
I created a Service, that will be called by the controller to get the menu items.
public interface IApplicationHelperService
{
List<Menu> GetMenuForRoles();
}
The implementation for the service
public class ApplicationHelperService : IApplicationHelperService
{
private readonly IMenuRepository _menuRepository; //this fecthes the entire menu from the datastore
private readonly ICommonService _commonService; //this is a Service that contained common items eg. UserDetails, ApplicationName etc.
public ApplicationHelperService(IMenuRepository menuRepository,ICommonService commonService)
{
this._menuRepository = menuRepository;
this._commonService = commonService;
}
public List<Menu> ApplicationMenu
{
get
{
return _menuRepository.GetMenu(_commonService.ApplicationName);
}
}
List<Menu> IApplicationHelperService.GetMenuForRoles()
{
return ApplicationMenu.Where(p => p.ParentID == null && p.IsInRole(_commonService.CurrentUser.Roles)).OrderBy(p => p.MenuOrder).ToList();
}
}
Then the CommonService (used for common items needed in the Services eg. CurrentUser
public interface ICommonService
{
IUser CurrentUser { get; }
string ApplicationName { get; }
}
On the class the implements the ICommonService, i get the current user using the context, in other words my service layer does not know about the HttpContext, since there is a possibility that this might by used for another type of application in the future. So this way i can handle by Current User differently for all applications, but my Service Layer will not mind.
So what you should give feedback on is, is this approach to inject this kind of common service into all services a good approach or is there another way of doing this, the reason i ask, is at a later stage i will need the current user's details for auditing purposes or whatever reason presents itself.
Hope this makes sense to someone. :-)
We are using a similar approach. The difference is that, we do not have a CommonService object injected into each service.
We are using WCF and we have written an extension to OperationContext to store Username etc. The properties defined in this extension can be accessed using static method calls. It has an advantage over CommonService implementation; since you are employing IOC, there is not direct way to pass parameters into CommonService in each service call. For instance, if you are sending the username on the WCF calls, you need to set the value of CurrentUser in each constructor.
I do not know whether you are planning to use WCF; but the point is that: if you need to pass variables to your CommonService, you will endup with populating this values inside each constructor. If you are not planning to pass variables, then you can just create a base class for your services and force the developers to use this base class.
Also, you should set the lifetime manager of CommonService as UnityPerResolveLifeTimeManager, in order not to create a new instance in each constructor.Otherwise, you may endup with having different instances in each Service.