MVC - Controller to Service Layer Communication - c#

In my ASP.net mvc app I am using a Service Layer and Repositories to keep my controllers thin. A typical details read only view looks like this:
public ActionResult Details(int id)
{
var project = _projectService.GetById(id);
return View(Mapper.Map<Project, ProjectDetails>(project));
}
Service Layer:
public class ProjectService : IProjectService
{
public Project GetById(int id)
{
var project = _projectRepository.GetProject(id);
// do some stuff
return project;
}
}
public class ProjectRepository : IProjectRepository
{
public Project GetProject(int id)
{
return context.Projects.Find(id);
}
}
Moving from the service layer to the view model is pretty easy because of automapper, which can flatten things pretty easily. Moving the other direct, from the view model to pass into my service layer is where I struggle to come up with a good solution.
In a situation like a Create action, what is a good approach for this?
[HttpPost]
public ActionResult Create(CreateProjectViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
// TODO
return RedirectToAction("Index");
}
I'm pretty sure that the service layer should not know anything about view models, but I also don't think that AutoMapper works well in this scenario either, since it's not good at taking a flat model and making it into a complex object.
What should my controller look like to communicate with the service layer? I want to keep the code in the controller as light as possible.

You could define a bidirectional mapping and then go the other way around:
[HttpPost]
public ActionResult Create(CreateProjectViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
Project project = Mapper.Map<CreateProjectViewModel, Project>(model);
// pass the project entity to your service layer
_projectService.Create(project);
return RedirectToAction("Index");
}
or if you are updating an entity you might first want to fetch the existing entity that you want to update from the service:
[HttpPost]
public ActionResult Update(CreateProjectViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
Project project = _projectService.GetById(model.Id);
Mapper.Map<CreateProjectViewModel, Project>(model, project);
// pass the project entity to your service layer
_projectService.Update(project);
return RedirectToAction("Index");
}

The only way I have seen this done so far is to manually create a bunch of model transformation classes, for example:
public interface ITransformer<out To, in From>
where To : class
{
To Transform(From instance);
}
public class SomeDataToSomeViewModelTransformer : ITransformer<SomeViewModel, SomeDataModel>
{
public SomeViewModel Transform(SomeDataModel instance)
{
return new SomeViewModel
{
InvitationId = instance.Id,
Email = instance.EmailAddress,
GroupId = instance.Group.Id
};
}
}
And another Transformer implementation to go back the other way (ViewModel -> DataModel).
And having the Controller know to call the correct transformer.
I +1 your question because I would love to see a nice clean way to do this too, without having to manually write a bunch of code to map models.

If your service layer is solely dedicated to support your MVC application and no other clients you could consider using the objects passed through and from your service layer as part of your viewmodels. This would obviate the need to automap the inbound calls as you'd be sending in the actual objects required from the controller.
You could also consider not having the services return domain objects, this would mean that the automapping should be invoked with the service methods rather than the controller actions.

Related

ASP.NET MVC - What's the best way to store objects on POST/PUT (Architecture)

