具有Repository,UnitOfWork和Ninject的多个dbcontexts [英] Multiple dbcontexts with Repository, UnitOfWork and Ninject

查看:225
本文介绍了具有Repository,UnitOfWork和Ninject的多个dbcontexts的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在慢慢掌握EF,存储库,UnitOfWork和Ninject,并将我的实现包括在内(见下文)。



我的数据层的目的是提供从具有Oracle后端的现有人力资源系统读取数据的能力,并通过消费此数据提供附加功能。我正在构建的应用程序将使用SQL后端,目前我刚刚在人力资源系统Oracle dbm中创建了一些额外的表,但是我希望将其分开并挂接到诸如SQL依赖关系和服务代理等功能。



我的数据层从我的应用程序中完全抽象出来,并被分解为Data,Data.Contracts和Models。我一直在为自己的表使用代码第一种方法,并使用Devart的dotConnect for Oracle映射Oracle数据库并为我生成我的模型。



最后我有一个业务层,我注入UnitOfWork并保留我所有的业务逻辑,业务层然后被注入到我的表示层。最终这将被一个服务层所取代,所以我的数据可以保存在我们所有系统的一个地方,但我还没有在那里。



我会真的很感激任何人可以检查我的代码到目前为止,并告诉我如何可以引入多个dbcontexts(SQL& Oracle),可以与同一个UnitOfWork无缝地使用。



最后,我也努力解决我可以拥有一个可以分配给所有模型的通用存储库模式,然后还创建特定于模型的存储库,甚至可以分配给特定模型的其他通用存储库方法。例如。我可能想要提供读取所有模型的方法,但我可能只想在某些型号上添加,编辑和删除功能。



非常感谢, p>

Andy

  using System; 
使用System.Data.Entity;
使用Data.Contracts;

命名空间Data.Helpers
{
///< summary>
///可以通过类型提供存储库的类的接口。
///如果它不能在其缓存的仓库中找到一个,则该类可以动态创建存储库。
///< / summary>
///< remarks>
///由此提供者创建的存储库往往需要< see cref =DbContext/>
///检索数据。
///< / remarks>
public interface IRepositoryProvider
{
///< summary>
///获取并设置< see cref =DbContext/>用于初始化
///如果必须创建。
///< / summary>
DbContext DbContext {get;组; }

///< summary>
///获取< see cref =IRepository {T}/>对于实体类型,T.
///< / summary>
///< typeparam name =T>
///< see cref =IRepository {T}/> ;.的根实体类型。
///< / typeparam>
IRepository< T> GetRepositoryForEntityType< T>()其中T:class;

///< summary>
///获取一个类型为T的存储库。
///< / summary>
///< typeparam name =T>
///存储库的类型,通常是自定义存储库接口。
///< / typeparam>
///< param name =factory>
///可选的存储库创建功能,它使用< see cref =DbContext/>
///并返回一个存储库T.如果必须创建存储库,则使用它。
///< / param>
///< remarks>
///查找缓存中请求的存储库,如果找到,返回。
///如果没有找到,尝试在工厂生产一个,如果出厂参数为null,则返回
///默认工厂。
///< / remarks>
T GetRepository< T>(Func< DbContext,object> factory = null)其中T:class;


///< summary>
///设置从该提供程序返回的存储库。
///< / summary>
///< remarks>
///如果不希望该提供者创建一个,请设置一个存储库。
///有用的测试和开发没有后端
///由一个类型T的存储库返回的对象的实现。
///< / remarks>
void SetRepository&T;(T repository);
}
}

使用系统;
使用System.Collections.Generic;
使用System.Data.Entity;
使用Data.Contracts;

