[已编辑:以下实体由实体框架生成]
我正在尝试实现通用存储库。下面是一些定义特殊特征的接口。
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)之类的东西,或者其他任何东西,这样我就可以了所有表的一个实现,而不是每个表都有很多实现?
答案 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这样的普通操作,这正是我正在寻找的。 p>