C#存储库实现,需要有关通用修复的建议

时间:2011-09-08 11:49:17

标签: c# asp.net-mvc entity-framework generics repository-pattern

[已编辑:以下实体由实体框架生成]

我正在尝试实现通用存储库。下面是一些定义特殊特征的接口。

namespace AnimeVoter.DataLayer.Repositories
{
    internal interface ICanCreate<TEntity>
    {
        void Create(TEntity entity);
    }
    internal interface ICanUpdate<TEntity>
    {
        bool Update(TEntity entity);
    }
    internal interface ICanDelete<TEntity>
    {
        bool Delete(TEntity entity);
    }
    internal interface ICanGetList<TEntity>
    {
        IEnumerable<TEntity> GetList();
    }
    internal interface ICanGetById<TEntity>
    {
        TEntity GetById(int id);
    }
}

现在我还有一个抽象类,它结合了下面的特征。

namespace AnimeVoter.DataLayer.Repositories
{
    public abstract class CrudRepository<TEntity> : 
        ICanCreate<TEntity>,
        ICanUpdate<TEntity>,
        ICanDelete<TEntity>,
        ICanGetList<TEntity>,
        ICanGetById<TEntity>
    {
        public abstract void Create(TEntity entity);
        public abstract bool Update(TEntity entity);
        public abstract bool Delete(TEntity entity);
        public abstract IEnumerable<TEntity> GetList();
        public abstract TEntity GetById(int id);
    }
}

然后我有10-15个具体类使用上面的抽象。我只会展示两个。我还将讨论限制在常用方法Create()。

下面是数据库中的User表:

namespace AnimeVoter.DataLayer.Repositories.Impl
{
    public class UserRepository : CrudRepository<User>, IDisposable
    {
        DbEntities db = new DbEntities();

        public override void Create(User entity)
        {
            db.Users.AddObject(entity);
        }

        ...

以下是数据库中的Title表:

namespace AnimeVoter.DataLayer.Repositories.Impl
{
    public class TitleRepository : CrudRepository<Title>, IDisposable
    {
        DbEntities db = new DbEntities();

        public override void Create(Title entity)
        {
            db.Titles.AddObject(entity);
        }

        ...

所以有问题!当我向User表添加新记录时,我执行 db.Users.AddObject(entity)。当添加到Title表时,我执行 db.Titles.AddObject(entity)

我现在想知道如何使用泛型来重构这一点,所以我可以做一些像 db&lt;“TableName”&gt; .AddObject(entity)之类的东西,或者其他任何东西,这样我就可以了所有表的一个实现,而不是每个表都有很多实现?

4 个答案:

答案 0 :(得分:2)

您需要做的主要事情是为实体创建一个ObjectSet,然后针对该ObjectSet实例执行操作。以下是有关如何创建处理所有CRUD操作的通用存储库的完整示例。

public class TitleRepository : CrudRepository<Title>
{
    public TitleRepository()
        : base(new DbEntities())
    {
    }
}

public abstract class CrudRepository<TEntity> :
    ICanCreate<TEntity>,
    ICanUpdate<TEntity>,
    ICanDelete<TEntity>,
    ICanGetList<TEntity>,
    ICanGetById<TEntity>
    where TEntity : EntityObject
{
    private readonly ObjectSet<TEntity> _objectSet;
    private readonly string _primaryKey;

    protected CrudRepository(ObjectContext context)
    {
        this._objectSet = context.CreateObjectSet<TEntity>();
        this._primaryKey = this.GetPrimaryKeyPropertyName();
    }

    public void Create(TEntity entity)
    {
        this._objectSet.AddObject(entity);
        this._objectSet.Context.SaveChanges();
    }

    public bool Update(TEntity entity)
    {
        if (entity.EntityState == EntityState.Detached)
        {
            this._objectSet.Attach(entity);
        }

        this._objectSet.Context.SaveChanges();
        return true;
    }

    public bool Delete(TEntity entity)
    {
        this._objectSet.DeleteObject(entity);
        this._objectSet.Context.SaveChanges();
        return true;
    }

    public IEnumerable<TEntity> GetList()
    {
        return this._objectSet.ToList();
    }

    public TEntity GetById(int id)
    {
        return this._objectSet.Where(this.CreateGetByIdExpression(id)).FirstOrDefault();
    }

    // Build an Expression that can be used to query an Entity by Id.
    private Expression<Func<TEntity, bool>> CreateGetByIdExpression(object id)
    {
        ParameterExpression e = Expression.Parameter(typeof(TEntity), "e");
        PropertyInfo pi = typeof(TEntity).GetProperty(this._primaryKey);
        MemberExpression m = Expression.MakeMemberAccess(e, pi);
        ConstantExpression c = Expression.Constant(id, id.GetType());
        BinaryExpression b = Expression.Equal(m, c);
        Expression<Func<TEntity, bool>> lambda = Expression.Lambda<Func<TEntity, bool>>(b, e);

        return lambda;
    }

    // Use the EF metadata to get the primary key property name.
    private string GetPrimaryKeyPropertyName()
    {
        return this._objectSet.Context
                              .MetadataWorkspace
                              .GetEntityContainer(this._objectSet.Context.DefaultContainerName, DataSpace.CSpace)
                              .BaseEntitySets
                              .First(meta => meta.ElementType == this._objectSet.EntitySet.ElementType)
                              .ElementType.KeyMembers
                              .Select(k => k.Name)
                              .FirstOrDefault();
    }
}

答案 1 :(得分:1)

要实现此目的,您必须在实体类型和db表之间定义此类映射。您可以考虑DbContractFactory之类的内容并将其注入基类CrudRepository<TEntity>类,这样它就能够在运行时根据当前实体类型TEntity检索表引用/名称,如

dbContractFactory.GetDbContract<TEntity>()

通过这种方式,您可以通过在此类工厂/地图中存储所有关系来将db-specifics与实体实现本身分开。

编辑:示例

interface IDbContract
{
   string TableName { get; }
}

public sealed class DbContractFactory
{
   private readonly IDictionary<Type, IDbContract> dbContractMap;

   public void RegisterContract<TEntity>(IDbContract)
   {
         // store in dbContractMap
   }

   public IDbContract GetDbContract<TEntity>()
   {  
       if (dbContractMap.Contains(typeof(TEntity))
       {
          // retrieve and return
       }    
   }
}

答案 2 :(得分:1)

只需用CreateObjectSet替换db.Title 我是这样做的:

public class CrudRepository<TEntity> where TEntity : class
{
    DbEntities db = new DbEntities();

    public override void Create(TEntity entity)
    {
        db.CreateObjectSet<TEntity>().AddObject(entity);
    }
}

编辑: 忘了哪里......

答案 3 :(得分:0)

我在这里找到答案http://geekswithblogs.net/seanfao/archive/2009/12/03/136680.aspx。这非常好,因为它消除了EF映射的每个表的多个存储库对象,特别是对于像CRUD这样的普通操作,这正是我正在寻找的。