命名空间Data.Helpers
{
///< summary>
///存储库的制造商。
///< / summary>
///< remarks>
///此类的一个实例包含不同类型的存储库工厂函数。
///每个工厂函数接受EF< see cref =DbContext/>并返回
///一个绑定到该DbContext的存储库。
///< para>
///设计为Singleton,在web应用程序中配置,以
///创建任何类型的存储库所需的所有工厂函数。
///应该线程安全使用,因为它在应用程序启动时配置,
///在任何工厂请求之前,应该是不可变的。
///< / para>
///< / remarks>
public class RepositoryFactories
{
///< summary>
///返回运行时存储库工厂函数
///每个都是特定类型的存储库的工厂。
///< / summary>
///< remarks>
///修改此方法以添加自定义工厂功能
///< / remarks>
private IDictionary< Type,Func< DbContext,object>> GetFactories()
{
返回新字典< Type,Func< DbContext,object>>
{
// {typeof(IArticleRepository),dbContext => new ArticleRepository(dbContext)},
// {typeof(IUrlRepository),dbContext =>新的UrlRepository(dbContext)},
};
}

///< summary>
///使用运行时库仓库初始化的构造方法
///< / summary>
public RepositoryFactories()
{
_repositoryFactories = GetFactories();
}

///< summary>
///初始化工厂任意集合的构造函数
///< / summary>
///< param name =工厂>
///该实例的存储库工厂函数。
///< / param>
///< remarks>
///这个ctor主要用于测试这个类
///< / remarks>
public RepositoryFactories(IDictionary< Type,Func< DbContext,object>>工厂)
{
_repositoryFactories =工厂;
}

///< summary>
///获取类型的存储库工厂函数。
///< / summary>
///< typeparam name =T>类型用作存储库工厂查找键。< / typeparam>
///< returns>如果找到存储库函数,否则为null。< / returns>
///< remarks>
///类型参数T通常是存储库类型
///,但可以是任何类型(例如,实体类型)
///< / remarks>
public Func< DbContext,object> GetRepositoryFactory< T>()
{

Func< DbContext,object>厂;
_repositoryFactories.TryGetValue(typeof(T),出厂);
返回工厂;
}

///< summary>
///获取< see cref =IRepository {T}/>其中T是实体类型。
///< / summary>
///< typeparam name =T>存储库的根类型,通常为实体类型。< / typeparam>
///< returns>
///给出EF< see cref =DbContext/>创建< see cref =IRepository {T}/>的工厂。
///< / returns>
///< remarks>
///首先查看< see cref =_ repositoryFactories/> ;.中的自定义工厂。
///如果没有,请返回到< see cref =DefaultEntityRepositoryFactory {T}/> ;.
///您可以将替代工厂替换为默认值,方法是将
///将类型T的存储库工厂添加到< see cref =_ repositoryFactories/> ;.
///< / remarks>
public Func< DbContext,object> GetRepositoryFactoryForEntityType< T>()其中T:class
{
return GetRepositoryFactory&T;()? DefaultEntityRepositoryFactory< T>();
}

///< summary>
///默认工厂为< see cref =IRepository {T}/>其中T是一个实体。
///< / summary>
///< typeparam name =T>存储库根实体的类型< / typeparam>
protected virtual Func< DbContext,object> DefaultEntityRepositoryFactory< T>()其中T:class
{
return dbContext =>新的EFR存储库< T>(dbContext);
}

///< summary>
///获取仓库工厂函数的字典。
///< / summary>
///< remarks>
///字典键是System.Type,通常是存储库类型。
///一个值是一个存储库工厂函数
///它需要一个< see cref =DbContext/>参数并返回
///存储库对象。来电者必须知道如何投掷它。
///< / remarks>
private readonly IDictionary< Type,Func< DbContext,object>> _repositoryFactories;
}
}


使用系统;
使用System.Collections.Generic;
使用System.Data.Entity;
使用Data.Contracts;

