Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

Dot Net Rules

Yes, to dance beneath the diamond sky with one hand waving free

Ιστορικό Δημοσιεύσεων

Looking into the Generic Repository Pattern, Entity Framework and ASP.Net MVC

When developing/architecting our custom ASP.Net MVC applications we do use the Repository Pattern/Generic Repository Pattern.

I want to share my experience and thoughts with the community of developers out there that use Generic Repositories in their code.

At first a definition is appropriate. Martin Fowler gives the definition of the Repository Pattern: "Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.". You can read more link in his post.

In simpler words, the Repository Pattern creates an abstraction layer between the data access layer and the business logic layer of an application.

Repository design pattern is a way to simplify data access and enforce separation of concerns.

When I began learning about repositories and implementing them in my own software design, it had a huge impact on my application architecture.

It helped me to automate testing practices, forced me to consider separation of concerns with each method and behavior I added to my code. In a nutshell the pattern makes one to write good, clean, testable, low coupling, non-duplicate code

There are many ways to implement the repository pattern and I will show some bad ways to use and finally I will talk about its right usage.

This is the first approach to implement the Generic Repository Pattern. I am just creating a new empty project and I am adding a class library project in it.

Let's say that I will implement the Generic Repository Pattern for two domain objects (Product and Customer) that need data access operations because these in memory domain objects at one point through Entity Framework will have to materialize into rows of data in the data store.

1) This is the code below for the Customer Repository(ICustomerRepository)

public interface ICustomerRepository {


IQueryable<Customer> GetAll();

Customer GetByID(int CustomerId);

IQueryable<Customer> FindBy(Expression<Func<Customer, bool>> predicate);

void Add(Customer entity);

void Remove(Customer entity);

void Edit(Customer entity);

void Save();

}

 

2) This is the code below for the Product Repository (IProductRepository)

public interface IProductRepository
{

IQueryable<Product> GetAll();


Product GetByID(int ProductId);

IQueryable<Product> FindBy(Expression<Func<Product, bool>> predicate);

void Add(Product entity);

void Remove(Product entity);

void Edit(Product entity);

void Save();

}

As you can understand I am already repeating almost identical code because the two repositories for the two domain objects have identical data access methods.

3) I am going to implement now the Customer Repository.

This is the code below.

public class CustomerRepository:ICustomerRepository
{

private readonly CustomerEntities context = new CustomerEntities();

public IQueryable<Customer> GetAll()
{

IQueryable<Customer> query = context.Customers;
return query;
}

public Customer GetByID(int CustomerId)
{

var query = this.GetAll().FirstOrDefault(x => x.CustomerId == CustomerId);
return query;
}

public void Add(Customer entity)
{

context.Customers.Add(entity);
}

public void Remove(Customer entity)
{

context.Customers.Remove(entity);
}

public void Edit(Customer entity)
{

context.Entry<Customer>(entity).State = System.Data.EntityState.Modified;
}

public void Save()
{

context.SaveChanges();
}

4) The next step would be to implement the Product Repository for the Product domain object. As you can understand the code would be almost identical. I have already violated the DRY (Do Not Repeat Yourself) principle.

The code above implements the Repository pattern but needs reengineering because we had to do the above implementation for all our classes in our domain.

5) The first step towards re-engineering the code is to create a generic interface. This is the new generic interface.

 public interface IGenericRepository<T> :IDisposable where T : class
{

IQueryable<T> GetAll();


IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);

void Add(T entity);

void Remove(T entity);

void Edit(T entity);

void Save();
}

6) Νow we need to re-engineer our code for the two interfaces IProductRepository and ICustomerRepository

public interface IProductRepository : IGenericRepository<Product>
{

Product GetByID(int ProductId);


}

public interface ICustomerRepository:IGenericRepository<Customer>
{

Customer GetByID(int CustomerId);

}

I only need to implement GetByID method and the other methods come with IGenericRepositoy<T> interface.

If I move to the next step and implement the concrete classes (CustomerRepository,ProductRepository), I need to create all the methods in them. So we still duplicate and repeat the code.

7) We must re-engineer the code and come up with a better solution. I will create an abstract class that will inherit from my generic interface.The code follows.

public abstract class GenericRepository<C,DBC> :
IGenericRepository<T> where T : class where DBC : DbContext, new()
{

private DBC _newentities = new DBC();


protected DBC Context
{

get { return _newentities; }
set { _newentities = value; }
}

public virtual IQueryable<T> GetAll()
{

IQueryable<T> query = _newentities.Set<T>();
return query;
}

public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{

IQueryable<T> query = _newentities.Set<T>().Where(predicate);
return query;
}

public virtual void Add(T entity)
{
_newentities.Set<T>().Add(entity);
}

public virtual void Remove(T entity)
{
_newentities.Set<T>().Remove(entity);
}

public virtual void Edit(T entity)
{
_newentities.Entry(entity).State = System.Data.EntityState.Modified;
}

public virtual void Save()
{
_newentities.SaveChanges();
}

private bool disposed = false;

protected virtual void Dispose(bool disposing)
{

if (!this.disposed)
if (disposing)
_newentities.Dispose();

this.disposed = true;
}
public void Dispose()
{

Dispose(true);

}

}

I created an abstract class which implements IGenericReposity<T> interface but also accepts another type parameter a type of DbContext class. 

The methods above which implement basic data access methods are marked as virtual so they can be overwritten.

8) The concrete implementation for the two domain classes follow

public class ProductReposiltory :
GenericRepository<DBEntities, Product>, IProductRepository
{

public Product GetByID(int ProductId)
{

var query = this.GetAll().FirstOrDefault(x => x.ProductId == ProductId);
return query;
}
}

public class CustomerRepository:GenericRepository<DBEntities, Customer>,ICustomerRepository
{

public Customer GetByID(int CustomerId)
{

var query = this.GetAll().FirstOrDefault(x => x.CustomerId == CustomerId);
return query;
}



}

My concrete classes inherit from the Generic Repository and their own specific interface which in their turn inherit from the the IGeneric Repository.

I managed to have a code that implements separation of concerns by having a separate layer that creates that abstraction. My code is more testable and I do not have to repeat it.

Ι will create in another post the MVC project that will use my repositories.

Hope that helps!!!

Share
Posted: Κυριακή, 15 Μαΐου 2016 8:19 μμ από το μέλος nikolaosk
Δημοσίευση στην κατηγορία: , , ,

Σχόλια:

Χωρίς Σχόλια

Έχει απενεργοποιηθεί η προσθήκη σχολίων από ανώνυμα μέλη