简体   繁体   English

通用存储库/工作单元问题

[英]Generic Repository / Unit of Work Issue

I have been learning the Repository and Unit of Work patterns from various sources, including here: 我一直在学习各种来源的知识库工作单元模式,包括:

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-application 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应用程序

If you browse to thr link above to Creating a Unit of Work Class there is the following: 如果您浏览上面的thr链接到创建工作单元类 ,则有以下内容:

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

The which is fine, but I wanted to have a go at extending the Unit of Work class that Generic and setup a collection of GenericRepositories so i can update then dynamnically based on the model I pass through. 这很好,但我想继续扩展Generic的工作单元类并设置GenericRepositories的集合,这样我就可以根据我通过的模型进行动态更新。

I ultimately want to do the following in my 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());
    }
}

So here is my classes and interfaces so far: 所以这是我的类和接口到目前为止:

IRepository.cs 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 GenericRepository.cs

/// /// Repository Class for looking afer Entities /// CRUD Operations /// /// public class GenericRepository : IRepository where TEntity : class { /// ///用于查找实体的存储库类/// CRUD操作/// ///公共类GenericRepository:IRepository其中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 : 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);
    }

}

} }

Now I know the implementation of repositories Dictionary and the method Repository are incorrect (Generic Types are wrong) because in my lambda expression it cannot resolve the ledgeruser LastName. 现在我知道存储库Dictionary的实现和方法库不正确(通用类型是错误的)因为在我的lambda表达式中它无法解析ledgeruser LastName。

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

Am I over engineering the issue, or is there a good clean way to create Generic Unit of Work with my collection of Generic Repositories, which are set and created based on the model object give (LedgerUser for eg above). 我是在设计这个问题,还是有一个很好的干净方式来创建我的通用存储库集合,这些通用存储库是基于模型对象给出的(例如上面的LedgerUser)设置和创建的。

A few ideas : 一些想法:

  1. Instead of creating everything with reflection in your UnitOfWork implementation, you could use an IoC container and automate the creation of repositories that way. 您可以使用IoC容器并以这种方式自动创建存储库,而不是在UnitOfWork实现中使用反射创建所有内容。 The MVC framework is actually built for this kind of approach. MVC框架实际上是为这种方法而构建的。 A simple explanation and example of using StructureMap for this purpose is available at http://www.mikesdotnetting.com/Article/117/Dependency-Injection-and-Inversion-of-Control-with-ASP.NET-MVC . 有关使用StructureMap的简单说明和示例,请访问http://www.mikesdotnetting.com/Article/117/Dependency-Injection-and-Inversion-of-Control-with-ASP.NET-MVC

  2. If you don't want to use a full-blown IoC container, you could still standardize your factory logic and code against the interface. 如果您不想使用完整的IoC容器,您仍然可以针对接口标准化您的工厂逻辑和代码。 This plays the same kind of role, but may work better if you want to keep using a custom unit of work implementation. 这起着同样的作用,但如果你想继续使用自定义的工作单元实现,可能会更好。 In my answer to C#/EF and the Repository Pattern: Where to put the ObjectContext in a solution with multiple repositories? 在我对C#/ EF和存储库模式的回答中:在哪里将ObjectContext放在具有多个存储库的解决方案中? , I posted a RepositoryProvider class that allows the creation of UnitOfWork -scoped repositories with customizeable factories. ,我发布了一个RepositoryProvider类,允许使用可定制的工厂创建UnitOfWork -scoped存储库。 I would recommend at least looking over this code, as it is a similar, but more effective way to do what your code example aims at. 我建议至少查看此代码,因为它是一种类似但更有效的方法来执行代码示例的目标。 One important thing to understand is that the example in that answer uses an ObjectContext as a UnitOfWork, so your change would need to account for this discrepancy by replacing occurrences of ObjectContext with an occurrence of IUnitOfWork . 要理解的一件重要事情是,该答案中的示例使用ObjectContext作为UnitOfWork,因此您的更改需要通过将IUnitOfWork的出现替换为ObjectContext的出现来解决这种差异。 If anything about that approach is unclear after reviewing the code, let me know and I will try to explain how to adapt your specific use case. 如果在查看代码后有任何关于该方法的内容尚不清楚,请告诉我,我将尝试解释如何调整您的特定用例。

  3. It seems like there is something circular about your factory logic. 看起来你的工厂逻辑有一些循环。 If the repository creates LedgerUser s, then it shouldn't require a LedgerUser to create the factory. 如果存储库创建了LedgerUser ,那么它不应该要求LedgerUser来创建工厂。 It seems to me like what you really want is a Type parameter like unitOfWork.Repository(typeof(LedgerUser)) . 在我看来,你真正想要的是一个Type参数,比如unitOfWork.Repository(typeof(LedgerUser)) You could make this more fluent by creating a generic type parameter over load and do unitOfWork.Repository<LedgerUser >()`. 您可以通过在load上创建泛型类型参数来使其更流畅,并执行unitOfWork.Repository<LedgerUser >()`。 Based on your example, there doesn't seem to be any reason at all that you would need an instance. 根据您的示例,您似乎根本不需要实例。

  4. It all seems like you would prefer strong-typing to an interface in your Repository method. 在您的Repository方法中,似乎您更喜欢强类型接口。 I think maybe what you are going for is more like: 我想也许你想要的更像是:


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

instead of 代替

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

Then, if your call was to Repository<LedgerUser> , your method would return a GenericRepository<LedgerUser> , even though the signature says IRepository<LedgerUser> . 然后,如果您的调用是Repository<LedgerUser> ,则您的方法将返回GenericRepository<LedgerUser> ,即使签名显示IRepository<LedgerUser> This is pretty much how the link to the RepositoryProvider implementation that I suggested works. 这几乎是我建议的RepositoryProvider实现的链接。

i do not see why you want to pass an instance to the object instead of the type of the object, and hold repositories by type: 我不明白为什么要将实例传递给对象而不是对象的类型,并按类型保存存储库:

try this 试试这个

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)
    );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM