微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

asp.net-web-api – Web Api的动态连接字符串

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

解决方法

根据提供的信息,我将使用相同的控制器并查找连接字符串,而不是为每个客户端托管单独的Web API实例.托管多个实例会有更多的复杂性,并且给出的唯一区别是连接字符串,我不认为复杂性是合理的.

我们需要做的第一件事是确定调用哪个客户端以获取适当的连接字符串.这可以通过令牌,标题,请求数据或路由来完成.路由是最简单的,最常见的是客户端可访问,所以我将演示使用它;但是,在决定如何做出决定时,请仔细考虑您的要求.

[Route( "{clientId}" )]
public Foo Get( string clientId ) { /* ... */ }

接下来,我们需要为客户端获取正确的DbContext.我们希望继续使用DI,但这很复杂,因为在创建Controller之后我们才知道构造对象需要什么连接字符串.因此,我们需要注入某种形式的工厂而不是对象本身.在这种情况下,我们将其表示为Func< string,IUnitOfWork>理解它将’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);
        // ...
    }
}

剩下的就是配置我们的依赖注入容器,为我们提供Func< string,IUnitOfWork>.实施之间可能会有很大差异.以下是在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));
                    };
            });
 }

使用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);
                };
        });

如果您发现注册变得过于繁琐,因为需要手动进行一些连接,您可能需要在确定客户端后查看更新(或创建新的)容器,以便您可以更多地依赖容器.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