Implementing Repository Pattern And Dependency Injection In ADO.NET Using Generics In C#

Nowadays, I am trying to learn different design patterns in object oriented paradigm that are pretty useful to implement generic solutions for different scenarios. Few weeks ago for a job hunt, I got an assignment to develop a web application that would interact with database, so I took it up as a challenge and decided to make it loosely coupled using design patterns which were applicable in that scenario.

One of them which is implemented in my assignment is repository pattern using generics and with that Dependency Injection using which I injected dependencies of Repository class via constructor.

I made a generic class which would be inherited by other types against the different tables in the application. In this class, I have used different framework features like Reflection and Generics.

My generic class is an abstract class, so it needs to be inherited for making use of it. You will see next how we will use it.

Here is the Repository class:

  1. public abstract class Repository<tentity> where TEntity : new()  
  2. {  
  3.     DbContext _context;  
  4.   
  5.     public Repository(DbContext context)  
  6.     {  
  7.         _context = context;  
  8.     }  
  9.   
  10.     protected DbContext Context   
  11.     {   
  12.         get  
  13.         {  
  14.           return this._context;  
  15.         }   
  16.     }  
  17.   
  18.     protected IEnumerable<tentity> ToList(IDbCommand command)  
  19.     {  
  20.         using (var record = command.ExecuteReader())  
  21.         {  
  22.             List<tentity> items = new List<tentity>();  
  23.             while (record.Read())  
  24.             {  
  25.                       
  26.                 items.Add(Map<tentity>(record));  
  27.             }  
  28.             return items;  
  29.         }  
  30.     }  
  31.           
  32.     protected TEntity Map<tentity>(IDataRecord record)  
  33.     {  
  34.         var objT = Activator.CreateInstance<tentity>();  
  35.         foreach (var property in typeof(TEntity).GetProperties())  
  36.         {  
  37.             if (record.HasColumn(property.Name) && !record.IsDBNull(record.GetOrdinal(property.Name)))  
  38.                 property.SetValue(objT, record[property.Name]);  
  39.         }  
  40.         return objT;  
  41.     }  
  42. }  
Now, I have table in database User whose schema is:
  1. CREATE TABLE [dbo].[tblUser] (  
  2.     [UserID]    INT           IDENTITY (1, 1) NOT NULL,  
  3.     [FirstName] NVARCHAR (25) NULL,  
  4.     [LastName]  NVARCHAR (25) NULL,  
  5.     [UserName]  NVARCHAR (25) NULL,  
  6.     [Password]  NVARCHAR (25) NULL,  
  7.     [IsActive]  BIT           NULL,  
  8.     [IsDeleted] BIT           NULL,  
  9.     [CreatedBy] INT           NULL,  
  10.     [CreatedAt] DATETIME      NULL,  
  11.     [UpdatedBy] INT           NULL,  
  12.     [UpdatedAt] DATETIME      NULL,  
  13.     [Email]     NVARCHAR (50) NULL,  
  14.     PRIMARY KEY CLUSTERED ([UserID] ASC)  
  15. );  
Against this table, I have Model class for mapping from table to that type which looks like the following,
  1. public class User  
  2. {  
  3.     public int UserID { getset; }  
  4.   
  5.     public string FirstName { getset; }  
  6.   
  7.     public string LastName { getset; }  
  8.   
  9.     public string UserName { getset; }  
  10.   
  11.     public string Password { getset; }  
  12.   
  13.     public bool IsActive { getset; }  
  14.   
  15.     public bool IsDeleted { getset; }  
  16.   
  17.     public DateTime CreatedAt { getset; }  
  18.   
  19.     public int CreatedBy { getset; }  
  20.   
  21.     public DateTime UpdatedAt { getset; }  
  22.   
  23.     public int UpdatedBy { getset; }  
  24.   
  25.     public string Email { getset; }  
  26. }  
We want to fetch data from User table for which we will create a Repository class for User type and then we will write implementation to fetch records from User table from database. All our methods that need to get data, insert data, update data or delete data from User table will reside in the UserRepository class.