命名空间Data.Helpers
{
///< summary>
///提供< see cref =IRepository {T}/>为客户端请求。
///< / summary>
///< remarks>
///缓存给定类型的存储库,以便每个提供者只创建一个存储库。
///每个客户端请求创建一个新的提供者。
///< / remarks>
public class RepositoryProvider:IRepositoryProvider
{
public RepositoryProvider(RepositoryFactories repositoryFactories)
{
_repositoryFactories = repositoryFactories;
Repositories = new Dictionary< Type,object>();
}

///< summary>
///获取并设置< see cref =DbContext/>用于初始化
///如果必须创建。
///< / summary>
public DbContext DbContext {get;组; }

///< summary>
///获取或创建和缓存默认的< see cref =IRepository {T}/>对于一个类型为T.的实体。
///< / summary>
///< typeparam name =T>
///< see cref =IRepository {T}/> ;.的根实体类型。
///< / typeparam>
///< remarks>
///如果在缓存中找不到存储库,请使用工厂创建一个。
///< / remarks>
public IRepository< T> GetRepositoryForEntityType< T>()其中T:class
{
return GetRepository< IRepository< T>(
_repositoryFactories.GetRepositoryFactoryForEntityType< T>
}

///< summary>
///获取或创建和缓存一个类型为T的存储库。
///< / summary>
///< typeparam name =T>
///存储库的类型,通常是自定义存储库接口。
///< / typeparam>
///< param name =factory>
///一个可选的存储库创建函数,它使用一个DbContext参数
///并返回一个存储库T.如果存储库必须被创建并且
///调用者想要指定具体的工厂使用而不是一个
///注入的< see cref =RepositoryFactories/>。
///< / param>
///< remarks>
///查找缓存中请求的存储库,如果找到,返回。
///如果没有找到,尝试使用< see cref =MakeRepository {T}/> ;.
///< / remarks>
public virtual T GetRepository< T>(Func< DbContext,object> factory = null)其中T:class
{
//在typeof(T)下查找T字典缓存。
对象repoObj;
Repositories.TryGetValue(typeof(T),out repoObj);
if(repoObj!= null)
{
return(T)repoObj;
}

//找不到或空;做一个,添加到字典缓存,并返回。
返回MakeRepository< T>(factory,DbContext);
}

///< summary>
///获取存储库对象的字典,由存储库类型键入。
///< / summary>
///< remarks>
///调用者必须知道如何将存储库对象转换为有用的类型。
///< p>这是一个扩展点。您可以在这里注册完整的存储库
///它们将被使用,而不是这个提供者将创建的那些。< / p>
///< / remarks>
protected Dictionary< Type,object>存储库{get;私人集合}

///< summary>创建一个类型为T的仓库。< / summary>
///< typeparam name =T>要进行存储库的类型。< / typeparam>
///< param name =dbContext>
///< see cref =DbContext/>用于初始化存储库。
///< / param>
///< param name =factory>
/// Factory with< see cref =DbContext/>论据。用于创建存储库。
///如果为null,则从< see cref =_ repositoryFactories/> ;.获取工厂。
///< / param>
///< returns>< / returns>
protected virtual T MakeRepository< T>(Func< DbContext,object> factory,DbContext dbContext)
{
var f = factory? _repositoryFactories.GetRepositoryFactory< T>();
if(f == null)
{
throw new NotImplementedException(No factory for repository type,+ typeof(T).FullName);
}
var repo =(T)f(dbContext);
存储库[typeof(T)] = repo;
return repo;
}

///< summary>
///设置此提供程序返回的类型T的存储库。
///< / summary>
///< remarks>
///如果您不希望此提供者创建一个,请插入自定义存储库。
///有用的测试和开发没有后端
///由一个类型T的存储库返回的对象的实现。
///< / remarks>
public void SetRepository&T;(T repository)
{
存储库[typeof(T)] = repository;
}

///< summary>
///< see cref =RepositoryFactories/>用于创建新的存储库。
///< / summary>
///< remarks>
///应该由构造函数注入
///< / remarks>
private RepositoryFactories _repositoryFactories;
}
}

使用系统;
使用System.Data;
使用System.Data.Entity;
使用System.Data.Entity.Infrastructure;
使用System.Linq;
使用Data.Contracts;

命名空间数据
{
///< summary>
///用于数据访问的EF依赖的通用存储库
///< / summary>
///< typeparam name =T>此存储库的实体类型< / typeparam>
public class EFRepository< T> :IRepository< T>其中T:class
{
public EFRepository(DbContext dbContext)
{
if(dbContext == null)
throw new ArgumentNullException(dbContext);
DbContext = dbContext;
DbSet = DbContext.Set< T>();
}

protected DbContext DbContext {get;组; }

protected DbSet< T> DbSet {get;组; }

public virtual IQueryable< T> GetAll()
{
return DbSet;
}

public virtual T GetById(int id)
{
//返回DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate< T>(id));
return DbSet.Find(id);
}

public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if(dbEntityEntry.State!= EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}

public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if(dbEntityEntry.State == EntityState.Detached)
{
DbSet.Attach(entity);
}
dbEntityEntry.State = EntityState.Modified;
}

public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if(dbEntityEntry.State!= EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}

public virtual void Delete(int id)
{
var entity = GetById(id);
if(entity == null)return; // 未找到;假设已经删除。
删除(实体);
}
}
}

