views:

110

answers:

3

I'm trying to get an understanding of how to structure an ASP.Net MVC2 Web App using repositories.

A lot of examples, tutorials and books I have read structure the App like so:

public interface IProductsRepository
{
    IQueryable<Product> Products { get; }
}

public class SqlProductsRepository : IProductsRepository
{
    public Table<Product> productsTable;

    public IQueryable<Product> Products
    {
        get { return productsTable }
    }
}

public class ProductsController : Controller
{
    IProductsRepository productsRepository;
    public ProductsController(IProductsRepository productsRepository)
    {
       // set the Repository
    }

    public ActionResult GetLatestPublishedProducts(int pageSize, int page, ref int totalItemCount)
    {
        // Do LINQ query 
        return View(from p in productsRepository
                    where p.PublishedOn != null 
                    orderby p.ProductId descending 
                    select to(p))
                    .Skip((page  - 1) * pageSize)
                    .Take(pageSize)
                    .ToList());
    }
}

One thing I don't understand is why the Linq query lives in the controller.

I'd like to know why doing something like this is not correct:

public class SqlProductsRepository : IProductsRepository
{
    // set data model stuff here

    public List<Product> GetLatestPublishedProducts(int pageSize, int page, ref int totalItemCount) {
        // DO LINQ Query in here
    }
}

public class ProductsController : Controller
{
    // set repository stuff here

    public ActionResult GetLatestPublishedProducts(int pageSize, int page, ref int totalItemCount)
    {       
        return View(productsRepository.GetLatestPublishedProducts(pageSize, page, totalItemCount));
    }
}
+4  A: 

Your second example is what you should be doing. The repositories should handle all data access.

The controller should only get already filtered/sorted data and pass it to the view. The controller should not be reponsible for any data or business logic. It should only be responsible for retrieving data and passing it on.

When writing code in a controller, I ask myself this question;

Will I have to write this code again if I were to write this application in WinForms?

So your IProductsRepository would have the method GetLatestPublishedProducts, which you'd then implement in your SqlProductsRepository.

The responsibilities are:

  • Repository - Gets data from the database
  • Controller - Passes data to the View for rendering

You can then take this a step further and separate any business logic out as well so that the Controller itself avoids using repositories. So you'd have a third Service layer that sits between the Controller and Repository.

GenericTypeTea
Cheers :D That's exactly what I was thinking. It's kind of frustrating reading these books and they show you something wrong. Does anyone know of a good Real World MVC2 project where I could check out the source?
sf
@sf - The problem with a lot of books is they're written to teach you to use something, not how to design your application. Which is why you should probably buy a decent design patterns book or DDD book to go along with your learning.
GenericTypeTea
+2  A: 

Check the Intentions of the different classes.

  • Repository: Provide Access to Database
  • Controller: control the flow of your website (i know, there are better intentions)

that means, if you see the Linq as your main business logic, than it should NOT be in any of the 2. i would create a ProductService, that contains all of this abstracted calls, as they will be commonly reused.

what should be in the Service:

from p in productsRepository
                    where p.PublishedOn != null 
                    orderby p.ProductId descending 
                    select to(p))

what should be in the Controller:

from p in productService.GetLatestPublishedProducts()
                    .Skip((page  - 1) * pageSize)
                    .Take(pageSize)

And, if possible, use the IEnumerable. not a list.

cRichter
Thanks for that. I'll also be sure to use IEnumerable too.
sf
+1  A: 

Make the goal to keep your controllers lean. This will help in keep things in order. Use repositories to handle all data access logic. I also include a service layer to handle other business and validation logic. So an my contoller, service and repository methods something like Create would look like this:

// Controller
public ActionResult Create(MyModel myModel)
{
    if (!_service.CreateMyModel(myModel)) 
    {
        return View(myModel);
    }

    return RedirectToAction("Index");
}

// Service
public bool CreateMyModel(MyModel myModel)
{
    // Validation logic
    if (!ValidateMyModel(myModel))  
    {
        return false;
    }

    return _repository.CreareMyModel(myModel);
}

// Repository
public book CreateMyModel(MyModel myModel)
{
    _entities.AddToMyModelsSet(myModel);
    try
    {
        _entities.SaveChages();
        return true;    
    }
    catch
    {
        return false;
    }
}
gnome
Shouldn't the validation be done by MVC. that you have ModelState.IsValid? what is the use case, that you leverage that back to the Service and do it manually?
cRichter
You can leave the validation up to MVC. I use custom modelwrapper and validation class that I wrote in my MVC applications. It allows me to perform specific / additional validation. The ValidateMyModel method in the end returns the ModelState.IsValid
gnome