如何解决 castle windsor for C# 中的运行时依赖关系
How to resolve runtime dependencies in castle windsor for C#
我这里有一个特定的场景,我需要根据用户传递连接字符串,因为用户可能会根据his/her企业映射到不同的数据库。
这是我用来解决静态变量依赖关系的代码:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IUserRepository>()
.ImplementedBy(typeof(IKS.Dare.Optimix.Repository.EntityFramework.UserModule.UserRepository))
.DependsOn(Dependency.OnValue("connectionString", DatabaseSettings.DefaultConnectionString))
);
}
因为这个 DefaultConnectionString
应该是一个动态变量,我不想锁定这个变量以使其线程安全,因为这会降低性能。我想要一种方法来处理这种情况。
可能考虑我们可以给一个session,可以申请如下:
DynamicParameters((k, d) => d["connectionString"] = Session["connectionString"])
但这是在一个不使用任何 Web 组件的不同项目中,它只是一个安装程序项目,基本上是为解决依赖关系而设计的。
我的通用存储库如下所示
public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
private const string IsActive = "IsActive", DbContext = "dbContext", EntityPropertyName = "Entity";
private string connectionString = String.Empty, provider = String.Empty;
public GenericRepository(string connectionString, string provider)
{
this.connectionString = connectionString;
this.provider = provider;
}
public int Count()
{
string tableName = typeof(T).Name;
string query = SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
int count = DbHelper.ExecuteScalar<int>(query: query, commandType: System.Data.CommandType.Text, connectionString: connectionString, provider: provider, parameters: null);
return count;
}
}
DBHelper class 如下所示
public static int ExecuteNonQuery(string query, CommandType commandType = CommandType.StoredProcedure,
IList<DbParameter> parameters = null, int? timeout = null, string connectionString = "", string provider = "")
{
using (var connection = CreateDbConnection(connectionString, provider))
{
connection.Open();
using (DbCommand command = CreateDbCommand(sqlQuery: query, parameters: parameters,
connection: connection, commandType: commandType, timeout: timeout))
{
return command.ExecuteNonQuery();
}
}
}
public static DbParameter CreateParameter<TValue>(string name, TValue value, DbType dbType,
ParameterDirection parameterDirection = ParameterDirection.Input, string provider = "")
{
DbParameter param = CreateDbProviderFactory(provider).CreateParameter();
param.Value = value;
param.ParameterName = name;
param.DbType = dbType;
param.Direction = parameterDirection;
return param;
}
public static DbConnection CreateDbConnection()
{
return CreateDbConnection(String.Empty, String.Empty);
}
public static DbConnection CreateDbConnection(string connectionString = "", string provider = "")
{
DbConnection connection = null;
if (String.IsNullOrEmpty(provider))
{
if (String.IsNullOrEmpty(DatabaseSettings.DefaultProvider))
throw new ArgumentNullException("provider");
else
provider = DatabaseSettings.DefaultProvider;
}
connection = CreateDbProviderFactory(provider).CreateConnection();
connection.ConnectionString = connectionString;
return connection;
}
如有任何帮助,我们将不胜感激。
注意:我无法编辑史蒂文的回答。
[编辑] 为了更清楚,它可以实现为:
这里的controller继承自BaseController
public class UserController : BaseController
{
//
// GET: /Index/
private IUserRepository userRepository;
public UserController(IUserRepository userRepository)
: base(userRepository)
{
this.userRepository = userRepository;
}
}
并且 BaseController 继承自 Controller,其中数据库设置在 Base 控制器的构造函数中设置,因此我们不需要在任何地方设置它
public abstract class BaseController : Controller
{
public BaseController(IUserRepository userRepository)
{
userRepository.connectionStringProvider.Provider = WebUtilities.CurrentUserData.Provider;
userRepository.connectionStringProvider.ConnectionString = WebUtilities.CurrentUserData.ConnectionString;
}
}
您正在寻找的是多租户。您可以 google "castle windsor multi tenancy" 并找到许多有用的文章。
这是一个类似的 Whosebug question that links to some good articles on Windsor and multi tenancy. In particular, look into Windsor's IHandlerSelector 界面。
由于连接字符串是运行时数据,因此您不应使用它来构建应用程序组件,如 this article 中所述。因此,正如文章所建议的那样,您应该将连接字符串隐藏在提供者抽象之后。例如:
public interface IConnectionStringProvider {
string ConnectionString { get; }
}
这样您的存储库就可以依赖 IConnectionStringProvider
并且可以在运行时调用 IConnectionStringProvider.ConnectionString
:
public int Count()
{
string tableName = typeof(T).Name;
string query = SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
return DbHelper.ExecuteScalar<int>(
this.connectionStringProvider.ConnectionString,
provider: provider, parameters: null);
}
创建一个 IConnectionStringProvider
将为您获取正确的连接字符串将很简单:
class DatabaseConnectionStringProvider : IConnectionStringProvider
{
public string ConnectionString => Session["connectionString"];
}
因为这个类依赖于特定的应用程序(在这种情况下是 ASP.NET 会话),所以 class 不应该是应用程序核心逻辑的一部分。相反,这个适配器应该位于应用程序的启动路径中(a.k.a。合成根,你配置容器的地方)。
您甚至可能想考虑不将 IConnectionStringProvider
传递到您的存储库中,而是创建一个自己创建连接的抽象。这将完全隐藏存在连接字符串的事实。
我这里有一个特定的场景,我需要根据用户传递连接字符串,因为用户可能会根据his/her企业映射到不同的数据库。
这是我用来解决静态变量依赖关系的代码:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IUserRepository>()
.ImplementedBy(typeof(IKS.Dare.Optimix.Repository.EntityFramework.UserModule.UserRepository))
.DependsOn(Dependency.OnValue("connectionString", DatabaseSettings.DefaultConnectionString))
);
}
因为这个 DefaultConnectionString
应该是一个动态变量,我不想锁定这个变量以使其线程安全,因为这会降低性能。我想要一种方法来处理这种情况。
可能考虑我们可以给一个session,可以申请如下:
DynamicParameters((k, d) => d["connectionString"] = Session["connectionString"])
但这是在一个不使用任何 Web 组件的不同项目中,它只是一个安装程序项目,基本上是为解决依赖关系而设计的。
我的通用存储库如下所示
public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
private const string IsActive = "IsActive", DbContext = "dbContext", EntityPropertyName = "Entity";
private string connectionString = String.Empty, provider = String.Empty;
public GenericRepository(string connectionString, string provider)
{
this.connectionString = connectionString;
this.provider = provider;
}
public int Count()
{
string tableName = typeof(T).Name;
string query = SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
int count = DbHelper.ExecuteScalar<int>(query: query, commandType: System.Data.CommandType.Text, connectionString: connectionString, provider: provider, parameters: null);
return count;
}
}
DBHelper class 如下所示
public static int ExecuteNonQuery(string query, CommandType commandType = CommandType.StoredProcedure,
IList<DbParameter> parameters = null, int? timeout = null, string connectionString = "", string provider = "")
{
using (var connection = CreateDbConnection(connectionString, provider))
{
connection.Open();
using (DbCommand command = CreateDbCommand(sqlQuery: query, parameters: parameters,
connection: connection, commandType: commandType, timeout: timeout))
{
return command.ExecuteNonQuery();
}
}
}
public static DbParameter CreateParameter<TValue>(string name, TValue value, DbType dbType,
ParameterDirection parameterDirection = ParameterDirection.Input, string provider = "")
{
DbParameter param = CreateDbProviderFactory(provider).CreateParameter();
param.Value = value;
param.ParameterName = name;
param.DbType = dbType;
param.Direction = parameterDirection;
return param;
}
public static DbConnection CreateDbConnection()
{
return CreateDbConnection(String.Empty, String.Empty);
}
public static DbConnection CreateDbConnection(string connectionString = "", string provider = "")
{
DbConnection connection = null;
if (String.IsNullOrEmpty(provider))
{
if (String.IsNullOrEmpty(DatabaseSettings.DefaultProvider))
throw new ArgumentNullException("provider");
else
provider = DatabaseSettings.DefaultProvider;
}
connection = CreateDbProviderFactory(provider).CreateConnection();
connection.ConnectionString = connectionString;
return connection;
}
如有任何帮助,我们将不胜感激。
注意:我无法编辑史蒂文的回答。 [编辑] 为了更清楚,它可以实现为:
这里的controller继承自BaseController
public class UserController : BaseController
{
//
// GET: /Index/
private IUserRepository userRepository;
public UserController(IUserRepository userRepository)
: base(userRepository)
{
this.userRepository = userRepository;
}
}
并且 BaseController 继承自 Controller,其中数据库设置在 Base 控制器的构造函数中设置,因此我们不需要在任何地方设置它
public abstract class BaseController : Controller
{
public BaseController(IUserRepository userRepository)
{
userRepository.connectionStringProvider.Provider = WebUtilities.CurrentUserData.Provider;
userRepository.connectionStringProvider.ConnectionString = WebUtilities.CurrentUserData.ConnectionString;
}
}
您正在寻找的是多租户。您可以 google "castle windsor multi tenancy" 并找到许多有用的文章。
这是一个类似的 Whosebug question that links to some good articles on Windsor and multi tenancy. In particular, look into Windsor's IHandlerSelector 界面。
由于连接字符串是运行时数据,因此您不应使用它来构建应用程序组件,如 this article 中所述。因此,正如文章所建议的那样,您应该将连接字符串隐藏在提供者抽象之后。例如:
public interface IConnectionStringProvider {
string ConnectionString { get; }
}
这样您的存储库就可以依赖 IConnectionStringProvider
并且可以在运行时调用 IConnectionStringProvider.ConnectionString
:
public int Count()
{
string tableName = typeof(T).Name;
string query = SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
return DbHelper.ExecuteScalar<int>(
this.connectionStringProvider.ConnectionString,
provider: provider, parameters: null);
}
创建一个 IConnectionStringProvider
将为您获取正确的连接字符串将很简单:
class DatabaseConnectionStringProvider : IConnectionStringProvider
{
public string ConnectionString => Session["connectionString"];
}
因为这个类依赖于特定的应用程序(在这种情况下是 ASP.NET 会话),所以 class 不应该是应用程序核心逻辑的一部分。相反,这个适配器应该位于应用程序的启动路径中(a.k.a。合成根,你配置容器的地方)。
您甚至可能想考虑不将 IConnectionStringProvider
传递到您的存储库中,而是创建一个自己创建连接的抽象。这将完全隐藏存在连接字符串的事实。