使用系统;
使用Data.Contracts;
使用Data.Helpers;
使用模型;

命名空间数据
{
///< summary>
///工作单位
/// 1)将控制权与控制器脱钩
/// 2)将DbContext和EF与控制器脱钩
/ // 3)管理UoW
///< / summary>
///< remarks>
///这个类实现了单位工作模式,其中
///UoW用作查询和保存到数据库的外观。
///将查询委托给仓库。
///每个存储库用作特定
//根实体类型的容器,例如< see cref =Url/>。
///存储库通常会显示Get方法进行查询,如果支持这些功能,
///将提供添加,更新和删除方法。
///存储库依靠他们的父UoW提供
///数据层(在这个例子中是EF DbContext)的接口。
///< / remarks>
public class UnitOfWork:IUnitOfWork,IDisposable
{
public UnitOfWork(IRepositoryProvider repositoryProvider)
{
CreateDbContext();

repositoryProvider.DbContext = DbContext;
RepositoryProvider = repositoryProvider;
}

//存储库
public IRepository< DASH_PYLVR> DASH_PYLVRs {get {return GetStandardRepo< DASH_PYLVR>(); }}
public IRepository< DASH_SickRecord> DASH_SickRecords {get {return GetStandardRepo< DASH_SickRecord>(); }}
public IRepository< EMDET> EMDETs {get {return GetStandardRepo< EMDET>(); }}
public IRepository< EMLVA> EMLVAs {get {return GetStandardRepo< EMLVA>(); }}
public IRepository< EMLVE> EMLVEs {get {return GetStandardRepo< EMLVE>(); }}
public IRepository< EMMPO> EMMPOs {get {return GetStandardRepo< EMMPO>(); }}
public IRepository< EMPOS> EMPOSs {get {return GetStandardRepo< EMPOS>(); }}
public IRepository< EVENTLOG> EVENTLOGs {get {return GetStandardRepo< EVENTLOG>(); }}
public IRepository< IDMSTAGING> IDMSTAGINGs {get {return GetStandardRepo< IDMSTAGING>(); }}
public IRepository< PP_BRADFORD> PP_BRADFORDs {get {return GetStandardRepo< PP_BRADFORD>(); }}
public IRepository< PP_BRADFORD_SCORES> PP_BRADFORD_SCORESs {get {return GetStandardRepo< PP_BRADFORD_SCORES>(); }}
public IRepository< PSDET> PSDETs {get {return GetStandardRepo< PSDET>(); }}
public IRepository< PSLDW> PSLDWs {get {return GetStandardRepo< PSLDW>(); }}
public IRepository< UPZ88> UPZ88s {get {return GetStandardRepo< UPZ88>(); }}

///< summary>
///保存对数据库的更改
///< / summary>
public void Commit()
{
//System.Diagnostics.Debug.WriteLine(\"Committed);
DbContext.SaveChanges();
}

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

//不要启用代理实体,否则序列化失败
DbContext.Configuration.ProxyCreationEnabled = false;

//显式加载导航属性(避免序列化问题)
DbContext.Configuration.LazyLoadingEnabled = false;

//因为Web API将执行验证,我不需要/想要EF来执行此操作
DbContext.Configuration.ValidateOnSaveEnabled = false;
}

protected IRepositoryProvider RepositoryProvider {get;组; }

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

private CHRISCSEntities DbContext {get;组;

#region IDisposable

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


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

#endregion
}
}


使用系统。 LINQ的;

命名空间Data.Contracts
{
public interface IRepository< T>其中T:class
{
IQueryable< T>得到所有();
T GetById(int id);
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(int id);
}
}

使用模型;

