使用旧版ADO.NET实现通用仓储模式

12

因为平台限制,我正在尝试使用ado.net实现存储库模式。

public interface IGenericRepository<T> : IDisposable where T : class
{
    IQueryable<T> GetAll();
    IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
    void Add(T entity);
    void Delete(T entity);
    void Edit(T entity);
    void Save();
}

如何完成以下抽象类的实现?
public abstract class GenericRepository<C, T> :
    IGenericRepository<T>
    where T : class
    where C : IDbDataAdapter, new()
{

    private C dbDataAdapter = new C();
    protected C DB
    {
        get { return dbDataAdapter; }
        set { dbDataAdapter = value; }
    }

    public virtual IQueryable<T> GetAll()
    {
        DataTable dt;
        dbDataAdapter.fill(dt);
        IQueryable<T> query = dt....?;
        return query;
    }

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

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

更新:
我将通过继承这两个接口/类来实现指定域的存储库。

public class FooRepository :
    GenericRepository<FooBarEntities, Foo>, IFooRepository {

    public Foo GetSingle(int fooId) {

        var query = GetAll().FirstOrDefault(x => x.FooId == fooId);
        return query;
    }
}
4个回答

6
通常不建议采用通用存储库。存储库是一个重要的域概念,您不希望过度泛化它,就像您不希望泛化实体一样。通用存储库具有CRUD操作,会将焦点从您的域转移开来。请参阅Greg Young的这篇文章
另外,请注意,公开IQueryable会引入紧耦合问题,同时会使您的代码更加偏向于数据而非域驱动。详见此文

我已经更新了问题,以解释领域/数据驱动问题。 - ca9163d9

2

虽然不完全相关,但我有类似的问题,这是我的解决方案(可以帮助任何人)

创建身份(Identity)类:

public class Identity
    {
        public int Id { get; set; }
    }

创建实体类:
public class Employee: Identity
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public int Age { get; set; }
    }

仓库接口(也可以使用抽象类):

interface IRepository<T> where T: Identity
    {
        T GetById(int id);
        ICollection<T> GetAll();
        ICollection<T> GetAll(string where);
        void Update(T entity);
        void Insert(T entity);
        bool Delete(T entity);
        bool Delete(ICollection<T> entityes);
    }

创建通用抽象类:
public abstract class AbstractRepository<T>: IRepository<T> where T : Identity
    {
        protected abstract string TableName { get; }

        protected abstract T DataRowToModel(DataRow dr);

        protected virtual ICollection<T> DataTableToCollection(DataTable dt)
        {
            if (dt == null)
            {
                return null;
            }
            return dt.AsEnumerable().Select(x => DataRowToModel(x)).ToList();
        }

        public virtual T GetById(int id)
        {
            var query = $"select * from {TableName} where id = {id}";
            //the data access layer is implemented elsewhere
            DataRow dr = DAL.SelectDataRow(query); 
            return DataRowToModel(dr);
        }

        public virtual void Delete(T entity)
        {
            if (entity.Id == 0 )
            {
                return;
            }
            var query = $"delete from {TableName} where id = {entity.Id}";
            DAL.Query(query);
        }

        public virtual void Delete(ICollection<T> entityes)
        {
            var collectionId = IdentityCollectionToSqlIdFormat(entityes);
            if (string.IsNullOrEmpty(collectionId))
            {
                return;
            }
            var query = $"delete from {TableName} where id in ({collectionId})";
            DAL.Query(query);
        }

        public virtual ICollection<T> GetAll()
        {
            var query = $"select * from {TableName}";
            DataTable dt = DAL.SelectDataTable(query);
            return DataTableToCollection(dt);
        }

        public virtual ICollection<T> GetAll(string where)
        {
            var query = $"select * from {TableName} where {where}";
            DataTable dt = DAL.SelectDataTable(query);
            return DataTableToCollection(dt);
        }

        protected virtual string IdentityCollectionToSqlIdFormat(ICollection<T> collection)
        {
            var array = collection.Select(x => x.Id);
            return string.Join(",", array);
        }

        public abstract bool Update(T entity);
        public abstract bool Insert(T entity);
    }

并实现EmployeeRepository:

public class EmployeeRepository : AbstractRepository<Employe>
    {
        protected sealed override string TableName
        {
            get
            {
                return "dbo.Employees";
            }
        }

        protected sealed override Employe DataRowToModel(DataRow dr)
        {
            if (dr == null)
            {
                return null;
            }
            return new Employe
            {
                Id = dr.Field<int>("id"),
                Name = dr.Field<string>("name"),
                Surname = dr.Field<string>("surname"),
                Age = dr.Field<int>("age")

            };
        }


        public override void Insert(Employe entity)
        {
            var query = $@"insert into {TableName} (name, surname, age)
                            values({entity.Name},{entity.Surname},{entity.Age})";
            DAL.Query(query);
        }

        public override bool Update(Employe entity)
        {
            throw new NotImplementedException();
        }
    }

这就是全部。在代码中使用:

    public class SomeService
{
    public void SomeeMethod()
    {
     int employeeId = 10; // for example
     EmployeeRepository repository = new EmployeeRepository();
     Employee employee = repository.GetById(employeeId);
     repository.Delete(employee);
     //...
    }
    }

抱歉,不行。那是很久以前的事了。 - Alexander Brattsev

1
如果你正在从一个充满不一致性和大量存储过程的传统数据库转向ORM/Repository模式,那么你可能会发现实现通用存储库模式非常令人沮丧。我知道通用存储库模式在教程部分非常受欢迎,当许多新应用程序使用Entity Framework和Active Record来创建数据库时(请记住,这种风格意味着没有存储过程或最少使用存储过程)。在这些新场景中,数据往往更干净,因此很容易将其连接到某个通用存储库模式,因为每个实体基本上都有一个ID。

0

你正在尝试构建一个典型OR映射器的一部分。这是一项巨大的工作。为什么不直接使用ORM呢?


1
同意。有很多ORM可用。 - Glenn Ferrie
1
我正在为一个传统应用程序添加功能,不想引入任何新的 DLL。 - ca9163d9
1
好的,那么你需要一个人月的时间来实现FindBy方法。你需要从表达式中创建SQL字符串。这一点绝对不是小事。我建议你改变接口设计,不支持任意表达式。也许你可以支持where-select-orderby-top序列。也许这就足够了。 - usr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接