65.9K
CodeProject 正在变化。 阅读更多。
Home

在C#中使用泛型在ADO.NET中实现存储库模式和依赖注入

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (14投票s)

2015 年 10 月 9 日

CPOL

2分钟阅读

viewsIcon

103094

downloadIcon

3857

在ADO.NET中使用泛型实现存储库模式

如今,我正在尝试学习面向对象范式中不同的设计模式,这些模式对于为不同的场景实现通用解决方案非常有用。几周前,为了找工作,我接到了一项任务,要做一个与数据库交互的Web应用程序,所以我把它作为一个挑战,并决定使用适用于该场景的设计模式来实现松耦合。

其中一个在我的作业中实现的是使用泛型的存储库模式,并使用依赖注入,通过构造函数注入存储库类的依赖项。

我创建了一个通用类,该类将由应用程序中不同表的其他类型继承。在这个类中,我使用了不同的框架特性,如反射和泛型。

我的通用类是一个abstract类,因此需要被继承才能使用它。您将在后面看到我们将如何使用它。

这是Repository

public abstract class Repository<tentity> where TEntity : new()
{
    DbContext _context;

    public Repository(DbContext context)
    {
        _context = context;
    }

    protected DbContext Context 
    { 
        get
        {
          return this._context;
        } 
    }

    protected IEnumerable<tentity> ToList(IDbCommand command)
    {
        using (var record = command.ExecuteReader())
        {
            List<tentity> items = new List<tentity>();
            while (record.Read())
            {
                    
                items.Add(Map<tentity>(record));
            }
            return items;
        }
    }
        
    protected TEntity Map<tentity>(IDataRecord record)
    {
        var objT = Activator.CreateInstance<tentity>();
        foreach (var property in typeof(TEntity).GetProperties())
        {
            if (record.HasColumn(property.Name) && !record.IsDBNull(record.GetOrdinal(property.Name)))
                property.SetValue(objT, record[property.Name]);
        }
        return objT;
    }
}

现在,我在数据库中有表User,其模式是

CREATE TABLE [dbo].[tblUser] (
    [UserID]    INT           IDENTITY (1, 1) NOT NULL,
    [FirstName] NVARCHAR (25) NULL,
    [LastName]  NVARCHAR (25) NULL,
    [UserName]  NVARCHAR (25) NULL,
    [Password]  NVARCHAR (25) NULL,
    [IsActive]  BIT           NULL,
    [IsDeleted] BIT           NULL,
    [CreatedBy] INT           NULL,
    [CreatedAt] DATETIME      NULL,
    [UpdatedBy] INT           NULL,
    [UpdatedAt] DATETIME      NULL,
    [Email]     NVARCHAR (50) NULL,
    PRIMARY KEY CLUSTERED ([UserID] ASC)
);

针对此表,我有一个Model类用于从表映射到该类型,它看起来像

public class User
{
    public int UserID { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string UserName { get; set; }

    public string Password { get; set; }

    public bool IsActive { get; set; }

    public bool IsDeleted { get; set; }

    public DateTime CreatedAt { get; set; }

    public int CreatedBy { get; set; }

    public DateTime UpdatedAt { get; set; }

    public int UpdatedBy { get; set; }

    public string Email { get; set; }
}

我们希望从User表中获取数据,为此我们将为User类型创建一个Repository类,然后我们将编写实现以从数据库中的User表中获取记录。所有需要获取数据、插入数据、更新数据或删除User表中的数据的我们的方法都将驻留在UserRepository类中。

这是UserRepository类的实现

public class UserRepository : Repository
{
    private DbContext _context;
    public UserRepository(DbContext context)
        : base(context)
    {
        _context = context;
    }

    public IList GetUsers()
    {
        using (var command = _context.CreateCommand())
        {
            command.CommandText = "exec [dbo].[uspGetUsers]";

            return this.ToList(command).ToList();
        }
    }

    public User CreateUser(User user)
    {
        using (var command = _context.CreateCommand())
        {
            command.CommandType = CommandType.StoredProcedure;
            command.CommandText = "uspSignUp";

            command.Parameters.Add(command.CreateParameter("@pFirstName", user.FirstName));
            command.Parameters.Add(command.CreateParameter("@pLastName", user.LastName));
            command.Parameters.Add(command.CreateParameter("@pUserName", user.UserName));
            command.Parameters.Add(command.CreateParameter("@pPassword", user.Password));
            command.Parameters.Add(command.CreateParameter("@pEmail", user.Email));

            return this.ToList(command).FirstOrDefault();
        }
    }

    public User LoginUser(string id, string password)
    {
        using (var command = _context.CreateCommand())
        {
            command.CommandType = CommandType.StoredProcedure;
            command.CommandText = "uspSignIn";

            command.Parameters.Add(command.CreateParameter("@pId", id));
            command.Parameters.Add(command.CreateParameter("@pPassword", password));

            return this.ToList(command).FirstOrDefault();
        }
    }

    public User GetUserByUsernameOrEmail(string username, string email)
    {
        using (var command = _context.CreateCommand())
        {
            command.CommandType = CommandType.StoredProcedure;
            command.CommandText = "uspGetUserByUsernameOrEmail";

            command.Parameters.Add(command.CreateParameter("@pUsername", username));
            command.Parameters.Add(command.CreateParameter("@pEmail", email));

            return this.ToList(command).FirstOrDefault();
        }
    }
}

我们现在已经完成了UserRepository,我添加了方法并编写了一个存储过程来完成这项作业。现在,我将告诉您如何在服务层或业务规则中使用它来执行操作。

首先,创建一个名为IUserService的接口

[ServiceContract]
public interface IUserService
{
    [OperationContract]
    IList GetUsers();

