ASP.NET MVC Pattern - How to use two seperate repositores in a controller(using DI) - model-view-controller

I have two very simple tables. Product and ProductCategory (ProductCategory is like a 'lookup' table for Product). On my controller, for my Index() method I want to list the categories of the products. When the user clicks on a category, I want to pass my category to my List() method to show all the products of a specific category.
I'm using ninject DI framework; I currently have something like this.
private IProductCategory productCategoryRepository;
private IProduct productRepository;
public StoreController(IProductCategory productCategoryRepository)
{
this.productCategoryRepository = productCategoryRepository;
}
public ViewResult Index()
{
return View(productCategoryRepository.GetCategories());
}
public ViewResult List(string category, int page = 1) //Use default value
{
...
};
I have basic repositories per table/entity (i.e. GetCategories(), GetProducts(), GetProductsByCategory..etc.) What's the best way...or how can I use two seperate repositories in a controller? I'd prefer not to pass them both through a controller.
Note: Product and ProductCategory is not considered an aggregate.

As i mentioned earlier, a service layer would help you resolve this. The service layer is a point of contract between the user interface and the middle tier. This could be a WCF service or a simple service implementation i am showing below.
public interface IMyProductService
{
IList<Product> GetProducts();
IList<Product> GetProductsByCategory();
IList<Category> GetCategories();
}
public class MyProductService : IMyProductService
{
IProductRepository _prodRepo;
IProductCategoryRepository _catRepo;
public MyProductService(IProductRepository prodRepo, IProductCategoryRepository catRepo)
{
_prodRepo = prodRepo;
_catRepo = catRepo;
}
// The rest of IMyProductService Implementation
}
Your MVC controller will have a reference to IMyProductService likely using constructor injection with a DI framework of your choice.

Related

Is my MVC approach correct?

I'm writing an app in MVC 5 right now. I've made MVC app(for iOS) some time ago, but honestly i'm a little bit confused right now. I tried to find some info about this pattern, but it seems that there are many approaches.
My app uses external database operating on JSON format. I have bunch of methods in Api class that return objects filled with data from database. In my opinion this Api class is basically model, but i am not sure.
Model:
//Model
public class UserModel
{
public string Name { get; set; }
public string Lastname { get; set; }
}
Api class(Model?):
public class Api
{
public UserModel GetUserData()
{
UserModel model = new UserModel();
//code connecting to DB and filling UserModel object
return model;
}
}
Controller:
//Controller
public ActionResult Index ()
{
UserModel model = new UserModel();
Api api = new Api();
model = api.GetUserData();
return View(model);
}
View:
#*View*#
#model application.Models.UserModel
#Model.Name
#Model.Lastname
And my last question. Where should i put methods like loginuser, delete user:
public void DeleteUser(UserModel model)
{
//code deleting user
}
Should it go to model or controller? What i think is - if it will be used multiple times in different places i should put it in model, otherwise it should go to controller.
Thanks in advance.
Your Api class is not a model, the model classes must be entities of your domain. Your domain is the core of your business, for example, if your are making a School Application, your models/entities are: Student, Teacher, Discipline ...
So your Api belongs to your infrasctucture layer, and all classes with database access responsability.
Your controller's must only orchestrate the workflow, for example:
private readonly api;
public HomeController()
{
this.api = new Api();
}
public ActionResult Index ()
{
var model = api.GetUserData();
return View(model);
}
Your Api probably will be used on other Actions, so you can make a private field and initialize it from constructor. But the best approach is to use an IoC container.
The LoginUser must be a action on your Controller, but the business rule could be on other layer of your project, so it can be reusable. For example:
public ActionResult Login(string login, string password)
{
var user = api.GetUserByLogin(login);
if(user == null)
{
ViewBag.ErrorMsg = "There is no user with this login";
return View();
}
//userService is a class responsible for business rules for users
var isSuccess = userService.LoginUser(login, password);
if(isSuccess)
return RedirectToAction("Index", "Home");
ViewBag.ErrorMsg = "Password is incorrect";
return View();
}
Be careful because the ASP.NET MVC still have the concept of ViewModels. That are classes where we use to show data in our Views. In our school application, for example, we could have a View which is necessary to show a Discipline with all students enrolled, and what professor. So we need to use information of 3 entities, in that case we make a ViewModel to show all this information together.

Web api controller with multiple post methods with the same name but with different parameters

