1

我正在开发一个应用程序,并开始使用 John Papa 的示例中的一些代码作为我的基础。在网上我发现了同样的代码,它出现在 Stackoverflow 上的一个问题的答案中。这是问题:

如何从实体框架中的上下文中分离实体?

它在以下给出的答案中:SynerCoder

答案的一部分建议使用以下类,该类用于从缓存存储库的字典中提供存储库。有人可以帮助我并告诉我这样做真的有好处吗?我理解代码,但看不到将存储库保存在字典中的意义。不是每个新的网络请求都会看到一个空字典并且无论如何都必须获取/创建一个新的存储库。

Data/Helpers/IRepositoryProvider.cs

using System;
using System.Collections.Generic;
using System.Data.Entity;
using Data.Contracts;

namespace Data.Helpers
{
    /// <summary>
    /// A maker of Repositories.
    /// </summary>
    /// <remarks>
    /// An instance of this class contains repository factory functions for different types.
    /// Each factory function takes an EF <see cref="DbContext"/> and returns
    /// a repository bound to that DbContext.
    /// <para>
    /// Designed to be a "Singleton", configured at web application start with
    /// all of the factory functions needed to create any type of repository.
    /// Should be thread-safe to use because it is configured at app start,
    /// before any request for a factory, and should be immutable thereafter.
    /// </para>
    /// </remarks>
    public class RepositoryFactories
    {
        /// <summary>
        /// Return the runtime repository factory functions,
        /// each one is a factory for a repository of a particular type.
        /// </summary>
        /// <remarks>
        /// MODIFY THIS METHOD TO ADD CUSTOM FACTORY FUNCTIONS
        /// </remarks>
        private IDictionary<Type, Func<DbContext, object>> GetFactories()
        {
            return new Dictionary<Type, Func<DbContext, object>>
                {
                   //If you have an custom implementation of an IRepository<T>
                   //{typeof(IArticleRepository), dbContext => new ArticleRepository(dbContext)}
                };
        }

        /// <summary>
        /// Constructor that initializes with runtime repository factories
        /// </summary>
        public RepositoryFactories()
        {
            _repositoryFactories = GetFactories();
        }

        /// <summary>
        /// Constructor that initializes with an arbitrary collection of factories
        /// </summary>
        /// <param name="factories">
        /// The repository factory functions for this instance. 
        /// </param>
        /// <remarks>
        /// This ctor is primarily useful for testing this class
        /// </remarks>
        public RepositoryFactories(IDictionary<Type, Func<DbContext, object>> factories)
        {
            _repositoryFactories = factories;
        }

        /// <summary>
        /// Get the repository factory function for the type.
        /// </summary>
        /// <typeparam name="T">Type serving as the repository factory lookup key.</typeparam>
        /// <returns>The repository function if found, else null.</returns>
        /// <remarks>
        /// The type parameter, T, is typically the repository type 
        /// but could be any type (e.g., an entity type)
        /// </remarks>
        public Func<DbContext, object> GetRepositoryFactory<T>()
        {

            Func<DbContext, object> factory;
            _repositoryFactories.TryGetValue(typeof(T), out factory);
            return factory;
        }

        /// <summary>
        /// Get the factory for <see cref="IRepository{T}"/> where T is an entity type.
        /// </summary>
        /// <typeparam name="T">The root type of the repository, typically an entity type.</typeparam>
        /// <returns>
        /// A factory that creates the <see cref="IRepository{T}"/>, given an EF <see cref="DbContext"/>.
        /// </returns>
        /// <remarks>
        /// Looks first for a custom factory in <see cref="_repositoryFactories"/>.
        /// If not, falls back to the <see cref="DefaultEntityRepositoryFactory{T}"/>.
        /// You can substitute an alternative factory for the default one by adding
        /// a repository factory for type "T" to <see cref="_repositoryFactories"/>.
        /// </remarks>
        public Func<DbContext, object> GetRepositoryFactoryForEntityType<T>() where T : class
        {
            return GetRepositoryFactory<T>() ?? DefaultEntityRepositoryFactory<T>();
        }

        /// <summary>
        /// Default factory for a <see cref="IRepository{T}"/> where T is an entity.
        /// </summary>
        /// <typeparam name="T">Type of the repository's root entity</typeparam>
        protected virtual Func<DbContext, object> DefaultEntityRepositoryFactory<T>() where T : class
        {
            return dbContext => new EFRepository<T>(dbContext);
        }

