1

我们开始在我们公司开发一个小框架,在不同的应用程序之间共享代码。对于数据访问,我们使用的是 EF4。我们有一个自定义 DbContext 类和一个通用存储库:

public class RMDbContext : DbContext
{
    // ....
}

public interface IRepository 
{
    IQueryable<T> All();
    void Delete(T entity) where T : class;
    void Add(T entity) where T : class;
    void Update(T entity) where T : class;
    int SaveChanges();
    void RollbackChanges();
}

这里的问题是如何使用我们自定义的 DbContext 类 (RMDbContext) 来实现存储库。我的同事认为最好的方法是让 RMDbContext 实现 IRepository 接口:

public class RMDbContext : DbContext, IRepository
{
    // ....
}

老实说,我不喜欢这种方法,因为上下文与特定的合同(IRepository)相关联。IMO 最好创建一个使用 RMDbContext 的存储库实现,如下所示:

public class Repository<T> : IRepository where T : RMDbContext, new()
{
    protected readonly RMDbContext context;

    public class Repository()
    {
         context = new T();
    }

    // ....
}

您如何看待这两种方法?你会选择哪一个,为什么?

4

4 回答 4

4

我个人会鼓励你们不要创建任何东西,只需使用 dbContext,它有你需要的所有方法。

我自己实现了#1(实现 IRepository),但你最终会做一些时髦的编程来获得正确的 ObjectSet 或 EntitySet 以从你的 Add/Delete 方法中添加或删除。

随着您在对象模型中添加继承层次结构,该代码将变得越来越复杂。

于 2011-11-14T22:13:36.733 回答
3

我们在工作中所做的是实现这样的模式:

interface ICRUD<T> : ICreatable<T>, IRetrievable<T>, IUpdatable<T>, IDeletable<T>
{
}

interface ICreatable<T>
{
    T Create();
}

interface IRetrieve<T>
{
    T Retrieve(params object[] keys);
}

interface IUpdatable<T>
{
    void Update(T existing);
}

interface ICreatable<T>
{
    void Delete(T existing);
}

然后我们创建了一个实体驱动的基础存储库:

public abstract class BaseRepository<TModel, TEntities> where TEntities : IDbSet<TModel>
{
    protected TEntities Entities {get; set;}
    protected DbContext Db {get; set;}

    public BaseRepository (DbContext db, TEntities entities)
    {
        Db = db;
        Entities = entities;
    }

    public virtual TModel Create() { return Entities.Create (); }
    public virtual TModel Retrieve (params object[] keys) { return Entities.Find (keys); }
    public virtual void Update (TModel existing) { Db.Entry(existing).State = Modified; }
    public virtual void Delete (TModel existing) { Db.Entry(existing).State = Removed; }
}

如果您注意到,BaseRepository 实际上并没有使用 ICRUD,只是具有相同的方法签名。由于我们对接口进行编码,这让我们可以使用大量共享代码,而不会暴露我们不希望使用基类的功能。开发人员可以在不了解实体的情况下随意实现数据存储(例如 ICRUD 可以与 Web 服务通信),或者他们也可以通过覆盖任何提供的方法来自由地增强 BaseRepository 提供的行为和做一些不同的事情。

于 2011-11-15T00:54:31.083 回答
2

首先,您不应该将存储库与您的 RMDbContext 紧密耦合,因为这是一种不好的设计气味,您应该始终使用接口。

其次,如果存在,存储库不应实现 IRMDbContext 接口,因为存储库不需要它。你应该使用它而不是实现它,所以如果你创建你的 Repository 最好在他的构造函数中使用 IRMDbContext 的参数,如下所示

