5

我一直在从各种来源学习存储库工作单元模式,包括这里:

http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net- mvc-应用程序

如果您浏览到上面的创建工作单元类的链接,则会出现以下内容:

    private GenericRepository<Department> departmentRepository;
    private GenericRepository<Course> courseRepository;

这很好,但我想尝试扩展 Generic 的 Unit of Work 类并设置 GenericRepositories 的集合,以便我可以根据我通过的模型动态更新。

我最终想在我的Controller中执行以下操作:

public class LedgerUserController : Controller
{
    private GenericUnitOfWork unitOfWork = new GenericUnitOfWork();
    LedgerUser ledgeruser = new LedgerUser();


    public ActionResult Index()
    {
        //var ledgerusers = db.LedgerUsers.Include(l => l.Image).Include(l => l.UserType);
        var view = unitOfWork.Repository(ledgeruser).Get(l => l.LastName == "smith");
        return View(view.ToList());
    }
}

所以到目前为止,这是我的类和接口:

IRepository.cs

/// <summary>
/// Generic Repository for CRUD Operations and methods
/// to locate entities within your store. This is not specific to which Data Access
/// tools your are using (Direct SQL, EF, NHibernate, etc).
/// </summary>
public interface IRepository<T> where T : class
{
    //--Search Operations
    IQueryable<T> GetAll();
    IEnumerable<T> GetAllList();
    IEnumerable<T> Get(Expression<Func<T,bool>> filter);
    T GetIt(Expression<Func<T, bool>> filter);
    T GetById(object id);


    //--CRUD Operations
    void Create(T entity);
    void Update(T entity);
    void Delete(T entity);

}

GenericRepository.cs

/// /// 用于查找实体的存储库类 /// CRUD 操作 /// /// public class GenericRepository : IRepository where TEntity : class {

    internal AccountsContext context;
    internal DbSet<TEntity> dbSet;
    internal IQueryable<TEntity> query;

    /// <summary>
    /// Default Constructor.
    /// </summary>
    /// <param name="context"></param>
    public GenericRepository(AccountsContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    #region Methods
    #region Search Functionality
    /// <summary>
    /// Obtain the whole Entity to query if needed.
    /// </summary>
    /// <returns>IQueryable object.</returns>
    public virtual IQueryable<TEntity> GetAll()
    {
        IQueryable<TEntity> query = dbSet;
        return query;

    }

    /// <summary>
    /// Obtain the whole Entity to Enumerate throught if needed.
    /// </summary>
    /// <returns>IEnumerble object.</returns>
    public virtual IEnumerable<TEntity> GetAllList()
    {
        IQueryable<TEntity> query = dbSet;
        return query.ToList();

    }

    /// <summary>
    /// Locate an Entity by its indexed id.
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual TEntity GetById(object id)
    {
        return dbSet.Find(id);
    }

    /// <summary>
    /// Gets a collection based on LINQ lambda expressions
    /// </summary>
    /// <param name="filter">Lambda Expression</param>
    /// <returns>Query</returns>
    public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter)
    {
        query = dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        return this.query.ToList();
    }

    /// <summary>
    /// Gets one record based on a one-to-one relationship.
    /// </summary>
    /// <param name="filter">Lambda Expression.</param>
    /// <returns>One record.</returns>
    public virtual TEntity GetIt(Expression<Func<TEntity, bool>> filter)
    {
        IQueryable<TEntity> query = dbSet;
        return query.SingleOrDefault(filter);

    }


    #endregion
    #region CRUD Functionality

    /// <summary>
    /// Used to create a new entity into the database.
    /// </summary>
    /// <param name="entity">Entity to create.</param>
    public virtual void Create(TEntity entity)
    {
        dbSet.Add(entity);
    }

    /// <summary>
    /// Used to update an entity that already exists in the
    /// database.
    /// </summary>
    /// <param name="entity">Entity to update.</param>
    public virtual void Update(TEntity entity)
    {
        dbSet.Attach(entity);
        context.Entry(entity).State = EntityState.Modified;
    }

    /// <summary>
    /// Used to delete an entity from the database.
    /// </summary>
    /// <param name="entity">Entity to delete.</param>
    public virtual void Delete(TEntity entity)
    {
        if (context.Entry(entity).State == EntityState.Detached)
        {
            dbSet.Attach(entity);
        }
        dbSet.Remove(entity);
    }

    #endregion
    #endregion

}
#endregion

GenericUnitOfWork.cs

  /// <summary>
/// Unit of work class that handles multiple Repositories
/// and shares the context.
/// </summary>
public class GenericUnitOfWork : IUnitOfWork

{
    private AccountsContext context = new AccountsContext();

