6

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);
    }
}

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

-1, Although you have good insight into GetById, it wasn't the answer to any of the OP's questions.
@Levitikon: he asked about IDisposable and I answered it. That's 1 answer (at least that's how I learned to count, not sure about you :P)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.