基于Domain类将不同的DbContexts注入通用存储库 - Autofac

时间:2017-07-21 18:46:00

标签: c# entity-framework autofac repository-pattern

在我的应用程序中,我需要与两个数据库进行交互。我有两个域类,它们位于两个不同的数据库中。我还有一个通用的存储库模式,它在其构造函数中接受UoW。我正在寻找一种基于Domain类注入适当UoW的方法。 我不想为第二个数据库编写第二个通用存储库。。有什么好的解决方案吗?

public interface IEntity
{
    int Id { get; set; }
}

位于数据库A

public class Team: IEntity
{
    public int Id { get; set; }
    public string Name{ get; set; }

}

位于数据库B

public class Player: IEntity
{
    public int Id { get; set; }
    public string FullName { get; set; }
}

我还有一个带UoW的通用存储库模式

public interface IUnitOfWork
{
    IList<IEntity> Set<T>();
    void SaveChanges();
}

public class DbADbContext : IUnitOfWork
{
    public IList<IEntity> Set<T>()
    {
        return new IEntity[] { new User() { Id = 10, FullName = "Eric Cantona" } };
    }

    public void SaveChanges()
    {

    }
}

public class DbBDataContext: IUnitOfWork
{
    public IList<IEntity> Set<T>()
    {
        return new IEntity[] { new Tender() { Id = 1, Title = "Manchester United" } };
    }

    public void SaveChanges()
    {

    }

public interface IRepository<TEntity> where TEntity: class, IEntity
{
    IList<IEntity> Table();
}

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{

    protected readonly IUnitOfWork Context;
    public BaseRepository(IUnitOfWork context)
    {
        Context = context;
    }

    IList<IEntity> IRepository<TEntity>.Table()
    {
        return Context.Set<TEntity>();
    }
}

我已经发现文章说Autofac会使用最后一个值覆盖注册。我知道我的问题是DbContexts是如何注册的。

 var builder = new ContainerBuilder();
 // problem is here
        builder.RegisterType<DbADbContext >().As<IUnitOfWork>()
        builder.RegisterType<DbBDbContext >().As<IUnitOfWork>()

 builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IRepository<>));
        var container = builder.Build();

3 个答案:

答案 0 :(得分:0)

这个怎么样:

builder.RegisterType<DbContextBase>().As<IUnitOfWork>()

并且

    DbADataContext: DbContextBase,IUnitOfWork
    DbBDataContext: DbContextBase,IUnitOfWork

或者在注册时,您可以执行以下操作:

containerBuilder.RegisterGeneric(typeof(DbADataContext<>)).Named("DbADataContext", typeof(IUnitOfWork<>));
containerBuilder.RegisterGeneric(typeof(DbBDataContext<>)).Named("DbBDataContext", typeof(IUnitOfWork<>));

答案 1 :(得分:0)

如果你想保留单个BaseRepository及其接口,你必须以某种方式配置,实体将由哪个DbContext处理。它可以在注册申请部分完成,但在这种情况下,您不能将BaseRepostory<T>注册为开放式通用,但在您的注册中要明确,如下所示:

containerBuilder.RegisterType<DbADataContext>().Named<IUnitOfWork>("A");
containerBuilder.RegisterType<DbBDataContext>().Named<IUnitOfWork>("B");

containerBuilder.Register(c => new BaseRepository<Team>(c.ResolveNamed<IUnitOfWork>("A")).As<IRepostory<Team>>();
containerBuilder.Register(c => new BaseRepository<Player>(c.ResolveNamed<IUnitOfWork>("B")).As<IRepository<Player>>();

(只是概念验证,未经测试的代码)

Autofac不够智能,无法自动了解&#34;您希望在每个存储库中使用哪个工作单元。

答案 2 :(得分:0)

我从@ tdragon的答案中获得灵感。

第一步是注册Named DbContext

        builder.RegisterType<Database1>()
            .Keyed<IUnitOfWork>(DbName.Db1)
            .Keyed<DbContext>(DbName.Db1).AsSelf().InstancePerRequest();

        builder.RegisterType<Database2>()
            .Keyed<IUnitOfWork>(DbName.Db2)
            .Keyed<DbContext>(DbName.Db2).AsSelf().InstancePerRequest();

请注意,DbName只是一个枚举。

以下代码扫描数据访问层程序集以查找域类。然后,它注册ReadOnlyRepository和BaseRepository。此代码的位置在DIConfig

Type entityType = typeof(IEntity);
var entityTypes =   Assembly.GetAssembly(typeof(IEntity))
                    .DefinedTypes.Where(t => t.ImplementedInterfaces.Contains(entityType));


var baseRepoType = typeof(BaseRepository<>);
var readOnlyRepoType = typeof(ReadOnlyRepository<>);
var baseRepoInterfaceType = typeof(IRepository<>);
var readOnlyRepoInterfaceType = typeof(IReadOnlyRepository<>);
var dbContextResolver = typeof(DbContextResolverHelper).GetMethod("ResolveDbContext");

foreach (var domainType in entityTypes)
{
  var baseRepositoryMaker = baseRepoType.MakeGenericType(domainType);
  var readonlyRepositoryMarker = readOnlyRepoType.MakeGenericType(domainType);

 var registerAsForBaseRepositoryTypes = baseRepoInterfaceType.MakeGenericType(domainType);
 var registerAsForReadOnlyRepositoryTypes = readOnlyRepoInterfaceType.MakeGenericType(domainType);

 var dbResolver = dbContextResolver.MakeGenericMethod(domainType);
            // register BaseRepository
 builder.Register(c => Activator.CreateInstance(baseRepositoryMaker, dbResolver.Invoke(null, new object[] { c }))
            ).As(registerAsForBaseRepositoryTypes).InstancePerRequest(jobTag);
            //register readonly repositories
 builder.Register(c => Activator.CreateInstance(readonlyRepositoryMarker, dbResolver.Invoke(null, new object[] { c })))
           .As(registerAsForReadOnlyRepositoryTypes).InstancePerRequest(jobTag);

}

以下方法尝试在每个DbContext中查找DbSet,以便找出属于哪个DataContext / Database的域类。

public class DbContextResolverHelper
{
    private static readonly ConcurrentDictionary<Type, DbName> TypeDictionary = new ConcurrentDictionary<Type, DbName>();


    public static DbContext ResolveDbContext<TEntity>(IComponentContext c) where TEntity : class, IEntity
    {
        var type = typeof(DbSet<TEntity>);


        var dbName = TypeDictionary.GetOrAdd(type, t =>
        {

            var typeOfDatabase1 = typeof(Database1);
            var entityInDatabase1 = typeOfDatabase1 .GetProperties().FirstOrDefault(p => p.PropertyType == type);
            return entityInDatabase1 != null ? DbName.Db1: DbName.Db2;


        });

        return c.ResolveKeyed<DbContext>(dbName);
    }
}