Here is the implementation of User Repository class:
  1. public class UserRepository : Repository  
  2. {  
  3.     private DbContext _context;  
  4.     public UserRepository(DbContext context)  
  5.         : base(context)  
  6.     {  
  7.         _context = context;  
  8.     }  
  9.   
  10.     public IList GetUsers()  
  11.     {  
  12.         using (var command = _context.CreateCommand())  
  13.         {  
  14.             command.CommandText = "exec [dbo].[uspGetUsers]";  
  15.   
  16.             return this.ToList(command).ToList();  
  17.         }  
  18.     }  
  19.   
  20.     public User CreateUser(User user)  
  21.     {  
  22.         using (var command = _context.CreateCommand())  
  23.         {  
  24.             command.CommandType = CommandType.StoredProcedure;  
  25.             command.CommandText = "uspSignUp";  
  26.   
  27.             command.Parameters.Add(command.CreateParameter("@pFirstName", user.FirstName));  
  28.             command.Parameters.Add(command.CreateParameter("@pLastName", user.LastName));  
  29.             command.Parameters.Add(command.CreateParameter("@pUserName", user.UserName));  
  30.             command.Parameters.Add(command.CreateParameter("@pPassword", user.Password));  
  31.             command.Parameters.Add(command.CreateParameter("@pEmail", user.Email));  
  32.   
  33.             return this.ToList(command).FirstOrDefault();  
  34.         }  
  35.     }  
  36.   
  37.     public User LoginUser(string id, string password)  
  38.     {  
  39.         using (var command = _context.CreateCommand())  
  40.         {  
  41.             command.CommandType = CommandType.StoredProcedure;  
  42.             command.CommandText = "uspSignIn";  
  43.   
  44.             command.Parameters.Add(command.CreateParameter("@pId", id));  
  45.             command.Parameters.Add(command.CreateParameter("@pPassword", password));  
  46.   
  47.             return this.ToList(command).FirstOrDefault();  
  48.         }  
  49.     }  
  50.   
  51.     public User GetUserByUsernameOrEmail(string username, string email)  
  52.     {  
  53.         using (var command = _context.CreateCommand())  
  54.         {  
  55.             command.CommandType = CommandType.StoredProcedure;  
  56.             command.CommandText = "uspGetUserByUsernameOrEmail";  
  57.   
  58.             command.Parameters.Add(command.CreateParameter("@pUsername", username));  
  59.             command.Parameters.Add(command.CreateParameter("@pEmail", email));  
  60.   
  61.             return this.ToList(command).FirstOrDefault();  
  62.         }  
  63.     }  
  64. }  
We are done for the UserRepository for now, I have added methods and wrote a Stored Procedure to complete the assignment. Now, I will tell how to make use of it in the Service Layer or in Business Rule to do operations.

Firstly, create an interface named IUserService:
  1. [ServiceContract]  
  2. public interface IUserService  
  3. {  
  4.     [OperationContract]  
  5.     IList GetUsers();  
  6.   
  7.     [OperationContract]  
  8.     User RegisterUser(User user);  
  9.   
  10.     [OperationContract]  
  11.     User Login(string id, string password);  
  12.   
  13.     [OperationContract]  
  14.     bool UserNameExists(string username, string email);  
  15. }  
Here is my WCF Service for user that calls the UserRepository for doing operations,
  1. public class UserService : IUserService  
  2. {  
  3.     private IConnectionFactory connectionFactory;  
  4.   
  5.     public IList<user> GetUsers()  
  6.     {  
  7.         connectionFactory = ConnectionHelper.GetConnection();  
  8.   
  9.         var context = new DbContext(connectionFactory);  
  10.   
  11.         var userRep = new UserRepository(context);  
  12.   
  13.         return userRep.GetUsers();  
  14.     }  
  15.   
  16.     public User RegisterUser(User user)  
  17.     {  
  18.         connectionFactory = ConnectionHelper.GetConnection();  
  19.   
  20.         var context = new DbContext(connectionFactory);  
  21.   
  22.         var userRep = new UserRepository(context);  
  23.   
  24.         return userRep.CreateUser(user);  
  25.     }  
  26.   
  27.     public User Login(string id, string password)  
  28.     {  
  29.         connectionFactory = ConnectionHelper.GetConnection();  
  30.   
  31.         var context = new DbContext(connectionFactory);  
  32.   
  33.         var userRep = new UserRepository(context);  
  34.   
  35.         return userRep.LoginUser(id, password);  
  36.     }  
  37.   
  38.     public bool UserNameExists(string username, string email)  
  39.     {  
  40.         connectionFactory = ConnectionHelper.GetConnection();  
  41.   
  42.         var context = new DbContext(connectionFactory);  
  43.   
  44.         var userRep = new UserRepository(context);  
  45.   
  46.         var user = userRep.GetUserByUsernameOrEmail(username, email);  
  47.         return !(user != null && user.UserID > 0);  
  48.   
  49.     }  
  50. }  
You can see that when creating instance of UserRepository, I am injecting database context via constructor and then I am calling different methods from userRepository according to need.

Now in future, when I add another table in database, I will create another Repository type and implement its Data Access logic and will call it in the same way, so applying Dependency Injection and Repository pattern, we are following DRY principle to some extent, but I am sure we can make it better than this.

 

Up Next
    Ebook Download
    View all
    Learn
    View all