        /// <summary>
        /// Get the dictionary of repository factory functions.
        /// </summary>
        /// <remarks>
        /// A dictionary key is a System.Type, typically a repository type.
        /// A value is a repository factory function
        /// that takes a <see cref="DbContext"/> argument and returns
        /// a repository object. Caller must know how to cast it.
        /// </remarks>
        private readonly IDictionary<Type, Func<DbContext, object>> _repositoryFactories;
    }
}

这是调用工厂的代码:

using System;
using Data.Contracts;
using Data.Helpers;
using Models;

namespace Data
{
    /// <summary>
    /// The "Unit of Work"
    ///     1) decouples the repos from the controllers
    ///     2) decouples the DbContext and EF from the controllers
    ///     3) manages the UoW
    /// </summary>
    /// <remarks>
    /// This class implements the "Unit of Work" pattern in which
    /// the "UoW" serves as a facade for querying and saving to the database.
    /// Querying is delegated to "repositories".
    /// Each repository serves as a container dedicated to a particular
    /// root entity type such as a <see cref="Url"/>.
    /// A repository typically exposes "Get" methods for querying and
    /// will offer add, update, and delete methods if those features are supported.
    /// The repositories rely on their parent UoW to provide the interface to the
    /// data layer (which is the EF DbContext in this example).
    /// </remarks>
    public class UnitOfWork : IUnitOfWork, IDisposable
    {
        public UnitOfWork(IRepositoryProvider repositoryProvider)
        {
            CreateDbContext();

            repositoryProvider.DbContext = DbContext;
            RepositoryProvider = repositoryProvider;       
        }

        // Repositories
        public IRepository<Event> Events { get { return GetStandardRepo<Event>(); } }
        public IRepository<Candidate> Candidates { get { return GetStandardRepo<Candidate>(); } }

        /// <summary>
        /// Save pending changes to the database
        /// </summary>
        public void Commit()
        {
            //System.Diagnostics.Debug.WriteLine("Committed");
            DbContext.SaveChanges();
        }

        protected void CreateDbContext()
        {
            DbContext = new UnicornsContext();

            // Do NOT enable proxied entities, else serialization fails
            DbContext.Configuration.ProxyCreationEnabled = false;

            // Load navigation properties explicitly (avoid serialization trouble)
            DbContext.Configuration.LazyLoadingEnabled = false;

            // Because Web API will perform validation, I don't need/want EF to do so
            DbContext.Configuration.ValidateOnSaveEnabled = false;
        }

        protected IRepositoryProvider RepositoryProvider { get; set; }

        private IRepository<T> GetStandardRepo<T>() where T : class
        {
            return RepositoryProvider.GetRepositoryForEntityType<T>();
        }
        private T GetRepo<T>() where T : class
        {
            return RepositoryProvider.GetRepository<T>();
        }

        private UnicornsContext DbContext { get; set; }

        #region IDisposable

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

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (DbContext != null)
                {
                    DbContext.Dispose();
                }
            }
        }

        #endregion
    }
}

在我看来,工厂让事情变得比他们需要的更复杂。我是否正确,我是否应该以更简单的方式来执行此操作,例如:

private IRepository<xx> = new GenericRepository<xx>(dbContext);

还有一点。在我的应用程序中,我使用的是 Unity。那么在构造函数中指定所需的存储库并让 Unity 为我创建存储库会更容易吗?如果我这样做了,那么有没有一种方法可以传递 dbContext 以便 Unity 在创建存储库时可以使用它?有没有人使用 Unity 创建这样的存储库?

4

1 回答 1

3

好的。这是我最好的镜头:

  1. 将存储库保存在缓存中的目的是确保每个请求仅启动存储库一次。存储库缓存在类中,并由方法RepositoryProvider公开。所以优点是工作单元不关心缓存或存储库的创建。UnitOfWorkGetRepositoryForEntityType

  2. 该类RepositoryProvider在每个工作单元中实例化一次。(注意——最好为每个请求创建新的存储库)。RepositoryProvider使用类型作为键将存储库保存在字典中。这在使用具有Type参数的通用存储库库时很好。但是,如果您创建了自定义存储库怎么办?在此示例中,按类型创建存储库是RepositoryFactories通过方法传递给类的MakeRepository。优点是创建存储库与缓存分开。

  3. 该类RepositoryFactories知道何时创建自定义存储库,因为它包含一个使用 Type 作为键和一个函数作为值的字典。该函数是自定义存储库的构造函数。如果字典中有一个值,则使用该构造函数,否则只需使用通用基本构造函数。

所有这一切意味着当您添加实体时,您不必修改任何这些类,除非您创建自定义存储库。当你这样做时,你所要做的就是在字典中添加一个条目RepositoryFactories

于 2013-06-20T11:41:09.283 回答