I have web api controller that has multiple post methods with the same name but with different parameters; when i run the application, i got an error:-
Multiple actions were found that match the request
note:- I don't want to use Action Routing as i want to unify my clients who use my web api
public Customer Post(Customer customer)
{
}
public Product Post(Product product)
{
}
The problem is that there's no way to distinguish between those two Post methods based on the URL that's getting passed to the web api.
The way to handle this would be to use a separate controller. One controller would be "api/Customer" and would have Post method that takes a Customer:
public class CustomerController : ApiController
{
public Customer Post(Customer customer) { }
}
The other would be "api/Product" and take a Product:
public class ProductController : ApiController
{
public Product Post(Product product) { }
}
If you really really wanted to pass both into one controller, you could create a class that has all the properties of both Customer and Product, and then look at the properties to figure out what just got passed into your controller. But... yuck.
public class EvilController : ApiController
{
public ProductOrCustomer Post(ProductOrCustomer whoKnows)
{
// Do stuff to figure out if whoKnows has
// Product properties or Customer properties
}
}
You could use a single controller, with a single method taking a parameter of an interface type that both classes implement. Then call private handlers based on runtime type.

N-Tier Service Layer Validation Show Business Logic Error in Presentation Layer

I am converting from the old ways of ASP.NET Web Forms to ASP.NET MVC. I have a project that I am working on that has about 40-50 tables in the database. I have decided to use Entity Framework as my data access layer. I have also decided to put a repository layer and unit of work abstraction over EF so that I am not tied to it and so that I can do unit testing. Finally, I want to make my controllers "thin" so I am looking at implementing a business "service" layer for my business logic.
The thing I am struggling with is how do I propagate Business Logic Errors from my service layer to my Presentation UI layer so that an appropriate error can be shown? Please note that I am trying to look for a solution that is NOT MVC specific as this service/business logic layer will likely be used in other things besides an MVC app (console app's, web services, etc.)
On to some code...
Lets say I have a POCO / data / domain model like so:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
// other properties (navigation, etc)...
}
An Entity Framework fluent configuration/mapping class like so:
public class CategoryMap : EntityTypeConfiguration<Category>
{
public CategoryMap()
{
this.HasKey(c => c.Id);
this.Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); // auto increment identity in our DB schema
this.Property(c=> c.Name)
.IsRequired() // defined as NOT NULL in DB schema so we put a constraint here
.HasMaxLength(150); // defined as varchar(150) in DB schema so we put a constraint here
this.Property(c=> c.Description)
.IsRequired(); // defined as NOT NULL in DB schema so we put a constraint here
// fluent config for related entities (navigation properties) would go here...
}
}
A unit of work encapsulating multiple repositories like so:
public class UnitOfWork : IUnitOfWork
{
private readonly MyDbContext context;
private CategoryRepository catRepo;
public UnitOfWork()
{
this.context = new MyDbContext();
}
public ICategoryRepository Categories
{
get { return this.catRepo?? (this.catRepo= new CategoryRepository (this.context)); }
}
}
A service / business logic layer like so:
public class CategoryService : ICategoryService
{
private readonly IUnitOfWork unitOfWork;
public CategoryService(IUnitOfWork uow) // injected by IoC
{
this.unitOfWork = uow;
}
public Category CreateNewCategory(Category category)
{
if (category == null)
{
throw new ArgumentNullException("category cannot be null");
}
// Simple business logic here to make sure another category with this name does not already exist.
int count = this.unitOfWork.Categories.Count(cat => cat.Name == category.Name);
if (count > 0)
{
// *** This is the error I want the user to see in the UI ***
throw new Exception("Sorry - a category with that name already exists!");
}
}
}
And a controller like this:
public ManageCategoriesController : Controller
{
ICategoryService catSvc;
public ManageCategoriesController(ICategoryService svc) // injected by IoC
{
this.catSvc = svc;
}
[HttpPost]
public ActionResult(CategoryCreateModel createModel) // my View Models / Create Models have Data Annotations on them
{
if (ModelState.IsValid)
{
// use of AutoMapper to map from View Model to domain model...
Category cat = Mapper.Map<CategoryCreateModel , Category>(createModel);
this.catSvc.CreateNewCategory(cat); // ***need to get potential errors from Service and display on form.***
return this.RedirectToAction("Index");
}
}
}
First of all, can anybody tell me if I am on the right track with using View Models? I feel like I almost have three View Models (Create, Edit, View/List) per domain model.
Secondly, my EF configuration/mapping class takes care of the database constraints. Some of these constraints (e.g. Max length) are also data annotations in the View Models and can easily be displayed on the UI. But where can I show my custom business logic errors?
First, your overall approach to MVC looks good to me :-)
Second, you most likely want to use DataAnnotation on your view models for model validation. Have a look this blog post for a good intro on using it in ASP.MVC.
In case of custom validation not suitable for data annotation you can do the following in your controller:
try
{
// the following exception could be thown by some nested validation logic
// e.g. while processing a post request
throw new ValidationException("the error description");
}
catch (ValidationException exception)
{
ModelState.AddModelError("", exception.Message);
}
This is a pretty old question, but for future readers I'd like to add something.
If you're actually using a N-Tier pattern, entity validation should be in your Service layer. Not in your MVC Controller.
The right way to do it is to do basic model validations in your model class, using ValidationAttributes, but re-validate your entities in your service layer.
Add a handling of custom exceptions in your controller to catch any validation error raised from the service layer, and display error messages.
If your service layer is just there to call your repositories, you're doing something wrong ;)

