我正在开发一个应用程序,我开始使用John Papa的一个例子中的一些代码作为我的基础。在网上看到我发现了相同的代码,它出现在一个问题的答案中 堆栈溢出。这是一个问题:
How to de-attach an entity from a Context in Entity Framework?
这是由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来创建像这样的存储库?
答案 0 :(得分:2)
行。这是我最好的镜头:
将缓存保留在缓存中的目的是确保每个请求仅启动一次存储库。存储库缓存位于RepositoryProvider
类中,并通过UnitOfWork
方法向GetRepositoryForEntityType
公开。因此,优点是工作单元不关心缓存或创建存储库。
每个工作单元实例化一次RepositoryProvider
类。 (注意 - 最好为每个请求创建新的存储库)。 RepositoryProvider
使用类型作为键将存储库保存在字典中。使用具有Type
参数的通用存储库库时,这很好。但是,如果您创建了自定义存储库呢?在此示例中,按类型创建存储库将通过RepositoryFactories
方法传递给MakeRepository
类。优点是创建存储库与缓存分离。
RepositoryFactories
类知道何时创建自定义存储库,因为它包含一个字典,该字典使用Type作为键,函数作为值。该函数是自定义存储库的构造函数。如果字典中有值,则使用该构造函数,否则只使用通用基础构造函数。
所有这些意味着在添加实体时,除非您创建自定义存储库,否则不必修改任何这些类。当你这样做时,你所要做的就是在RepositoryFactories