根据请求参数在运行时创建EF Core dbcontext



背景

我们正在使用ASP.NET Core和Entity Framework Core 2.2

构建Web应用程序

我们正在连接到旧数据库。设置是有16个数据库,所有数据库都具有完全相同的架构,并持有不同的上下文数据。我们无法更改此。

我们需要根据请求参数在运行时连接到特定数据库的要求。

想象一下母公司下的每个企业都有一个数据库。

想象每个数据库都有诸如员工,客户端和班次之类的表(员工为客户端工作的轮班)。

还有一个"中央"数据库,该数据库在所有其他数据库中拥有常见的信息,例如设置等。

我们需要在单个列表视图中列出所有业务的所有员工。

我们计划使用中央数据库中的SQL视图来检索这些数据,该数据库仅在每个数据库中进行联合(如果您对如何更好地执行此操作有建议,请分享)。)。

CREATE VIEW dbo.v_all_employees AS 
    SELECT EmployeeId, Fullname, 1 AS BusinessId FROM BusinessA.dbo.Employees
    UNION ALL SELECT EmployeeId, Fullname, 2 AS BusinessId FROM BusinessB.dbo.Employees
    -- etc. etc.

我们有一组模型,这些模型代表所有数据库中的所有实体(表),因为它们共享完全相同的架构,例如一个员工课,一个客户班等。

用例

用户转到网页,通过此路线查看所有业务的员工列表:

http://example.org/employees

用户然后单击一个"详细信息"链接以供单个员工查看更多详细信息,将用户带到URL:

http://example.org/employees/details?employeeed=123&businessid = xyz

我所困扰的是,鉴于BusinessID。

我提出了三种方式(现在要@mylee获得第四种方法),以实现所需的结果,并正在寻找社区的反馈。

每个选项都假定每个DBContext将实现一个接口,该接口揭示所有DBSET和DBCONTEXT方法,然后我们将使用Factory模式来处理确定要使用的DBContext实现的确定。

第一个选项:只需根据请求参数" Bizid"创建出厂创建正确的DBContext即可。

但是,这要求每个dbContext过度乘坐启用方法并设置dbprovider- dotnet核心框架通过其IOC容器扩展方法AdddBcontext:

为我们提供了这些方法。
public class ContextFactory : IContextFactory
{
    public IBIZContext GetContext(int bizId)
    {
        switch (bizId)
        {
            // Newing-up the DbContexts like this requires that the OnConfiguring method
            // for each context be present in each DbContext to setup the DbProvider
            // with the correct connection string.
            case 6:
                return new BIZ_AContext();
            case 7:
                return new BIZ_BContext();
            default:
                throw new Exception("Unexpected Business Id");
        }
    }
}

这个问题是我不喜欢我们如何在这里介绍上下文。它要求我们在每个上下文中都过度骑行配置方法,并可以访问连接字符串。

第二个选项:

我更喜欢使用IN型内置的IOC容器,该容器在startup.cs中进行了设置,但它展示了服务定位器抗模式。此外,它将HTTPContext泄漏出Web项目中的基础架构项目(我正在使用洋葱架构):

public class ContextFactoryUsingLocator : IContextFactoryUsingLocator
{
    public IBIZContext GetContext(IHttpContextAccessor httpContextFactory, int bizId)
    {
        // Injecting the HttpContextAccessor gives us access to the IoC Container via RequestServices;
        // But using it here exhibits the Service Locator anti-pattern.
        // Perhaps its ok to use the Service Locator pattern within a Factory in this case?
        switch (bizId)
        {
            case 6:
                return (BIZ_AContext)httpContextFactory.HttpContext.RequestServices.GetService(typeof(BIZ_AContext));
            case 7:
                return (BIZ_BContext)httpContextFactory.HttpContext.RequestServices.GetService(typeof(BIZ_BContext));
            default:
                throw new Exception("Unexpected Business Id");
        }
    }
}

第三个选项

将每个dbcontext注入工厂,并让工厂只需返回正确的实例:

