Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC Software Design Pattern: DI,Repository,Service Layer

Tags:

asp.net-mvc

EDIT

Should i put service layer and repository layer into one project, so the web project is able to reference to DbContext objects ? Now my web(controllers) are unable to reference to dbcontext objects. what is the right way?

// service and repository are together
(View <- Controller) -> (Service -> Repository -> EF DbContext) -> (DB) 
// separate service and repository layer
(View <- Controller) -> (Service) -> (Repository -> EF DbContext) -> (DB)

Below are original question

i know SO is an excellent community to post my questions about mvc design patterns. Please give me your advice and I will appreciate your help. Thank you!

We are planning for a new project and our priority is to develop an application that is extensible and loosely-coupled.

I am new to software development; I did some reading on MVC Music Store Tutorial, and followed by a book called Pro ASP.NET MVC 3 Framework by Steven Sanderson (Apress),From the book, I learnt about DDD(Domain driven design) and some other concepts like repository and dependency injection. I have followed the book to build the SportsStore website, and gained some basic understanding about DI. But I personally think that the example did not separate the business logic layer, so i did a research on that, i found a pattern called Service Layer Pattern, and from what i understand, it separates the Business logic layer. Based on this, I came out with a structure for my new project(sample project below).

Do i need to implement IDisposable interface? if yes, where and why? Is this structure feasible for a relatively big-scale project?

sample database design: Product(one)----(many)ProductCategoryRs(many)----(one)Category

the solution contains 3 projects: Repository,Service,Web

Repository:

Define IRepository interface , basic CRUD operations

Are these signatures sufficient? Should I add TEntity GetById(object id);?

public interface IRepository<TEntity>
{
    IQueryable<TEntity> All { get; }
    void Create(TEntity item);
    void Update(TEntity item);
    void Delete(TEntity item);
    void SaveChanges();
}

Implement generic Repository class

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    STOREEntities context;
    public Repository()
    {
        context = new STOREEntities();
    }
    public IQueryable<TEntity> All
    {
        get
        {
            return context.Set<TEntity>();
        }
    }
    public void Create(TEntity item)
    {
        context.Set<TEntity>().Add(item);
    }
    public void Update(TEntity item)
    {
        context.Entry<TEntity>(item).State = System.Data.EntityState.Modified;
    }
    public void Delete(TEntity item)
    {
        context.Set<TEntity>().Remove(item);
    }
    public void SaveChanges()
    {
        context.SaveChanges();
    }
}

Service: Define IProductService interface, extend business logic here.

public interface IProductService
{
    IEnumerable<Product> Products { get; }
    IEnumerable<Product> Get(Expression<Func<Product, Boolean>> filter);
    Product GetByProductId(int productId);
    void AddProduct(Product product);
    void EditProduct(Product product);
    void RemoveProduct(Product product);
    void SaveChanges();
}

Implement Product Service

    public class ProductService : IProductService
{
    IRepository<Product> repository; //Inject
    public ProductService(IRepository<Product> repo)
    {
        repository = repo;
    }
    public IEnumerable<Product> Products
    {
        get { return repository.All; }
    }
    public IEnumerable<Product> Get(Expression<Func<Product, bool>> filter)
    {
        return repository.All.Where(filter);
    }
    public Product GetByProductId(int productId)
    {
        return repository.All.SingleOrDefault(p => p.ProductID == productId);
    }
    public void AddProduct(Product product)
    {
        repository.Create(product);
    }
    public void EditProduct(Product product)
    {
        repository.Update(product);
    }
    public void RemoveProduct(Product product)
    {
        repository.Delete(product);
    }
    public void SaveChanges()
    {
        repository.SaveChanges();
    }
}

Web project, retrieve data from service and convert to viewmodel and display. ProductController code

public class ProductController : Controller
{
    IProductService productService; //inject
    public ProductController(IProductService service)
    {
        productService = service;
    }
    public ActionResult Index()
    {
        var products = productService.Products; //retrieve from service layer
        return View(products);
    }
}
like image 361
Timeless Avatar asked Mar 09 '12 10:03

Timeless


1 Answers

I believe you really should add a TEntity GetById(int id) to your IRepository<TEntity> generic interface.

Why? Because if you don't, and if you want to fetch a single record on your business layer, you only have two options (on the repository, data-access layer):

  1. Return a complete, "unlazy" collection, meaning that you'll return, say, 100,000 records in order to use a single one.
  2. Return a lazy collection like IQueryable<TEntity>, which, yes, will allow you to get a single record from the database, but may cause a lot of nasty side-effects.

The first option is clearly wrong. The second is controversial, but (unless your project has you as a single developer, and you really really really know what you're doing) it is potentially leaky and unsafe. So, if you do need a single record (and sometimes you'll surely do), expose a method that does exactly that.

Having said that, you should also not expose IQueryable<TEntity> All { get; }, for exactly the same reasons above. Use IEnumerable<TEntity> All { get; } instead, and make your concrete generic repository class return a real collection, by calling context.Set<TEntity>().ToList() for instance.

Edit

Regarding IDisposable:

There are only two (related) reasons for implementing the IDisposable interface that I can think of:

  1. Disposing unmanaged resources
  2. A cool way of implementing the RAII pattern.

In your case, you probably should use it on your repository implementation. Please take a look at this SO question for further information.

like image 167
rsenna Avatar answered Nov 15 '22 04:11

rsenna