如何在C#的城堡windsor中解决运行时依赖性

时间:2016-05-06 10:51:19

标签: c#-4.0 castle-windsor

我在这里有一个特定的场景,我需要根据用户传递连接字符串,因为用户可能会根据他/她的企业映射到不同的数据库。

这是我用来解决静态变量依赖的代码:

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应该是动态的,我不想锁定这个变量以使其线程安全,因为这会降低性能。我想要一种方法,以便我可以处理这种情况。

我们可以提供会议的可能考虑因素,可以按如下方式应用:

DynamicParameters((k, d) => d["connectionString"] = Session["connectionString"])

但是这是一个不使用任何Web组件的不同项目,它只是一个安装程序项目,基本上只用于解析依赖项。

My Generic存储库如下所示

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类如下所示

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;
        }

非常感谢任何帮助。

注意:我无法编辑史蒂文的答案。 [编辑]为了使其更清楚,它可以实现为:

此处控制器继承自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;
        }
    }

2 个答案:

答案 0 :(得分:3)

由于连接字符串是运行时数据,因此不应使用它来构建应用程序组件,如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"];
}

由于此clas依赖于特定于应用程序(在本例中为ASP.NET会话),因此该类不应该是应用程序核心逻辑的一部分。相反,这个适配器应该存在于应用程序的启动路径中(例如,组合根,配置容器的位置)。

您甚至可能想要考虑不将IConnectionStringProvider传递到您的存储库中,而是创建一个将自己创建连接的抽象。这将隐藏完全存在连接字符串的事实。

答案 1 :(得分:0)

您正在寻找的是多租户。您可以谷歌“castle windsor multi tenancy”并找到一些有用的文章。

这是一个类似的Stackoverflow question链接到一些关于温莎和多租户的好文章。特别要看看温莎的IHandlerSelector界面。