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?
Related
I have created ASP.NET Core application and now I'm trying to use ViewBag in _LoginPartial. I have created base controller:
public class BaseController : Controller
{
public ApplicationDbContext _db;
public BaseController(ApplicationDbContext db)
{
_db = db;
ViewData["MyKey"] = _db.MyTable.ToList();
}
}
All my controllers derive from this BaseController.
But when I see ViewData in _LoginPartial.cshtml, I can only see that it contains default Key=Title, Value=Home Page. What should I do to have MyKey available in _LoginPartial?
Thanks!
The problem is that you are trying to set ViewData content within the controller’s constructor.
The ViewData dictionary is not actually created by the controller. It is created at a very different point within the MVC pipeline and then gets injected into the controller. You can basically see this process as something like this:
// create controller
var controller = CreateController<MyController>();
// do stuff
// inject ViewData
controller.ViewData = GetViewDataDictionary();
// invoke controller action
var result = controller.SomeAction();
So the ViewData gets provided after the controller has been created; after its constructor ran. So any assignments to the view data within the constructor will not apply to the actual view data dictionary.
As such, you will need to set those values at a different time, you cannot use the constructor there. Using the ViewData in general is somewhat legacy construct that you should try to avoid if possible. Instead, you should work with strongly typed view model objects. Of course, this will require you to pass the data explicitly in each action, but that way you are also not introducing any implicit data flow.
An alternative, which is especially useful if what you are doing should actually always apply to the _LoginPartial, would be to use a view component. View components are reusable components that you can use inside of your views, which will behave similarly to a controller action. So you could just insert a view component into your partial, and have that run the logic (asynchronously even!) to provide the data from your database. And you wouldn’t need to mess with any of your controllers to make it work.
ViewData can be accessed after controller has been activated or by overrinding OnActionExecuting.
//
// Summary:
// Gets or sets Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary used by
// Microsoft.AspNetCore.Mvc.ViewResult and Microsoft.AspNetCore.Mvc.Controller.ViewBag.
//
// Remarks:
// By default, this property is intiailized when Microsoft.AspNetCore.Mvc.Controllers.IControllerActivator
// activates controllers.
// This property can be accessed after the controller has been activated, for example,
// in a controller action or by overriding Microsoft.AspNetCore.Mvc.Controller.OnActionExecuting(Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext).
// This property can be also accessed from within a unit test where it is initialized
// with Microsoft.AspNetCore.Mvc.ModelBinding.EmptyModelMetadataProvider.
[ViewDataDictionary]
public ViewDataDictionary ViewData { get; set; }
For a solution, you could try overring OnActionExecuting like below:
public class BaseController : Controller
{
public ApplicationDbContext _db;
public BaseController(ApplicationDbContext db)
{
_db = db;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
ViewData["MyKey"] = _db.Product.ToList();
base.OnActionExecuting(context);
}
}
_LoginPartial.cshtml
#foreach (Product item in #ViewData["MyKey"] as IList<Product>)
{
<li><a>#item.Name</a></li>
}
I am me to asp. Ne MVC I am using N_layrs architecture ( usiness, service, validation, data and presintation layers). I created a controller to view some data. I implemented the presentation interface to the controller abd I alled the display method (this method wil retrieve the data from the system layers and call the presentation interface method to gi e me the value) in the implemented interface method I assine the back items variable to local private variable but when I assign this list to View method the value be null.
public class AgreementController : Controller, IListView<IList<AgreementModel>>
{
private static ListPresenter<AgreementModel> _agreementListPresenter;
//This is the private member
private IList<AgreementModel> _items;
public RequestType RequestType
{
get
{
return RequestType.FindAll;
}
}
public string ListErrorMessage
{
set
{
}
}
// GET: Agreement
public ActionResult Index()
{
//Some unity code for DI
IUnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<IListView<IList<AgreementModel>>, AgreementController>();
unityContainer.RegisterType<Service<IBusinessService<AgreementModel>, AgreementModel>>();
unityContainer.RegisterType<IBusinessService<AgreementModel>, BusinessService<AgreementModel>>();
unityContainer.RegisterType<IRepository<AgreementModel>, AgreementRepository>();
unityContainer.RegisterType<Service<IBusinessService<PlanModel>, PlanModel>>();
unityContainer.RegisterType<IBusinessService<PlanModel>, BusinessService<PlanModel>>();
unityContainer.RegisterType<IRepository<PlanModel>, PlanRepository>();
unityContainer.RegisterType<IValidator<AgreementModel>, AgreementValidator<AgreementModel>>();
unityContainer.RegisterType<IValidator<PlanModel>, PlanValidator<PlanModel>>();
//Create the Presenter this will fire DisplayList
_agreementListPresenter = unityContainer.Resolve<ListPresenter<AgreementModel>>();
_agreementListPresenter.Display();
//The value here is NULL
return View(_items);
}
public void DisplayList(IList<AgreementModel> items)
{
//I recieved vale here and it is OK
_items = items;
}
}
}
This solved my issue
unityContainer.RegisterInstance<IListView<IList<AgreementModel>>>(this);
By this I regester the current controller object insted of createing new one
Quick answer.
It looks like you instantiate a new AgreementController via Unity, which presumably sets its own item list somewhere in your million classes.
But the View is sent the list of items from the Controller which you have called. Not the one you instanciated.
Long answer.
You should move all you dependency injection code to your App Startup or Global.ascx point. Its called when the app starts up, not when a page is requested.
Your AgreementController should have a constructor with parameters for the services it needs. Unity will create the controller and inject the services that you have registered in global.ascx
I have MVC5 app which has 2 areas: area1 and area2.
Some views in those areas are using code from controller which is actually same for view in area1 and view in area2.
Now I have 2 controllers per each area, but as I mentioned, the code is very same.
How can I use one Controller per each view in the area 1 and 2 to avoid code duplicity and have simpler maintainability?
Areas are just a layer, but they can still interact with each other and the application at large. If you need to share a controller, you can simply subclass it. Better yet, create an abstract controller outside of the areas and inherit each area's controller from that.
As far as views go, Razor has a very easy convention for handling overrides and fallbacks. It searches multiple directories, based on convention, for the required view and stops only when it finds a match.
For example, if you placed the view in Views\Shared, the last resort fallback, it can be used literally anywhere in your application, including each of your areas. The order of ops for view location is:
Areas\[AreaName]\Views\[ControllerName]
Areas\[AreaName]\Views\Shared
Views\[ControllerName]
Views\Shared
Razor will go down the list looking for the view in each location until it finds it.
You keep the separate controllers as they serve their purposes in the MVC framework. However, you can export much of the code in the controllers to service classes and each controller uses the services as needed -- Now you avoid code duplicity and have simpler maintainability.
Controllers
public class HomeController : Controller
{
private IFooService service;
public HomeController()
{
this.service = new FooService(dbContext);
}
public ActionResult CalculateFoo(int id)
{
var foo = this.service.CalculateFoo(id);
return View(foo);
}
}
public class FooController : Controller
{
private IFooService service;
public FooController()
{
this.service = new FooService(dbContext);
}
public ActionResult Details(int id)
{
var foo = this.service.CalculateFoo(id);
return View(foo);
}
}
Service
public class FooService : IFooService
{
private DbContext db;
public FooService(DbContext db)
{
this.db = db;
}
public Foo CalculateFoo(int id)
{
var foo = this.db.Foo.First(f => f.id == id);
// do stuff
return foo;
}
}
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.
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);
}