Using asp net mvc what's the best practice for creating an action that allows to create a new project whose owner is the current logged user?
// Entities
class User {
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
class Project {
[Key]
public int Id { get; set; }
public string Name { get; set; }
[Required]
public int OwnerId;
[ForeignKey("OwnerId")]
public User Owner? { get; set; }
}
// DTO / ModeView
class ProjectModelView {
public string Name;
}
class ProjectController : Controller {
private readonly ApplicationDbContext _context;
public ProjectController(ApplicationDbContext context) {
_context = context;
}
public IActionResult Create([Bind("Name")] Project project) {
return View();
}
// 1. Using the model directly
[HttpPost]
public IActionResult Create([Bind("Name")] Project project) {
project.Owner = Session.UserId;
if (ModelState.IsValid) {
_context.Projects.Add(project);
_context.SaveChanges();
return RedirectToAction(actionName: "Index");
}
return View(project);
}
// 2. Using a dto/model view (not sure if this is considerer a model view in this case)
[HttpPost]
public IActionResult Create(ProjectModelView project) {
if (ModelState.IsValid) {
_context.Projects.Add(new Project {
OwnerId = User,
Name = project.name
});
_context.SaveChanges();
return RedirectToAction(actionName: "Index");
}
return View(project);
}
}
I looked up the asp .net documentation and I couldn't find a correct answer.
Which is more "ASP like" and correct option? Is there better ways to do it? Also, is the dto a ViewModel in this case?
I use the Repository/Service pattern, togeter with N-tier architecture.
N-tier architecture looks like this
ProjectName.Web/Server, depending if youre making like an mvc application, then its .Web, if just a web api, then .Server
This is the main project with controllers, automapper, views etc.
Then three class libraries projects
ProjectName.Business, this projects is used to store most of the helper logic and diffrent kind of business logic for your application, also the DTOs or ViewModels
ProjectName.DataAccess, data access is the project with the direct connection to the database, this is the place where I use my repository folder with the context method, like put, get, post etc. Also the DbContext
ProjectName.Models, this project is pretty simple, it is just used for all the entities/models you're going to use
So how is all this connected?
The projects need project references.
This is how it will go
.Models > .DataAccess > .Business > .Web/Server
It goes from the bottom to the top, with the Web/Server project as the top since this is the real application.
So how do I implement the repository pattern into the N-Tier architecture?
I Create a Repository folder in the .DataAccess project.
Inside the repository create the files, for exempel if you have a model and controller called EmployeeController.cs and Employee.cs
Then inside the Repository folder, to keep it clean - create a sub folder, simply called Employee. Inside the Employee folder, create 2 files.
EmployeeRepository.cs and IEmployeeRepository.cs
Inside the EmployeeRepository you need a reference to IEmployeeRepository so your functions and methods will be available to other files.
And then just create all the context logic in the repository files.
To keep another layer between the .Web/Server project and the database, I Create a folder inside of the .Business project, called Service. This is the same principle as the Repository, create a sub folder called Employee, with EmployeeService.cs and IEmployeeService.cs, call all of the methods from the repository to the service, and then you call the service methods from the IEmployeeService to the EmployeeController inside of the .Web/Server project, using dependency injection.
And there you have it, complete isolation from the dbcontext in the controllers.
I can suggest you this improvements, hope it helps:
Do not inject your db context into controllers - it is bad practise. What if your controller's methods will contain a lot of logic? And what if two controllers has similar methods with similar algorithm? You can add MediatR library with commands and handlers, and in handlers you can use you db context logic.
Example: https://medium.com/dotnet-hub/use-mediatr-in-asp-net-or-asp-net-core-cqrs-and-mediator-in-dotnet-how-to-use-mediatr-cqrs-aspnetcore-5076e2f2880c
No dto is not view class, you should split domain objects and view objects, you can look to Mapper
Example: https://code-maze.com/automapper-net-core/
About your question:
Check the actor role: https://www.syncfusion.com/succinctly-free-ebooks/akka-net-succinctly/actors-in-asp-net-core
However I'm not sure that your question are still exists when you do a little refactoring from 1 and 2

Suggestions for cross layer builder pattern in MVC

I have an n-tired application with MVC as the presentation. The view is a form which operates like a wizard. When the user clicks next on each wizard step, I'd like the information collected from the step to be processed in the service layer.
What I have set up at the moment is:
User submits the current step in View
Controller takes in the current view's model
Convert the model to a DTO using AutoMapper
Pass the DTO into the service method to perform the necessary processing on the DTO
Convert the DTO back into it's view model Redirect to next step.
Code looks similar to:
public DemoController
{
private readonly IService _service;
public DemoController(IService service)
{
_service = service;
}
[HttpPost]
public ActionResult Next(ViewModel model, int value)
{
var dto = Mapper.Map<DTO>(model);
_service.ProcessNext(dto, value);
model = Mapper.Map<ViewModel>(dto);
return RedirectToAction("NextStepAfterThis");
}
}
Is there a more elegant way to achieve this result?

Using a DbContext variable from one Controller to Another

Hi I am using MVC 4 and C# to develop an application that has two controllers:
The first one is called Business, it has a method called Create that calls a method called CreatePartner from another Controller named PartnerController.
public class BusinessController : Controller
{
private storeContext db = new storeContext();
public ActionResult Create(Business business)
{
//Some stuff here
PartnerController pt = new PartnerController();
pt.CreatePartner(int partner_id);
//Here is another stuff that uses db DbContext variable
return RedirectToAction("Index");
}
}
This is the second controller Called Partner
public class PartnerController : Controller
{
private storeContext db = new storeContext();
public void CreatePartner(int partner_id)
{
//Some interesting stuff
}
}
Each controllers has its Dispose() method
The Problem is: After I called the CreatePartnet method from Business controller I try to use the db variable again to save other data but it throws me the following exception:
The operation can not be completed because the DbContext has been disposed
-What is the best way to Use methods from one controller to another that has the same DbContext variable name?.
-Something strange happens: My stuff works locally but when I publish my code in the IIS server is when the app throws that exception.
Thanks!
Might I suggest an alternative approach?
Controllers are not very good places for business logic; that is they're not very good places for "doing stuff". It's often demonstrated in MVC tutorials and examples in this manner but it's really only good for getting into MVC quickly - it's not very good practice.
Furthermore Controllers aren't really supposed to have methods to be called - from themselves or called from another Controller. Controllers should really just contain their Actions.
Instead, extract your logic to an external class. A Service is a design pattern in which commonly used business logic is abstracted away. That way things can have a reference to the service and execute the logic without knowing anything about the implementation.
Observe:
IPartnerService
public interface IPartnerService
{
void CreatePartner(int partnerId);
}
DefaultPartnerService
public class DefaultPartnerService : IPartnerService
{
private StoreContext db;
public DefaultPartnerService()
{
db = new StoreContext();
}
public void CreatePartner(int partnerId)
{
// Something interesting
}
}
BusinessController
public class BusinessController : Controller
{
private IPartnerService _partnerService;
public BusinessController()
{
_partnerService = new DefaultPartnerService();
}
public ActionResult Create(Business business)
{
_partnerService.CreatePartner(business.PartnerId);
return RedirectToAction("Index");
}
}
Of course this approach is also greatly simplified for educational purposes. It's not best practice yet, but it might put you on the right track. Eventually you'll discover problems with this approach and you'll gravitate to reading about Repositories, Unit of Work, Dependency Injection and so on.

How to perform Unit Testing on Create method in MVC?

How to perform a Unit testing on MVC application?
I have created the Controller Location.
It has properties like LocationName,Area,City,PinCode.
Now, I want to perform unit test to check whether Location saves in DB or not.
How to check it.
I have go through tons of videos, every where they just put the Unit test of
Mathematical operations like adding,Dividing , subtracting....
I would like to know how to perform the Unit testing of Create method of MVC
I have code something like below
[HttpPost]
public ActionResult Create(Location location)
{
if (ModelState.IsValid)
{
db.Locations.Add(location);
db.SaveChanges();
return RedirectToAction("Index");
}
}
In order to make your code testable, you should abstract dependencies of controller. It's very handy to use Repository pattern to abstract data access. Inject your repository into controller:
public class LocationController : Controller
{
private ILocationRepository _locationRepository;
public LocationController(ILocationRepository locationRepository)
{
_locationRepository = locationRepository;
}
}
Now you can mock your repository. Here is sample test with Moq framework and MvcContrib:
// Arrange
Mock<ILocationRepository> repository = new Mock<ILocationRepository>();
var controller = new LocationController(repository.Object);
Location location = new Location("New York);
// Act
var result = controller.Create(location);
// Assert
result.AssertActionRedirect()
.ToAction<LocationController>(c => c.Index());
repository.Verify(r => r.Add(location));
repository.Verify(r => r.Save());
And you can implement code, which will pass this test:
[HttpPost]
public ActionResult Create(Location location)
{
if (ModelState.IsValid)
{
_locationRepository.Add(location);
_locationRepository.Save();
return RedirectToAction("Index");
}
}
You can read more on implementing repositories and testing MVC applications here:
Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application. Nice feature also to have Unit of Work per request.

Get instance of objects based on other values without using service location

I am using a Unit of Work pattern in my mvc 3 app with Ninject. I have run into a problem where i'm having difficulty solving it without using new or service location of some kind.
I am using an abstract base model called MyModel which has 2 concrete subclasses MyModel1 and MyModel2. These need to get sent to the view based on a value set in the users record.
public class MyController : Controller
{
private IUnitOfWork _unitOfWork;
public MyController(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; }
...
public ActionResult MyMethod() {
var user = _unitOfWork.Get(user)
if (user.myprop == somevalue)
{
return View(new MyModel1());
}
return View(new MyModel2());
}
This works fine (although I don't like it, it's simple and works. The problem is that using new is an anti-pattern when using Dependancy Injection. Additionally, I now want the models to initialize themselves (from the database) so I need to inject the IUnitOfWork into the constructor of the models. I could, of course do this:
if (user.myprop == somevalue)
{
return View(DependancyResolver.Current.GetService(typeof(MyModel1)));
}
But Service Location is also an anti-pattern.
Any suggestions on how to solve this?
Using new is not an anti pattern for DI if used correctly. There is absolutely no problem to use new to create data containers such as view models.
But it is an anti pattern for MVC applications to have logic or data retrieving code in your view models so that they need dependencies. All this stuff belongs outside into the controller or some services. The data is assigned preformatted to the view model from outside.
Additionally, I now want the models to initialize themselves (from the
database) so I need to inject the IUnitOfWork into the constructor of
the models
No. You should not pass any models to your views. You pass VIEW MODELS only. View models are dumb. They only contain preformatted data for the view to display. If you used AutoMapper for example you could have externalized this into the mapping layer and your controller could become:
public ActionResult MyMethod() {
var user = _unitOfWork.Get(user)
var userViewModel = Mapper.Map<User, UserViewModel>(user);
return View(userViewModel);
}

Categories

Resources