命名空间Data.Contracts
{
///< summary>
///工作单位的接口
///< / summary>
public interface IUnitOfWork
{
//保存对数据存储的更改。
void Commit();

//存储库
IRepository< DASH_PYLVR> DASH_PYLVRs {get; }
IRepository< DASH_SickRecord> DASH_SickRecords {get; }
IRepository< EMDET> EMDETs {get; }
IRepository< EMLVA> EMLVAs {get; }
IRepository< EMLVE> EMLVEs {get; }
IRepository< EMMPO> EMMPOs {get; }
IRepository< EMPOS> EMPOSs {get; }
IRepository< EVENTLOG> EVENTLOGs {get; }
IRepository< IDMSTAGING> IDMSTAGING {get; }
IRepository< PP_BRADFORD> PP_BRADFORDs {get; }
IRepository< PP_BRADFORD_SCORES> PP_BRADFORD_SCORESs {get; }
IRepository< PSDET> PSDETs {get; }
IRepository< PSLDW> PSLDWs {get; }
IRepository< UPZ88> UPZ88s {get;
}
}


解决方案

这看起来很像John Papa的代码。我试图基本说明



让我们说你想让你的 ArticleRepository 只读。那么你可以这样做:

  public class ArticleRepository:EFRepository< Article> 
{
//添加一个自定义的实现 - 你可以在这里添加自定义的方法,但这个是空的
//因为我们隐藏的功能而不是创建新的函数
//我们继承自EFRepository,所以这个类有插入和更新方法,
//但这并不重要,因为我们只公开FindById和GetAll方法
//在界面
}

public interface IArticleRepository
{
//仅公开您想要的
FindById(int id)的自定义界面;
GetAll();
}

对于多个上下文,我没有经验,所以我可能在资格认证,但我认为您应该能够以与第一个相同的方式将您的第二个上下文添加到您的工作单元中,然后如果存在超过1个上下文的保存,则使用 TransactionScope()



现在 - 这是您代码中应该指定如何创建自定义存储库的位置:

  ///修改此方法以添加自定义工厂功能
///< / remarks>
private IDictionary< Type,Func< DbContext,object>> GetFactories()
{
返回新字典< Type,Func< DbContext,object>>
{
// {typeof(IArticleRepository),dbContext => new ArticleRepository(dbContext)},
// {typeof(IUrlRepository),dbContext =>新的UrlRepository(dbContext)},
};
}

只需取消评论词典中的第一个条目 RepositoryProvider 将返回 ArticleRepository 类的实例,当您要求 IArticleRepository 在您的工作单位



在这个特定的例子(实现是空的),你甚至不需要 ArticleRepository 类。您可以使用此行代替词典条目:



{typeof(IArticleRepository) ,dbContext =>新的EFRepository< Article>(dbContext)}



其他参考文献:



a href =http://www.tigranetworks.co.uk/blogs/electricdreams/grokking-the-repository-and-unit-of-work-patterns/ =nofollow noreferrer>存储库和工作单位模式



John Papa的原始来源< a>



如何确保在使用存储库模式时创建代理


I am slowly getting to grips with EF, repository, UnitOfWork and Ninject and have included my implementation so far (see below).

The purpose of my data layer is to provide the ability to read data from the existing HR system which has an Oracle back end, and provide addition functionality by consuming this data. The application I am building will use a SQL backend, currently I have just created some extra tables in the HR systems Oracle dbm but I want to keep this separate and hook in to features such as SQL dependency and service broker.

My data layer is completely abstracted from my application(s) and is split in to Data, Data.Contracts and Models. I have been using a code first approach for my own tables and have used Devart's dotConnect for Oracle to map the Oracle database and generate my models for me.

Finally I have a business layer where I inject the UnitOfWork and keep all my business logic, the business layer is then Injected in my presentation layer. Eventually this will be replaced by a service layer so my data can be maintained in a single place for all our systems, but I'm not quite there yet.

I would really appreciate if anyone can review my code so far and show me how I can introduce multiple dbcontexts (SQL & Oracle) which can be used seamlessly with the same UnitOfWork.

Finally I also am struggling to work out how I can have a generic repository pattern which I can assign to all models, but then also create model specific repositories or even additional generic repository methods that could be assigned to specific models. E.g. I may want to provide methods to read all models, but I may only want to have the ability to add, edit, delete on certain models.

Many thanks,

Andy

using System;
using System.Data.Entity;
using Data.Contracts;

namespace Data.Helpers
{
/// <summary>
/// Interface for a class that can provide repositories by type.
/// The class may create the repositories dynamically if it is unable
/// to find one in its cache of repositories.
/// </summary>
/// <remarks>
/// Repositories created by this provider tend to require a <see cref="DbContext"/>
/// to retrieve data.
/// </remarks>
public interface IRepositoryProvider
{
    /// <summary>
    /// Get and set the <see cref="DbContext"/> with which to initialize a repository
    /// if one must be created.
    /// </summary>
    DbContext DbContext { get; set; }

    /// <summary>
    /// Get an <see cref="IRepository{T}"/> for entity type, T.
    /// </summary>
    /// <typeparam name="T">
    /// Root entity type of the <see cref="IRepository{T}"/>.
    /// </typeparam>
    IRepository<T> GetRepositoryForEntityType<T>() where T : class;

    /// <summary>
    /// Get a repository of type T.
    /// </summary>
    /// <typeparam name="T">
    /// Type of the repository, typically a custom repository interface.
    /// </typeparam>
    /// <param name="factory">
    /// An optional repository creation function that takes a <see cref="DbContext"/>
    /// and returns a repository of T. Used if the repository must be created.
    /// </param>
    /// <remarks>
    /// Looks for the requested repository in its cache, returning if found.
    /// If not found, tries to make one with the factory, fallingback to 
    /// a default factory if the factory parameter is null.
    /// </remarks>
    T GetRepository<T>(Func<DbContext, object> factory = null) where T : class;


    /// <summary>
    /// Set the repository to return from this provider.
    /// </summary>
    /// <remarks>
    /// Set a repository if you don't want this provider to create one.
    /// Useful in testing and when developing without a backend
    /// implementation of the object returned by a repository of type T.
    /// </remarks>
    void SetRepository<T>(T repository);
    }
}

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>>
            {
               //{typeof(IArticleRepository), dbContext => new ArticleRepository(dbContext)},
               //{typeof(IUrlRepository), dbContext => new UrlRepository(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 System.Collections.Generic;
using System.Data.Entity;
using Data.Contracts;

namespace Data.Helpers
{
/// <summary>
/// Provides an <see cref="IRepository{T}"/> for a client request.
/// </summary>
/// <remarks>
/// Caches repositories of a given type so that repositories are only created once per provider.
/// create a new provider per client request.
/// </remarks>
public class RepositoryProvider : IRepositoryProvider
{
    public RepositoryProvider(RepositoryFactories repositoryFactories)
    {
        _repositoryFactories = repositoryFactories;
        Repositories = new Dictionary<Type, object>();
    }

    /// <summary>
    /// Get and set the <see cref="DbContext"/> with which to initialize a repository
    /// if one must be created.
    /// </summary>
    public DbContext DbContext { get; set; }

    /// <summary>
    /// Get or create-and-cache the default <see cref="IRepository{T}"/> for an entity of type T.
    /// </summary>
    /// <typeparam name="T">
    /// Root entity type of the <see cref="IRepository{T}"/>.
    /// </typeparam>
    /// <remarks>
    /// If can't find repository in cache, use a factory to create one.
    /// </remarks>
    public IRepository<T> GetRepositoryForEntityType<T>() where T : class
    {
        return GetRepository<IRepository<T>>(
            _repositoryFactories.GetRepositoryFactoryForEntityType<T>());
    }

    /// <summary>
    /// Get or create-and-cache a repository of type T.
    /// </summary>
    /// <typeparam name="T">
    /// Type of the repository, typically a custom repository interface.
    /// </typeparam>
    /// <param name="factory">
    /// An optional repository creation function that takes a DbContext argument
    /// and returns a repository of T. Used if the repository must be created and
    /// caller wants to specify the specific factory to use rather than one
    /// of the injected <see cref="RepositoryFactories"/>.
    /// </param>
    /// <remarks>
    /// Looks for the requested repository in its cache, returning if found.
    /// If not found, tries to make one using <see cref="MakeRepository{T}"/>.
    /// </remarks>
    public virtual T GetRepository<T>(Func<DbContext, object> factory = null) where T : class
    {
        // Look for T dictionary cache under typeof(T).
        object repoObj;
        Repositories.TryGetValue(typeof(T), out repoObj);
        if (repoObj != null)
        {
            return (T)repoObj;
        }

        // Not found or null; make one, add to dictionary cache, and return it.
        return MakeRepository<T>(factory, DbContext);
    }

    /// <summary>
    /// Get the dictionary of repository objects, keyed by repository type.
    /// </summary>
    /// <remarks>
    /// Caller must know how to cast the repository object to a useful type.
    /// <p>This is an extension point. You can register fully made repositories here
    /// and they will be used instead of the ones this provider would otherwise create.</p>
    /// </remarks>
    protected Dictionary<Type, object> Repositories { get; private set; }

    /// <summary>Make a repository of type T.</summary>
    /// <typeparam name="T">Type of repository to make.</typeparam>
    /// <param name="dbContext">
    /// The <see cref="DbContext"/> with which to initialize the repository.
    /// </param>        
    /// <param name="factory">
    /// Factory with <see cref="DbContext"/> argument. Used to make the repository.
    /// If null, gets factory from <see cref="_repositoryFactories"/>.
    /// </param>
    /// <returns></returns>
    protected virtual T MakeRepository<T>(Func<DbContext, object> factory, DbContext dbContext)
    {
        var f = factory ?? _repositoryFactories.GetRepositoryFactory<T>();
        if (f == null)
        {
            throw new NotImplementedException("No factory for repository type, " + typeof(T).FullName);
        }
        var repo = (T)f(dbContext);
        Repositories[typeof(T)] = repo;
        return repo;
    }

    /// <summary>
    /// Set the repository for type T that this provider should return.
    /// </summary>
    /// <remarks>
    /// Plug in a custom repository if you don't want this provider to create one.
    /// Useful in testing and when developing without a backend
    /// implementation of the object returned by a repository of type T.
    /// </remarks>
    public void SetRepository<T>(T repository)
    {
        Repositories[typeof(T)] = repository;
    }

    /// <summary>
    /// The <see cref="RepositoryFactories"/> with which to create a new repository.
    /// </summary>
    /// <remarks>
    /// Should be initialized by constructor injection
    /// </remarks>
    private RepositoryFactories _repositoryFactories;
    }
}

using System;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using Data.Contracts;

namespace Data
{
/// <summary>
/// The EF-dependent, generic repository for data access
/// </summary>
/// <typeparam name="T">Type of entity for this Repository.</typeparam>
public class EFRepository<T> : IRepository<T> where T : class
{
    public EFRepository(DbContext dbContext)
    {
        if (dbContext == null)
            throw new ArgumentNullException("dbContext");
        DbContext = dbContext;
        DbSet = DbContext.Set<T>();
    }

    protected DbContext DbContext { get; set; }

    protected DbSet<T> DbSet { get; set; }

    public virtual IQueryable<T> GetAll()
    {
        return DbSet;
    }

    public virtual T GetById(int id)
    {
        //return DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate<T>(id));
        return DbSet.Find(id);
    }

    public virtual void Add(T entity)
    {
        DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
        if (dbEntityEntry.State != EntityState.Detached)
        {
            dbEntityEntry.State = EntityState.Added;
        }
        else
        {
            DbSet.Add(entity);
        }
    }

    public virtual void Update(T entity)
    {
        DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
        if (dbEntityEntry.State == EntityState.Detached)
        {
            DbSet.Attach(entity);
        }
        dbEntityEntry.State = EntityState.Modified;
    }

    public virtual void Delete(T entity)
    {
        DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
        if (dbEntityEntry.State != EntityState.Deleted)
        {
            dbEntityEntry.State = EntityState.Deleted;
        }
        else
        {
            DbSet.Attach(entity);
            DbSet.Remove(entity);
        }
    }

    public virtual void Delete(int id)
    {
        var entity = GetById(id);
        if (entity == null) return; // not found; assume already deleted.
        Delete(entity);
    }
    }
}

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<DASH_PYLVR> DASH_PYLVRs { get { return GetStandardRepo<DASH_PYLVR>(); } }
    public IRepository<DASH_SickRecord> DASH_SickRecords { get { return GetStandardRepo<DASH_SickRecord>(); } }
    public IRepository<EMDET> EMDETs { get { return GetStandardRepo<EMDET>(); } }
    public IRepository<EMLVA> EMLVAs { get { return GetStandardRepo<EMLVA>(); } }
    public IRepository<EMLVE> EMLVEs { get { return GetStandardRepo<EMLVE>(); } }
    public IRepository<EMMPO> EMMPOs { get { return GetStandardRepo<EMMPO>(); } }
    public IRepository<EMPOS> EMPOSs { get { return GetStandardRepo<EMPOS>(); } }
    public IRepository<EVENTLOG> EVENTLOGs { get { return GetStandardRepo<EVENTLOG>(); } }
    public IRepository<IDMSTAGING> IDMSTAGINGs { get { return GetStandardRepo<IDMSTAGING>(); } }
    public IRepository<PP_BRADFORD> PP_BRADFORDs { get { return GetStandardRepo<PP_BRADFORD>(); } }
    public IRepository<PP_BRADFORD_SCORES> PP_BRADFORD_SCORESs { get { return GetStandardRepo<PP_BRADFORD_SCORES>(); } }
    public IRepository<PSDET> PSDETs { get { return GetStandardRepo<PSDET>(); } }
    public IRepository<PSLDW> PSLDWs { get { return GetStandardRepo<PSLDW>(); } }
    public IRepository<UPZ88> UPZ88s { get { return GetStandardRepo<UPZ88>(); } }

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

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

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


using System.Linq;

namespace Data.Contracts
{
    public interface IRepository<T> where T : class
    {
    IQueryable<T> GetAll();
    T GetById(int id);
    void Add(T entity);
    void Update(T entity);
    void Delete(T entity);
    void Delete(int id);
    }
}

using Models;

namespace Data.Contracts
{
/// <summary>
/// Interface for the "Unit of Work"
/// </summary>
public interface IUnitOfWork
{
    // Save pending changes to the data store.
    void Commit();

    // Repositories
    IRepository<DASH_PYLVR> DASH_PYLVRs { get; }
    IRepository<DASH_SickRecord> DASH_SickRecords { get; }
    IRepository<EMDET> EMDETs { get; }
    IRepository<EMLVA> EMLVAs { get; }
    IRepository<EMLVE> EMLVEs { get; }
    IRepository<EMMPO> EMMPOs { get; }
    IRepository<EMPOS> EMPOSs { get; }
    IRepository<EVENTLOG> EVENTLOGs { get; }
    IRepository<IDMSTAGING> IDMSTAGINGs { get; }
    IRepository<PP_BRADFORD> PP_BRADFORDs { get; }
    IRepository<PP_BRADFORD_SCORES> PP_BRADFORD_SCORESs { get; }
    IRepository<PSDET> PSDETs { get; }
    IRepository<PSLDW> PSLDWs { get; }
    IRepository<UPZ88> UPZ88s { get; }
    }
}

解决方案

This looks a lot like John Papa's code. I attempted a basic explanation of it before.

Lets say you want to make your ArticleRepository read only. Then you could do this:

public class ArticleRepository: EFRepository<Article>
{
   //Add a custom implementation - you can add custom methods here but this one is empty 
   //because we are hiding functions rather than creating new functions
   //We inherit from EFRepository so this class has Insert and Update methods, 
   //but that doesn't matter because we only expose the FindById and GetAll methods 
   //in the interface
}

public interface IArticleRepository
{
   //A custom interface that only exposes what you want
   FindById(int id);
   GetAll();
}

As for multiple contexts, I have no experience with it so I am probably under-qualified, but I think you should be able to add your second context to your unit of work in the same way as the first and then if there are saves going on that span more than 1 context, then do it using TransactionScope()

Now - this is the place in your code that you should specify how to create custom repositories:

/// MODIFY THIS METHOD TO ADD CUSTOM FACTORY FUNCTIONS
/// </remarks>
private IDictionary<Type, Func<DbContext, object>> GetFactories()
{
    return new Dictionary<Type, Func<DbContext, object>>
        {
           //{typeof(IArticleRepository), dbContext => new ArticleRepository(dbContext)},
           //{typeof(IUrlRepository), dbContext => new UrlRepository(dbContext)},
        };
}

Just un-comment the first entry in the Dictionary and the RepositoryProvider will return an instance of the ArticleRepository class when you ask for an IArticleRepository in your unit of work

In this particular example (where the implementation is empty) you don't even need the ArticleRepository class. You could use this line for the Dictionary entry instead:

{typeof(IArticleRepository), dbContext => new EFRepository<Article>(dbContext)}

Other references:

The repository and unit of work patterns

John Papa's original source

How to ensure proxies are created when using the repository pattern

这篇关于具有Repository,UnitOfWork和Ninject的多个dbcontexts的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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