    [OperationContract]
    User RegisterUser(User user);

    [OperationContract]
    User Login(string id, string password);

    [OperationContract]
    bool UserNameExists(string username, string email);
}

这是我的用于User的WCF服务,它调用UserRepository来执行操作

public class UserService : IUserService
{
    private IConnectionFactory connectionFactory;

    public IList<user> GetUsers()
    {
        connectionFactory = ConnectionHelper.GetConnection();

        var context = new DbContext(connectionFactory);

        var userRep = new UserRepository(context);

        return userRep.GetUsers();
    }

    public User RegisterUser(User user)
    {
        connectionFactory = ConnectionHelper.GetConnection();

        var context = new DbContext(connectionFactory);

        var userRep = new UserRepository(context);

        return userRep.CreateUser(user);
    }

    public User Login(string id, string password)
    {
        connectionFactory = ConnectionHelper.GetConnection();

        var context = new DbContext(connectionFactory);

        var userRep = new UserRepository(context);

        return userRep.LoginUser(id, password);
    }

    public bool UserNameExists(string username, string email)
    {
        connectionFactory = ConnectionHelper.GetConnection();

        var context = new DbContext(connectionFactory);

        var userRep = new UserRepository(context);

        var user = userRep.GetUserByUsernameOrEmail(username, email);
        return !(user != null && user.UserID > 0);

    }
}

您可以看到,在创建UserRepository的实例时,我通过构造函数注入数据库上下文,然后根据需要从userRepository中调用不同的方法。

 

这是DbContext的实现

public class DbContext
    {
    
        private readonly IDbConnection _connection;
        private readonly IConnectionFactory _connectionFactory;
        private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
        private readonly LinkedList<AdoNetUnitOfWork> _uows = new LinkedList<AdoNetUnitOfWork>();
 
        public DbContext(IConnectionFactory connectionFactory)
        {
            _connectionFactory = connectionFactory;
            _connection = _connectionFactory.Create();
        }
 
        public IUnitOfWork CreateUnitOfWork()
        {
            var transaction = _connection.BeginTransaction();
            var uow = new AdoNetUnitOfWork(transaction, RemoveTransaction, RemoveTransaction);
 
            _rwLock.EnterWriteLock();
            _uows.AddLast(uow);
            _rwLock.ExitWriteLock();
 
            return uow;
        }
 
        public IDbCommand CreateCommand()
        {
            var cmd = _connection.CreateCommand();
 
            _rwLock.EnterReadLock();
            if (_uows.Count > 0)
                cmd.Transaction = _uows.First.Value.Transaction;
            _rwLock.ExitReadLock();
 
            return cmd;
        }
 
        private void RemoveTransaction(AdoNetUnitOfWork obj)
        {
            _rwLock.EnterWriteLock();
            _uows.Remove(obj);
            _rwLock.ExitWriteLock();
        }
 
        public void Dispose()
        {
            _connection.Dispose();
        }
    }

 

ConnectionFactory的实现是

public class DbConnectionFactory : IConnectionFactory
    {
 
        private readonly DbProviderFactory _provider;
        private readonly string _connectionString;
        private readonly string _name;
 
        public DbConnectionFactory(string connectionName)
        {
            if (connectionName == null) throw new ArgumentNullException("connectionName");
 
            var conStr = ConfigurationManager.ConnectionStrings[connectionName];
            if (conStr == null)
                throw new ConfigurationErrorsException(string.Format("Failed to find connection string named '{0}' in app/web.config.", connectionName));
 
            _name = conStr.ProviderName;
            _provider = DbProviderFactories.GetFactory(conStr.ProviderName);
            _connectionString = conStr.ConnectionString;
 
        }
 
        public IDbConnection Create()
        {
            var connection = _provider.CreateConnection();
            if (connection == null)
                throw new ConfigurationErrorsException(string.Format("Failed to create a connection using the connection string named '{0}' in app/web.config.", _name));
 
            connection.ConnectionString = _connectionString;
            connection.Open();
            return connection;
        }
    }

 

UnitOfWork的实现

public class AdoNetUnitOfWork : IUnitOfWork
    {
        private IDbTransaction _transaction;
        private readonly Action<AdoNetUnitOfWork> _rolledBack;
        private readonly Action<AdoNetUnitOfWork> _committed;
 
        public AdoNetUnitOfWork(IDbTransaction transaction, Action<AdoNetUnitOfWork> rolledBack, Action<AdoNetUnitOfWork> committed)
        {
            Transaction = transaction;
            _transaction = transaction;
            _rolledBack = rolledBack;
            _committed = committed;
        }
 
        public IDbTransaction Transaction { get; private set; }
 
        public void Dispose()
        {
            if (_transaction == null)
                return;
 
            _transaction.Rollback();
            _transaction.Dispose();
            _rolledBack(this);
            _transaction = null;
        }
 
        public void SaveChanges()
        {
            if (_transaction == null)
                throw new InvalidOperationException("May not call save changes twice.");
 
            _transaction.Commit();
            _committed(this);
            _transaction = null;
        }
    }

 

IConnectionFacotry和IUnitOfWork的接口看起来像

public interface IUnitOfWork
    {
        void Dispose();
 
        void SaveChanges();
    }
public interface IConnectionFactory
    {
        IDbConnection Create();
    }

 

 

现在将来,当我在数据库中添加另一个表时,我将创建另一个Repository类型并实现其数据访问逻辑,并以相同的方式调用它,因此应用依赖注入和存储库模式,我们在某种程度上遵循DRY原则,但我相信我们可以做得更好。

© . All rights reserved.