如何在ServiceStack.net第2部分中使用Funq注册多个IDbConnectionFactory实例

时间:2014-10-01 11:53:46

标签: servicestack ormlite-servicestack

除了SAAS(多租户)用例,其中单个API实例使用相同模式的多个数据库(每个租户一个)之外,ServiceStack已经提供了所有内容。由于法律原因,这些数据库需要存放在不同的实例中。

所以,我的问题是,“是否可以根据过滤器中的meta更改每个请求的连接?

我的问题与此one有点类似,但附加的扭曲是每个数据库是相同的。

谢谢你, 斯蒂芬

编辑:如果内存正确地为我服务,我认为mythz之前我已经讨论过这个问题,并且发现即使我们能够改变ADO连接,那么IAuthRepository也是如此破碎。所以这不可行。

2 个答案:

答案 0 :(得分:4)

我添加了一个示例Multi Tenant test,显示了我首选的方法,我将使用自定义IDbConnectionFactory包装器,以便更好地查看和控制正在创建的Db连接,即:

public class MultiTenantDbFactory : IDbConnectionFactory
{
    private readonly IDbConnectionFactory dbFactory;

    public MultiTenantDbFactory(IDbConnectionFactory dbFactory)
    {
        this.dbFactory = dbFactory;
    }

    public IDbConnection OpenDbConnection()
    {
        var tenantId = RequestContext.Instance.Items["TenantId"] as string;
        return tenantId != null
            ? dbFactory.OpenDbConnectionString(GetConnectionString(tenantId))
            : dbFactory.OpenDbConnection();
    }

    public IDbConnection CreateDbConnection()
    {
        return dbFactory.CreateDbConnection();
    }
}

我还希望将 dbFactory单例实例用作非租户请求的默认值,该非租户请求还指定要使用的Dialect Provider:

var dbFactory = new OrmLiteConnectionFactory(
    AppSettings.GetString("MasterDb"), SqlServerDialect.Provider);

container.Register<IDbConnectionFactory>(c =>
    new MultiTenantDbFactory(dbFactory));

为了表明服务是特定于租户的,我只是创建一个自定义界面:

public interface IForTenant
{
    string TenantId { get; }
}

DTO可以实施哪些请求来表明他们特定于租户的请求,即:

public class GetTenant : IForTenant, IReturn<GetTenantResponse>
{
    public string TenantId { get; set; }
}

可以在整个ServiceStack's Request pipeline中轻松检测到,例如全局请求过滤器,以提取请求的租户并将其添加到RequestContext,例如:

GlobalRequestFilters.Add((req, res, dto) =>
{
    var forTennant = dto as IForTenant;
    if (forTennant != null)
        RequestContext.Instance.Items.Add("TenantId", forTennant.TenantId);
});

然后MultiTenantDbFactory可以读回来并打开与所需租户的Db连接:

var tenantId = RequestContext.Instance.Items["TenantId"] as string;
return new OrmLiteConnectionFactory(GetConnectionStringFor(tenantId))
    .OpenDbConnection()

只要有人访问其服务或依赖项中的base.Db,就会使用该文件。

答案 1 :(得分:3)

动态解析连接字符串

此方法使用全局请求筛选器来确定发出请求的人,并将连接字符串设置为请求项。然后,当IoC尝试解析IDbConnectionFactory时,它将检索该请求的连接字符串并建立数据库连接。

public override void Configure(Container container)
{
    // Tell the IoC to get the database connection factory on each request
    container.Register<IDbConnectionFactory>(c => GetDatabaseConnectionFactory()).ReusedWithin(ReuseScope.Request);

    // Create a filter that will determine the tenant and set the appropriate connection string
    GlobalRequestFilters.Add((req,res,obj) => {

        // Determine the connection string based on the some parameter you know about the tenant.
        var dbConnectionString = ...

        // Use a default value if the tenant was unknown
        var defaultConnectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;

        // Save the connection string to the RequestContext.Items collection, so we can read it later
        HostContext.RequestContext.Items.Add("ConnectionString", dbConnectionString ?? defaultConnectionString);
    });
}

// This method returns the correct database connection to the request
public static IDbConnectionFactory GetDatabaseConnectionFactory()
{
    // Read the connection string from our Items
    var dbConnectionString = HostContext.RequestContext.Items["ConnectionString"];

    if(dbConnectionString == null)
        throw new Exception("Connection string has not been set");

    // Return the connection factory for the given connection string
    return new OrmLiteConnectionFactory(dbConnectionString, SqlServerOrmLiteDialectProvider.Instance));
}

IUserAuthRepository支持

如果您还使IoC在每个请求上动态解析连接字符串,那么在进行身份验证时将使正确的存储库可用。

container.Register<IUserAuthRepository>(c => 
    new OrmLiteAuthRepository(GetDatabaseConnectionFactory())).ReusedWithin(ReuseScope.Request); 

我希望有所帮助。