EntityFrameWork Operations

Introduction

Data Access Layer is the layer where CRUD operations on the database happen and lays between Database and Business Layers.

  • Services implemented in this layer are being called from the upper layers, i.e. Business layer in the above diagram.
  • Operations provided by the services in this layer should be straightforward with no logic or validations on the data or try/catch; consequently, all this should be done in the upper layers.

Let’s dig in!

To show the goal for this abstraction blog, let’s take a look at the two methods below. The below code snippets are using EntityFrameWork 6 & MS SQL Database.

public async Task<EmployeeData> GetEmployeeAsync(int id)
{
   var employee = await _northwindEntities.Employees.FirstOrDefaultAsync(p => p.EmployeeID == id);
   if (employee!= null)
      return mapper.Map<EmployeeData>(employee);
   return null;
}

public async Task<CustomerData> GetCustomerAsync(int id)
{
   var customer = await _northwindEntities.Customers.FirstOrDefaultAsync(p => p.CustomerID == id);
   if (user != null)
       return mapper.Map<CustomerData>(customer);
   return null;
}

As we can see, both functions have the same objective which is finding an object in the database with the only difference is the table we are searching in: Customers & Employees. So, let’s move on and unify the code using a utility class DataOperationHandler and then call it in our data access services. The results of the unification can be seen below.

// _operationHandler is an object of type DataOperationHandler.
public async Task<EmployeeData> GetEmployeeAsync(int id)
{
    var employee = await _operationHandler.GetAsync(_northwindEntities.Employees, _mapper, id, "EmployeeID");
    return employee;
}

public async Task<CustomerData> GetCustomerAsync(int id)
{
    var customer = await _operationHandler.GetAsync(_northwindEntities.Customers, _mapper, id, "CustomerID");
     return customer;
}




Task<TReturn> GetAsync(DbSet<TEntity> dbSet, string idPropertyName, object idPropertyValue, IMapper mapper);
Task<List<TReturn>> GetListAsync(DbSet<TEntity> dbSet, IMapper mapper);
Task<TReturn> CreateAsync(DbContext context, DbSet<TEntity> dbSet, TReturn newRecord, IMapper mapper);
Task DeleteAsync(DbContext context, DbSet<TEntity> dbSet, string idPropertyName, object idPropertyValue);
Task UpdateAsync(DbContext context, DbSet<TEntity> dbSet, string idPropertyName, TReturn newValues, IMapper mapper);
Task UpdateAsync(DbContext context, DbSet<TEntity> dbSet, string idPropertyName, object newValues);

TReturn is the class type that the service will pass and received from upper layers while TEntity is the type of the DbSet like Employees in the example above.

The complete code for DataOperationHandler can be found here: https://gitlab.com/alwaqfi/dacrudabstraction

If you feel there are improvements or generalization can be done please leave a comment or create a pull request on the Issue Tracker.

 Lastly, Thanks for visiting this post and I hope you enjoyed it.