public class ContextFactoryInjected : IContextFactoryInjected
{
    private readonly BIZ_AContext _bizAContext;
    private readonly BIZ_BContext _bizBContext;
    public ContextFactoryInjected(
        BIZ_AContext bizAContext, 
        // 14 other DbContext dependencies omitted here for brevity
        BIZ_BContext bizBContext)
    {
        // Injecting all 16 DbContexts into the Factory seems to counter the intention of the Factory since the IoC Container
        // would be managing the creation of all the instances; isn't that the responsibility of the Factory?
        // More importantly; wouldn't this have serious performance implications, creating 16 instances of a DbContext on every Request?
        _bizAContext = bizAContext;
        _bizBContext = bizBContext;
    }
    public IBIZContext GetContext(int bizId)
    {
        switch (bizId)
        {
            case 6:
                return _bizAContext;
            case 7:
                return _bizBContext;
            default:
                throw new Exception("Unexpected Business Id");
        }
    }
}

第四个选项将DBContext的配置封装在工厂内(@Mylee建议)

public class ContextFactoryConfigured : IContextFactoryConfigured
{
    public IBIZContext GetContext(int bizId)
    {
        switch (bizId)
        {
            // Newing-up the DbContexts like this encapsulates all the details required for the DbContext within the Factory
            case 6:
                var bizAOptionsBuilder = new DbContextOptionsBuilder<BizAContext>();
                bizAOptionsBuilder.UseSqlServer(Settings.BizAConnectionString);
                return new BizAContext(bizAOptionsBuilder.Options);
            case 7:
                var bizBOptionsBuilder = new DbContextOptionsBuilder<BizBContext>();
                bizBOptionsBuilder.UseSqlServer(Settings.BizBConnectionString);
                return new BizBContext(bizBOptionsBuilder.Options);
            default:
                throw new Exception("Unexpected Business Id");
        }
    }
}

您是否同意选项2展示了服务定位器抗pattern,也就是说,说出厂依赖于对象的对象是正确的吗?

您认为选项4是这些方法中最好的方法,因为通常是工厂"新起来"其对象的责任,并且不会导致疑虑的混合(即没有''t需要httpcontext),并封装了在工厂内构建上下文所需的所有详细信息(例如连接串)?

或者有一种方法可以使用依赖注入而不会引起关注?

还是我在这里没有提到过的更好的方法?

我们使用多个数据库使用了旧系统的问题相同的结构,提出了与您的选项4相似的解决方案:

有一个接口和工厂方法来创建DBContext。它将连接字符串作为参数:

public interface ICustomDbContextFactory<out T> where T: DbContext
{
    T CreateDbContext(string connectionString);
}
public class CustomDbContextFactory<T> : ICustomDbContextFactory<T>  where T: DbContext
{
    public T CreateDbContext(string connectionString)
    {
        var optionsBuilder = new DbContextOptionsBuilder<T>();
        optionsBuilder.UseSqlServer(connectionString);
        return System.Activator.CreateInstance(typeof(T), optionsBuilder.Options) as T;
    }
}

工厂已在DI注册为单人:

services.AddSingleton<ICustomDbContextFactory<CustomDbContext>, CustomDbContextFactory<CustomDbContext>>();

然后您只需在需要时使用它(需要能够注入工厂):

using (var dbContext = customDbContextFactory.CreateDbContext(connectionString))
{
   // use your dbContext here
}

我认为这几乎与您的NR匹配。4解决方案(除了我们具有从工厂分离的连接字符串的逻辑上,我们发现它是我们可以弄清楚的问题的最清洁解决方案。想听听您完成了什么实施以及是否有更好的想法如何解决问题。

为什么不使用dbcontext的共同点?我认为这是实现您想要的更好的方法。

public class DataContext : DbContext
{
   public DataContext(string database)
: base("Data Source=********;Initial Catalog=" + database + ";Integrated Security=True")
   {
   }
} 

您需要另一个数据库来保留所有公司的数据。您可以在此链接上获取更多信息:

MVC中的单独数据库的多租户系统

相关内容

  • 没有找到相关文章

最新更新