具有Repository,UnitOfWork和Ninject的多个dbcontexts

问题描述 投票:1回答:2

我正在慢慢掌握EF,存储库,UnitOfWork和Ninject,并且到目前为止已经包含了我的实现(见下文)。

我的数据层的目的是提供从具有Oracle后端的现有HR系统读取数据的能力,并通过使用此数据来提供附加功能。我正在构建的应用程序将使用SQL后端,目前我刚刚在HR系统Oracle dbm中创建了一些额外的表,但我想将它保持独立并挂钩到SQL依赖项和服务代理等功能。

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

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

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

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

非常感谢,

安迪

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; }
    }
}
entity-framework ninject repository-pattern dbcontext unit-of-work
2个回答
2
投票

这看起来很像John Papa的代码。我之前尝试过它的basic explanation

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

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

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

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

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

只要取消评论Dictionary中的第一个条目,当你在工作单元中要求RepositoryProvider时,ArticleRepository将返回IArticleRepository类的实例

在这个特定的例子中(实现为空)你甚至不需要ArticleRepository类。您可以将此行用于Dictionary条目:

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

其他参考:

The repository and unit of work patterns

John Papa's original source

How to ensure proxies are created when using the repository pattern


0
投票

我正在推荐您编辑单位工作...

我有几个问题,我认为这是受影响的代码:

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

所以这是创建对DbContext的引用,目前强类型为“CHRISCSEntities”

我想我需要注射? dbcontext此时我可以将它设置为我感兴趣的DbContext吗?

我会给出个人免责声明,我仍然是DAL所有东西的亲戚,所以请原谅任何愚蠢的问题:)

我不完全确定我应该在哪里做,或者我是否需要制作某种DbContext Factory?

我们可以使用ninject为特定实体注入正确的DbContext并跟踪注入的DbContext吗?

© www.soinside.com 2019 - 2024. All rights reserved.