Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Repository with Data Access Layer

I am creating a new project using business objects (Employee, Product). Due to constraints, I am not using LINQ to SQL or any ORM Mapper.

I have to hand code the Data Access Layer(s). I am interested in using the 'Repository Pattern'.

According to what I understand, I have to create a generic repository IRepository which is implemented by all repositories ProductRepository, EmployeeRepository.

What confuses me is that different business objects have different requirements. For example:

ProductRepository

 GetAllProducts ();
 GetProductById (int id);
 GetProductByMaxPrice (double price);
 GetProductByNamePrice (string name, double Price);
 Get... (...);

EmployeeRepository

 GetEmployeeByAge ();
 GetEmployeeByJob (string description);
 GetEmployeeBySalary (double salary);
 Get... (...); //and so on

How can I create a generic repository which meets different data access requirements of different objects?

I have read a lot of theory regarding Repository Pattern but would really appreciate a working example.

Additionally, if I can create all repositories using a generic repository, using the factory pattern becomes easy aswell. For example:

interface IRepository
{
    ....
}

ProductRepository : IRepository
{
    ....
}

EmployeeRepository : IRepository
{
    ....
}

Then we can use the factory pattern effectively as:

IRepository repository;
repository = new ProductRepository ();
repository.Call_Product_Methods ();

repository = new EmployeeRepository ();
repository.Call_Employee_Methods ();
like image 686
Code Guru Avatar asked May 15 '13 18:05

Code Guru


People also ask

Is Repository a data access layer?

The Repository pattern has come in for a lot of criticism over the past few years by high-end . NET developers. This is understandable, because in most projects, the Repository layer is usually one of the worst-implemented parts of the codebase.

What is a generic Repository?

It is a data access pattern that prompts a more loosely coupled approach to data access. We create a generic repository, which queries the data source for the data, maps the data from the data source to a business entity, and persists changes in the business entity to the data source.

Is Entity Framework a data access layer?

Entity Framework is a data access layer. Specifically it's an Object Relational Mapper.

What is data access layer in MVC?

A data access layer (DAL) in computer software is a layer of a computer program which provides simplified access to data stored in persistent storage of some kind, such as an entity-relational database. This acronym is prevalently used in Microsoft environments.


1 Answers

The repository Pattern is a great pattern to use, but if it is not done correctly, instead of making your life easier, it will be a huge pain!

So, the best possible way to do this (since you dont want to use EF or other ORM) is by creating a generic interface, and then a base abstract implementation. This way you dont need to code each repository, you can just instantiate them by the type!

And after this, if you have any particular method specific to some of your entities, you can all inherit from the Repository and override or add methods and properties as nedded.

If you want to use the Repository Pattern, i also suggest that you use the IUnitOfWork pattern, and keep it separated from the repository.

Both interfaces should look something like this:

The very simple IUnitOfWork:

Public interface IUnitOfWork
{
    bool Save();
}

And them, the Repository interface, using generic:

public interface IRepository<TEntity> : IDisposable where TEntity : class

    IUnitOfWork Session { get;}

    IList<TEntity> GetAll();
    IList<TEntity> GetAll(string[] include);
    IList<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate);

    bool Add(TEntity entity);
    bool Delete(TEntity entity);
    bool Update(TEntity entity);
    bool IsValid(TEntity entity);
}

The methods .Add(), .Delete() should not send anything to the database, but they should always send the changes to the IUnitOfWork (that you can implement in you DAL class), and only when you call the .Save() method of the IUnitOfWork you will save things to the Database.

I have implemented my Repository class using EntityFramework, and that makes things easier, but you can do it any way you want.

The code you will use will be somethin like this:

void SomeMethod()
{
    using (IUnitOfWork session = new YourUnitOfWorkImplementation())
    {
        using (var rep = new Repository<Client>(session))
        {
            var client1 = new Client("Bob");
            var client2 = new Cliente("John");
            rep.Add(client1);
            rep.Add(client2);
            var clientToDelete = rep.GetAll(c=> c.Name == "Frank").FirstOrDefaut();
            rep.Delete(clientToDelete);

            //Now persist the changes to the database
            session.Save();

        {
    {
}

Like i said, with EF and the DbContext, this is a lot easier, to here are a small part of my Repository class:

public class Repository : Component, IRepository
{


    protected DbContext session;
    {
        get
        {
            if (session == null)
                throw new InvalidOperationException("A session IUnitOfWork do repositório não está instanciada.");
            return (session as IUnitOfWork);
        }
    }

    public virtual DbContext Context
    {
        get
        {
            return session;
        }
    }

    public Repository()
        : base()
    {
    }

    public Repository(DbContext instance)
        : this(instance as IUnitOfWork)
    {


    #endregion


    public IList<TEntity> GetAll<TEntity>() where TEntity : class
    {
        return session.Set<TEntity>().ToList();
    }


    public bool Add<TEntity>(TEntity entity) where TEntity : class
    {
        if (!IsValid(entity))
            return false;
        try
        {
            session.Set(typeof(TEntity)).Add(entity);
            return session.Entry(entity).GetValidationResult().IsValid;
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null)
                throw new Exception(ex.InnerException.Message, ex);
            throw new Exception(ex.Message, ex);
        }
    } ...

This way you dont need to build a GetEmployeeByAge, you would simply write:

IEnumerable<Employee> GetEmployee(int age)
{
 return  rep.GetAll<Employee>(e=> e.Age == age);
}

Or you could just call directly (no need to create the method)

like image 74
Gabriel Vonlanten C. Lopes Avatar answered Oct 19 '22 18:10

Gabriel Vonlanten C. Lopes