    Dictionary<string, GenericRepository<IRepository<IRepositoryEntity>>> repostories = null;

    /// <summary>
    /// Generic Repository method which checks the repository is available if not,
    /// it sets it up.
    /// </summary>
    /// <param name="entity">Entity</param>
    /// <returns>Repository to use.</returns>
    public  GenericRepository<IRepository<IRepositoryEntity>> Repository (IRepositoryEntity entity)
    {

            string index = entity.GetType().ToString();

            if (!repostories.ContainsKey(index))
            {

                //Reflections to create the repoositiory if it is not needed.
                Type type1 = typeof(GenericRepository<>);
                Type[] typeArgs = {typeof(IRepositoryEntity)};

                Type constructed = type1.MakeGenericType(typeArgs);
                object o = Activator.CreateInstance(constructed);

                if(o is  GenericRepository<IRepository<IRepositoryEntity>>)
                {
                    var rep = (GenericRepository<IRepository<IRepositoryEntity>>)o;
                    rep.context = this.context;
                    repostories.Add(index, rep);  
                }

            }

            return this.repostories[index];
    }

    /// <summary>
    /// Save method.
    /// </summary>
    public void Save()
    {
        context.SaveChanges();

    }

    private bool disposed = false;

    /// <summary>
    /// Dispose the conxtext when finished.
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

}

}

现在我知道存储库字典的实现和存储库方法不正确(通用类型错误),因为在我的 lambda 表达式中它无法解析 ledgeruser LastName。

var view = unitOfWork.Repository(ledgeruser).Get(l => l.LastName == "smith");

我是否过度设计了这个问题,或者是否有一种很好的干净方法来使用我的通用存储库集合创建通用工作单元,这些存储库是基于模型对象给出的(例如上面的 LedgerUser)设置和创建的。

4

2 回答 2

4

一些想法:

  1. UnitOfWork您可以使用 IoC 容器并以这种方式自动创建存储库,而不是在您的实现中使用反射创建所有内容。MVC 框架实际上就是为这种方法构建的。在http://www.mikesdotnetting.com/Article/117/Dependency-Injection-and-Inversion-of-Control-with-ASP.NET-MVC中提供了使用 StructureMap 的简单解释和示例。

  2. 如果您不想使用成熟的 IoC 容器,您仍然可以针对接口标准化您的工厂逻辑和代码。这起着相同的作用,但如果您想继续使用自定义工作单元实现,可能会更好。在我对 C#/EF 和存储库模式的回答中:将 ObjectContext 放在具有多个存储库的解决方案中的位置?,我发布了一个RepositoryProvider允许创建的类UnitOfWork具有可定制工厂的范围存储库。我建议至少查看这段代码,因为它是一种类似但更有效的方式来完成您的代码示例的目标。要理解的一件重要事情是,该答案中的示例使用 ObjectContext 作为 UnitOfWork,因此您的更改需要通过将出现替换ObjectContextIUnitOfWork. 如果在查看代码后对该方法有任何不清楚的地方,请告诉我,我将尝试解释如何调整您的特定用例。

  3. 似乎您的工厂逻辑有一些循环。如果存储库创建了LedgerUsers,那么它不应该需要 aLedgerUser来创建工厂。在我看来,你真正想要的是一个TypeunitOfWork.Repository(typeof(LedgerUser)). unitOfWork.Repository<LedgerUser您可以通过在负载上创建泛型类型参数并执行>()`来使其更加流畅。根据您的示例,您似乎根本没有任何理由需要实例。

  4. 看起来你更喜欢强类型而不是Repository方法中的接口。我想也许你想要的更像是:


    public IRepository Repository()
        where T : IRepositoryEntity 
    { 
           //  your factory/cache-retrieval logic here
    }

代替

public  GenericRepository<IRepository<IRepositoryEntity>> Repository (IRepositoryEntity entity)
{
      //  your factory/cache-retrieval logic here
}

然后,如果您调用的是Repository<LedgerUser>,您的方法将返回 a GenericRepository<LedgerUser>,即使签名显示IRepository<LedgerUser>。这几乎RepositoryProvider就是我建议的实现链接的工作方式。

于 2012-09-25T20:32:57.360 回答
2

我不明白为什么要将实例传递给对象而不是对象的类型,并按类型保存存储库:

尝试这个

ConcurrentDictionary<Type, object> _repositories = ...;

public GenericRepository<IRepository<TEntity>> Repository<TEntity>(IRepositoryEntity entity) where TEntity: IRepositoryEntity
{
    return (GenericRepository<IRepository<TEntity>>)_repositories.GetOrAdd(
        typeof(TEntity), 
        t => new GenericRepository<IRepository<TEntity>>(this.Context)
    );
}
于 2012-09-25T20:06:05.110 回答