使用“储存库”是否有任何优势。与ASP.NET MVC4和实体框架? [英] Is there any advantage in using a "repository factory" with ASP.NET MVC4 and Entity Framework?

查看:134
本文介绍了使用“储存库”是否有任何优势。与ASP.NET MVC4和实体框架?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am developing an application and I started to use as my base some code from an example by John Papa. Looking on the web I found this same code and it appears in an answer to a question on Stackoverflow. Here is the question:

如何从实体框架中的上下文中删除实体

正是在这个答案中由...提供:SynerCoder

It's in the answer that was given by: SynerCoder

答案的一部分提供了用于从缓存存储库的字典提供存储库的以下类。有人可以帮助我,告诉我,这样做真的有
的优势。我明白代码,但是看不到在字典中保存仓库的一点。如果每个新的网络请求都会看到一个
的空字典,并且必须获取/创建一个新的存储库,情况就不一样了。

One part of the answer suggests the following class that is used to provide a repository from a dictionary of cached repositories. Can someone help me out and tell me is there really an advantage in doing this. I understand the code but can't see the point of keeping repositories in a dictionary. Would it not be the case that every new web request would see an empty dictionary and have to get / make a new repository anyway.

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

以下是调用工厂的代码:

Here's the code that calls the factory:

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

在我看来,工厂使事情变得更复杂,需要它们。我是正确的,我应该这样做一个比较简单的方法,比如:

It seems to me that the factory makes things more complicated that they need be. Am I correct and should I do this a simpler way such as with something like:

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

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

One more point. In my application I am using Unity. So would it be even easier to just specify the needed repositories in the constructor and have Unity create the repositories for me. If I did this then is there a way I could pass around the dbContext so it could be used by Unity when creating the repository? Has anyone used Unity to create repositories like this?

推荐答案

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

OK. Here's my best shot:


  1. 将存储库保存在缓存中的要点是确保每个请求只启动一次存储库。存储库缓存位于 RepositoryProvider 类中,并通过 GetRepositoryForEntityType c UnitOfWork / code>方法。因此,优点在于工作单元不涉及缓存或创建存储库。

  1. The point of keeping repositories in a cache is to ensure that the repository is only initiated once per request. The repository cache is in the RepositoryProvider class and is exposed to the UnitOfWork by the GetRepositoryForEntityType method. So the advantage is that the unit of work is not concerned with caching or creation of repositories.

RepositoryProvider 类每单位工作实例化一次。 (NB - 最好为每个请求创建新的存储库)。 RepositoryProvider 使用类型作为键将存储库保存在字典中。当使用具有类型参数的通用存储库库时,这是很好的。但是如果您创建了一个自定义存储库呢?在这个例子中,按类型创建存储库通过 MakeRepository 方法传递给 RepositoryFactories 类。优点是创建存储库与缓存分离。

The RepositoryProvider class is instantiated once per unit of work. (NB - it is desirable to create the repositories new for every request). The RepositoryProvider keeps the repositories in a dictionary using the type as a key. This is fine when using the generic repository base which has a Type parameter. But what if you have created a custom repository? In this example the creation of repositories by type is handed off to the RepositoryFactories class via the MakeRepository method. The advantage is that creating repositories is separated from caching.

RepositoryFactories 类知道什么时候自定义存储库,因为它包含使用Type作为键和作为值的函数的字典。该函数是自定义存储库的构造函数。如果字典中有值,那么使用该构造函数,否则只需使用通用基础构造函数。

The RepositoryFactories class knows when to make a custom repository because it contains a dictionary that uses Type as a key and a function as a value. The function is the constructor for a custom repository. If there's a value in the dictionary then use that constructor otherwise just use the generic base constructor.

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

All this means that as you add entities you do not have to modify any of these classes unless you create a custom repository. And when you do that all you have to do is add an entry to the dictionary in RepositoryFactories

这篇关于使用“储存库”是否有任何优势。与ASP.NET MVC4和实体框架?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