Asp.net web api Web Api的动态连接字符串

Asp.net web api Web Api的动态连接字符串,asp.net-web-api,entity-framework-6,connection-string,Asp.net Web Api,Entity Framework 6,Connection String,我通过web api公开我的存储库操作。已使用实体框架和工作单元模式实现了存储库。我有许多相同数据库的实例。每一个都代表不同客户机的数据。现在的问题是如何通过每次webapi调用动态设置连接字符串?我应该在每次调用中获取连接字符串参数吗?或者我应该为每个客户端托管web Api 您可以更改每个DbContext实例的connectionstring 例如: public class AwesomeContext : DbContext { public AwesomeContext (s

我通过web api公开我的存储库操作。已使用实体框架和工作单元模式实现了存储库。我有许多相同数据库的实例。每一个都代表不同客户机的数据。现在的问题是如何通过每次webapi调用动态设置连接字符串?我应该在每次调用中获取连接字符串参数吗?或者我应该为每个客户端托管web Api

您可以更改每个DbContext实例的connectionstring

例如:

public class AwesomeContext : DbContext
{
    public AwesomeContext (string connectionString)
        : base(connectionString)
    {
    }
    public DbSet<AwesomePeople> AwesomePeoples { get; set; }
}
根据连接字符串的数量,您可以为客户机/构造映射创建一个DB表,或将其保存在解决方案中(例如,数组)

如果您不能/不想更改构造函数,也可以稍后更改

将此添加到DbContext覆盖:

    public void SetConnectionString(string connectionString)
    {
        this.Database.Connection.ConnectionString = connectionString;
    }
并在执行任何DB操作之前调用该方法:

   using(AwesomeContext context = new AwesomeContext())
   {

    context.SetConnectionString(ConfigurationManager.ConnectionStrings["newConnectionString"].ConnectionString)
    return context.AwesomePeoples.ToList();

   }

根据提供的信息,我将使用相同的控制器并查找连接字符串,而不是为每个客户机托管单独的WebAPI实例。托管多个实例会有更大的复杂性,并且鉴于所指出的唯一区别是连接字符串,我认为这种复杂性是不合理的

我们需要做的第一件事是确定哪个客户端正在调用,以便获得适当的连接字符串。这可以通过令牌、头、请求数据或路由来完成。路由是最简单的,也是客户端最容易访问的,所以我将演示如何使用它;然而,仔细考虑你的决定,你将如何作出决定。

[Route( "{clientId}" )]
public Foo Get( string clientId ) { /* ... */ }
接下来,我们需要为客户端获取正确的
DbContext
。我们希望继续使用DI,但这很复杂,因为在创建控制器之前,我们不知道构造对象需要什么连接字符串。因此,我们需要注入某种形式的工厂,而不是对象本身。在本例中,我们将其表示为
Func
,理解为它将“clientId”作为字符串,并返回一个适当实例化的
IUnitOfWork
。我们也可以为此使用命名接口

[RoutePrefix("foo")]
public class FooController : ApiController
{  
    private Func<string, IUnitOfWork> unitOfWorkFactory;

    public FooController( Func<string, IUnitOfWork> unitOfWorkFactory )
    {
        this.unitOfWorkFactory = unitOfWorkFactory;
    }

    [Route( "{clientId}" )]
    public Foo Get( string clientId )
    {
        var unitOfWork = unitOfWorkFactory(clientId);
        // ...
    }
}
[RoutePrefix(“foo”)]
公共类FooController:ApicController
{  
私营Func Unitof WorkFactory;
公共食品控制员(Func unitOfWorkFactory)
{
this.unitOfWorkFactory=unitOfWorkFactory;
}
[路由(“{clientId}”)]
公共Foo-Get(字符串clientId)
{
var unitOfWork=unitOfWorkFactory(客户端ID);
// ...
}
}
剩下的就是配置依赖项注入容器,为我们提供
Func
。这在不同的实现中可能会有很大的差异。以下是在Autofac中执行此操作的一种可能方法

protected override void Load( ContainerBuilder builder )
{
    // It is expected `MyDbContext` has a constructor that takes the connection string as a parameter
    // This registration may need to be tweaked depending on what other constructors you have.
    builder.Register<MyDbContext>().ForType<DbContext>().InstancePerRequest();

    // It is expected `UnitOfWork`'s constructor takes a `DbContext` as a parameter
    builder.RegisterType<UnitOfWork>().ForType<IUnitOfWork>().InstancePerRequest();

    builder.Register<Func<string, Bar>>(
        c =>
            {
                var dbContextFactory = c.Resolve<Func<string, DbContext>>();
                var unitOfWorkFactory = c.Resolve<Func<DbContext, IUnitOfWork>>();

                return clientId =>
                    {
                        // You may have injected another type to help with this
                        var connectionString = GetConnectionStringForClient(clientId);
                        return unitOfWorkFactory(dbContextFactory(connectionString));
                    };
            });
 }
protected override void Load(ContainerBuilder生成器)
{
//“MyDbContext”应有一个将连接字符串作为参数的构造函数
//此注册可能需要根据您拥有的其他构造函数进行调整。
builder.Register().ForType().InstancePerRequest();
//预期'UnitOfWork'的构造函数将'DbContext'作为参数
builder.RegisterType().ForType().InstancePerRequest();
建筑商登记(
c=>
{
var dbContextFactory=c.Resolve();
var unitOfWorkFactory=c.Resolve();
返回clientId=>
{
//您可能已经注入了另一种类型来帮助实现这一点
var connectionString=GetConnectionStringForClient(clientId);
返回unitOfWorkFactory(dbContextFactory(connectionString));
};
});
}
之所以使用Autofac,是因为注释表明Autofac当前正在使用,尽管其他容器也可能出现类似的结果

这样,控制器应该能够被实例化,并且每个请求都将使用适当的连接字符串


基于链接项目的注册示例:

builder.Register<Func<string, IEmployeeService>>(
    c =>
        {
            var dbContextFactory = c.Resolve<Func<string, IMainContext>>();
            var unitOfWorkFactory = c.Resolve<Func<IMainContext, IUnitOfWork>>();
            var repositoryFactory = c.Resolve<Func<IMainContext, IEmployeeRepository>>();
            var serviceFactory = c.Resolve<Func<IUnitOfWork, IEmployeeService>>();

            return clientId =>
                {
                    // You may have injected another type to help with this
                    var connectionString = GetConnectionStringForClient(clientId);

                    IMainContext dbContext = dbContextFactory(connectionString);
                    IUnitOfWork unitOfWork = unitOfWorkFactory(dbContext);
                    IEmployeeRepository employeeRepository = repositoryFactory(dbContext);
                    unitOfWork.employeeRepositoty = employeeRepository;

                    return serviceFactory(unitOfWork);
                };
        });
builder.Register(
c=>
{
var dbContextFactory=c.Resolve();
var unitOfWorkFactory=c.Resolve();
var repositoryFactory=c.Resolve();
var serviceFactory=c.Resolve();
返回clientId=>
{
//您可能已经注入了另一种类型来帮助实现这一点
var connectionString=GetConnectionStringForClient(clientId);
IMainContext dbContext=dbContextFactory(connectionString);
IUnitOfWork unitOfWork=unitOfWorkFactory(dbContext);
IEEmployeeRepository employeeRepository=存储工厂(dbContext);
unitOfWork.employeeRepository=employeeRepository;
退货服务工厂(Unitof Work);
};
});

如果您发现注册变得太麻烦,因为需要手动进行一些连接,那么您可能需要在确定客户端后更新(或创建一个新的)容器,以便您可以更多地依赖容器。

我正在通过interface和autofac注入上下文。这就像公共类AwesomeContext:DbContext,IAwesomeContextI更新了答案,这样即使您正在注入contextI注入的是IAwesomeContext,而不是DbContextI,我无法查看代码(您需要提供示例),但您可以将我的示例更改为您的实现。您只需将SetConnectionString方法添加到接口中,并在特定的上下文类中实现它。然后调用注入的variableCode示例中的方法,这里是stackoverflow.com/q/30404108/1711287。我进一步证实了DbContext对象的预期生存期是多少?我希望这对每一个人来说都是一个新的例子
builder.Register<Func<string, IEmployeeService>>(
    c =>
        {
            var dbContextFactory = c.Resolve<Func<string, IMainContext>>();
            var unitOfWorkFactory = c.Resolve<Func<IMainContext, IUnitOfWork>>();
            var repositoryFactory = c.Resolve<Func<IMainContext, IEmployeeRepository>>();
            var serviceFactory = c.Resolve<Func<IUnitOfWork, IEmployeeService>>();

            return clientId =>
                {
                    // You may have injected another type to help with this
                    var connectionString = GetConnectionStringForClient(clientId);

                    IMainContext dbContext = dbContextFactory(connectionString);
                    IUnitOfWork unitOfWork = unitOfWorkFactory(dbContext);
                    IEmployeeRepository employeeRepository = repositoryFactory(dbContext);
                    unitOfWork.employeeRepositoty = employeeRepository;

                    return serviceFactory(unitOfWork);
                };
        });