Wrapper class in MVC3

I want to create a wrapper class so that all queries should not be in controller. Currently select queries are placed in Controller. But I want to create another layer for abstraction.
I already created a viewmodel class. But wrapper class is something else.
How do I do that?
I don't do any queries directly in my controllers. I have a service layer which my controller would call, and each service layer would do a call to the repository to insert, update or delete data or bring back data.
The sample code below uses ASP.NET MVC3 and Entity Framework code first. Lets assume you want to bring back all the countries and use it for whatever reason in your controller/view:
My database context class:
public class DatabaseContext : DbContext
{
public DbSet<Country> Countries { get; set; }
}
My country repository class:
public class CountryRepository : ICountryRepository
{
DatabaseContext db = new DatabaseContext();
public IEnumerable<Country> GetAll()
{
return db.Countries;
}
}
My service layer that calls my repository:
public class CountryService : ICountryService
{
private readonly ICountryRepository countryRepository;
public CountryService(ICountryRepository countryRepository)
{
// Check for nulls on countryRepository
this.countryRepository = countryRepository;
}
public IEnumerable<Country> GetAll()
{
// Do whatever else needs to be done
return countryRepository.GetAll();
}
}
My controller that would call my service layer:
public class CountryController : Controller
{
private readonly ICountryService countryService;
public CountryController(ICountryService countryService)
{
// Check for nulls on countryService
this.countryService = countryService;
}
public ActionResult List()
{
// Get all the countries
IEnumerable<Country> countries = countryService.GetAll();
// Do whatever you need to do
return View();
}
}
There are lots of info on the internet on how to get you data and display it, inserting, editing, etc. A good place to start is at http://www.asp.net/mvc. Work through their tutorials, it will do you good. All the best.

View and Domain model, where to perform calculation

I just started using View & Domain model design in my MVC web app but got the question where to perform calculations and other View related actions. I will try to give example below.
My Domain Model (Linq2Sql)
public class Product
{
public int Id;
public string Name;
}
The View Model with new UserCount property which I would like to calculate.
public class ProductViewModel
{
public int Id;
public string Name;
public int UserCount;
}
My controller action looks like
public ActionResult _SelectionClientSide_Products()
{
IQueryable<Product> products = _repository.GetProducts(true);
var model = Mapper.Map<IEnumerable<Product>, IEnumerable<ProductViewModel>>(products);
return View(model);
}
I query for data using repository method and get IQueryable<Product> and map it to ProductViewModel list. But I also need to perform another query operation to count users for every queried product and assign all values to ProductViewModel. What design I should follow to achieve this?
The relationship between tables
Products -> Orders - > Users
EDIT
I have decided to remove AutoMapper because it gives more problems than benefits and created my own Builder which contain everything what I need. I make field assign and also add calculation.
public ActionResult _SelectionClientSide_Products()
{
Data = new ProductViewModelBuilder(_repository).Build();
return View(Data);
}
namespace PC.Models
{
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int UsersCount { get; set; }
}
public class ProductViewModelBuilder
{
private readonly IDataAccess _repository;
public ProductViewModelBuilder(IDataAccess repository)
{
_repository = repository;
}
public IQueryable<ProductViewModel> Build()
{
return _repository.GetProducts().Select(p=> new ProductViewModel
{
Id = p.Id,
Name = p.Name,
UsersCount = _repository.CountUsers(p.Id)
});
}
}
}
In my opinion, by the time your Controller has begun it's model setup, there should be no more "logic" or talking to the model. Having a "builder" which queries the model again is bad practice. Not really "breaking" the MVC pattern, but still bad practice. Your ViewModel has a dependency on your DAL. Bad bad boy. :)
If you have to run another query, well that should be encapsulated in the original query. Instead of calling _repository.GetProducts, call a different method which not only gets the products, but get's the count as well. Create a DTO if necessary.
Then your controller should look like:
public ActionResult _SelectionClientSide_Products()
{
var someDto = _repository.GetProductsAndUserCount(true);
var model = Mapper.Map<IEnumerable<SomeDto>, IEnumerable<ProductViewModel>>(someDto);
return View(model);
}
Personally i go for generic repositories over specialized ones, as i don't want a IProductRepository interface with 50 signatures. I opt for LINQ IEnumerable<T> extensions (aka "pipes and filters") which allows complex queries to be built and remain in my domain.
So my version of the above would be:
public ActionResult _SelectionClientSide_Products()
{
var someDto = _productRepository.Find().WithSomeCondition(true).ToSomeDto();
var model = Mapper.Map<IEnumerable<SomeDto>, IEnumerable<ProductViewModel>>(someDto);
return View(model);
}
On a side note, why do you say AutoMapper causes you more problems that benefits? AutoMapper has saved me tons and tons of repetitive code. If you know how to use it properly, it's your best friend. Hands down the most important NuGet package in my current application.

Resources