HiveBrain v1.2.0
Get Started
← Back to all entries
principlecsharpMajor

Repository/Service Design Pattern

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
patterndesignservicerepository

Problem

I use the Repository/Service design pattern in my projects and I have found something that might be a bit redundant. Am I writing any unnecessary code?

With that in mind, here is my structure:

public class Competition
{
    public int Id { get; set; }
    [Required] public string UserId { get; set; }
    public string UserName { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime? DateModified { get; set; }
    [Required] public string Name { get; set; }
}


This is an example POCO class, but as you will see I am using Generics so it can be anything:

internal interface IRepository : IDisposable where TEntity : class
{
    IList GetAll();
    TEntity Get(int id);
    void Save(TEntity model);
    void Delete(int id);
}


My interface implements IDisposable and allows any class object to be set as TEntity:

internal class CompetitionRepository : IRepository
{
    private readonly string userId;
    private readonly CompetitionProvider provider;

    public CompetitionRepository(string userId) : this(userId, "DefaultConnection")
    {
    }

    public CompetitionRepository(string userId, string connectionString)
    {
        this.userId = userId;
        this.provider = new CompetitionProvider(connectionString);
    }

    public IList GetAll()
    {
        return provider.Get(this.userId);
    }

    public Competition Get(int id)
    {
        return GetAll().Where(model => model.Id == id).SingleOrDefault();
    }

    public void Save(Competition model)
    {
        provider.Save(model);
    }

    public void Delete(int id)
    {
        provider.Delete(id);
    }

    public void Dispose()
    {
        provider.Dispose();
    }
}


My repository which implements IRepository, specifying Competition as TEntity. This repository queries my database hence the provider.

In this repository, I only do the minimal of what is required to query my database:

```
public class CompetitionService : IReposi

Solution

You are on the right track. The separation of business logic (services) and data-access logic (repositories) is a good thing, I strongly recommend this. I created a generic repository implementing the IRepository interface. It's slightly different than yours, but you get the point (I have a BaseEntity constraint, you can also use class for this):

public class EfRepository : IRepository
    where TEntity : BaseEntity, new()
{
    private readonly IDbContext _context;
    private IDbSet _entities;

    public EfRepository(IDbContext context)
    {
        _context = context;
    }

    private IDbSet Entities
    {
        get { return _entities ?? (_entities = _context.Set()); }
    }

    public TEntity GetById(object id)
    {
        return Entities.Find(id);
    }

    public void Insert(TEntity entity)
    {
        Entities.Add(entity);
        _context.SaveChanges();
    }

    public void Update(TEntity entity)
    {
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        // Create a new instance of an entity (BaseEntity) and add the id.
        var entity = new TEntity
                     {
                         ID = id
                     };

        // Attach the entity to the context and call the delete method.
        Entities.Attach(entity);
        Delete(entity);
    }

    public void Delete(TEntity entity)
    {
        Entities.Remove(entity);
        _context.SaveChanges();
    }

    public IList Table
    {
        get { return Entities.ToList(); }
    }
}


This greatly reduces the amount of duplicate code when you would use specific repositories. I use this in my services like:

public class ProductService : IProductService
{
    private readonly IRepository _productRepository;

    public ProductService(IRepository productRepository)
    {
        _productRepository = productRepository;
    }

    public bool AddProduct(Product product)
    {
        // Validate etc.
        _productRepository.Insert(product);

        // return validation result.
        return true;
    }
}


As you may have noticed I use an IoC framework to inject the dependencies.

Code Snippets

public class EfRepository<TEntity> : IRepository<TEntity>
    where TEntity : BaseEntity, new()
{
    private readonly IDbContext _context;
    private IDbSet<TEntity> _entities;

    public EfRepository(IDbContext context)
    {
        _context = context;
    }

    private IDbSet<TEntity> Entities
    {
        get { return _entities ?? (_entities = _context.Set<TEntity>()); }
    }

    public TEntity GetById(object id)
    {
        return Entities.Find(id);
    }

    public void Insert(TEntity entity)
    {
        Entities.Add(entity);
        _context.SaveChanges();
    }

    public void Update(TEntity entity)
    {
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        // Create a new instance of an entity (BaseEntity) and add the id.
        var entity = new TEntity
                     {
                         ID = id
                     };

        // Attach the entity to the context and call the delete method.
        Entities.Attach(entity);
        Delete(entity);
    }

    public void Delete(TEntity entity)
    {
        Entities.Remove(entity);
        _context.SaveChanges();
    }

    public IList<TEntity> Table
    {
        get { return Entities.ToList(); }
    }
}
public class ProductService : IProductService
{
    private readonly IRepository<Product> _productRepository;

    public ProductService(IRepository<Product> productRepository)
    {
        _productRepository = productRepository;
    }

    public bool AddProduct(Product product)
    {
        // Validate etc.
        _productRepository.Insert(product);

        // return validation result.
        return true;
    }
}

Context

StackExchange Code Review Q#33109, answer score: 25

Revisions (0)

No revisions yet.