公共类存储库{

protected readonly IRMDbContext context;

public class Repository(IRMDbContext rMDbContext)
{
    this.context = rMDbContext;
}

工作单元类是实例化上下文并将其发送到存储库的类,请参见以下链接

EF 工作单元

于 2011-11-15T00:49:40.547 回答
2

EF Repositry 最佳实践...
Entity Framework - Code First 模型方法,为数据库表创建 POCO 实体。
此模型可用于 WCF 数据协定或自定义属性。
这是我使用依赖注入的迷人选择

IRepository 接口

/// <summary>
/// Repository
/// </summary>
public partial interface IRepository<T> where T : BaseEntity
{
    /// <summary>
    /// Returns the queryable entity set for the given type {T}.
    /// </summary>
    IQueryable<T> Table { get; }

    /// <summary>
    /// Creates a new instance of an entity of type {T}
    /// </summary>
    /// <returns>The new entity instance.</returns>
    T Create();

    /// <summary>
    /// Gets an entity by id from the database or the local change tracker.
    /// </summary>
    /// <param name="id">The id of the entity. This can also be a composite key.</param>
    /// <returns>The resolved entity</returns>
    T GetById(object id);

    /// <summary>
    /// Marks the entity instance to be saved to the store.
    /// </summary>
    /// <param name="entity">An entity instance that should be saved to the database.</param>
    /// <remarks>Implementors should delegate this to the current <see cref="IDbContext" /></remarks>
    void Insert(T entity);

    /// <summary>
    /// Marks multiple entities to be saved to the store.
    /// </summary>
    /// <param name="entities">The list of entity instances to be saved to the database</param>
    /// <param name="batchSize">The number of entities to insert before saving to the database (if <see cref="AutoCommitEnabled"/> is true)</param>
    void InsertRange(IEnumerable<T> entities, int batchSize = 100);

    /// <summary>
    /// Marks the changes of an existing entity to be saved to the store.
    /// </summary>
    /// <param name="entity">An instance that should be updated in the database.</param>
    /// <remarks>Implementors should delegate this to the current <see cref="IDbContext" /></remarks>
    void Update(T entity);

    /// <summary>
    /// Marks an existing entity to be deleted from the store.
    /// </summary>
    /// <param name="entity">An entity instance that should be deleted from the database.</param>
    /// <remarks>Implementors should delegate this to the current <see cref="IDbContext" /></remarks>
    void Delete(T entity);


    /// <summary>
    /// Returns the data context associated with the repository.
    /// </summary>
    /// <remarks>
    /// The context is likely shared among multiple repository types.
    /// So committing data or changing configuration also affects other repositories. 
    /// </remarks>
    IDbContext Context { get; }

    /// <summary>
    /// Gets or sets a value indicating whether database write operations
    /// such as insert, delete or update should be committed immediately.
    /// </summary>
    bool AutoCommitEnabled { get; set; }
}

执行

 /// <summary>
/// Entity Framework repository
/// </summary>
public partial class EfRepository<T> : IRepository<T> where T : BaseEntity
{

    #region Fields

    private readonly IDbContext _context;
    private IDbSet<T> _entities;

    #endregion

    #region Ctor

    public EfRepository(IDbContext context)
    {
        this._context = context;
        this.AutoCommitEnabled = true;
    }

    #endregion

    #region interface members

    public virtual IQueryable<T> Table
    {
        get
        {
            return this.Entities;
        }
    }

    public T Create()
    {
        return this.Entities.Create();
    }

    public T GetById(object id)
    {
        return this.Entities.Find(id);
    }

    public void Insert(T entity)
    {
        if (entity == null)
            throw new ArgumentNullException("entity");

        this.Entities.Add(entity);

        if (this.AutoCommitEnabled)
            _context.SaveChanges();
    }

    public void InsertRange(IEnumerable<T> entities, int batchSize = 100)
    {
        try
        {
            if (entities == null)
                throw new ArgumentNullException("entities");

            if (entities.HasItems())
            {
                if (batchSize <= 0)
                {
                    // insert all in one step
                    entities.Each(x => this.Entities.Add(x));
                    if (this.AutoCommitEnabled)
                        _context.SaveChanges();
                }
                else
                {
                    int i = 1;
                    bool saved = false;
                    foreach (var entity in entities)
                    {
                        this.Entities.Add(entity);
                        saved = false;
                        if (i % batchSize == 0)
                        {
                            if (this.AutoCommitEnabled)
                                _context.SaveChanges();
                            i = 0;
                            saved = true;
                        }
                        i++;
                    }

                    if (!saved)
                    {
                        if (this.AutoCommitEnabled)
                            _context.SaveChanges();
                    }
                }
            }
        }
        catch (DbEntityValidationException ex)
        {
            throw ex;
        }
    }

    public void Update(T entity)
    {
        if (entity == null)
            throw new ArgumentNullException("entity");

        if (this.AutoCommitEnabled)
        {
            _context.SaveChanges();
        }
        else
        {
            try
            {
                this.Entities.Attach(entity);
                InternalContext.Entry(entity).State = System.Data.EntityState.Modified;
            }
            finally { }
        }
    }

    public void Delete(T entity)
    {
        if (entity == null)
            throw new ArgumentNullException("entity");

        if (InternalContext.Entry(entity).State == System.Data.EntityState.Detached)
        {
            this.Entities.Attach(entity);
        }

        this.Entities.Remove(entity);

        if (this.AutoCommitEnabled)
            _context.SaveChanges();
    }


    public IDbContext Context
    {
        get { return _context; }
    }

    public bool AutoCommitEnabled { get; set; }

    #endregion

    #region Helpers

    protected internal ObjectContextBase InternalContext
    {
        get { return _context as ObjectContextBase; }
    }

    private DbSet<T> Entities
    {
        get
        {
            if (_entities == null)
            {
                _entities = _context.Set<T>();
            }
            return _entities as DbSet<T>;
        }
    }

    #endregion

}

使用这个我们可以在将来添加另一个存储库以及实现。这不会影响任何现有数据。尝试一下.....

于 2014-05-02T06:57:33.673 回答