如何在Castle Windsor for C#中解决运行时依赖项 [英] How to resolve runtime dependencies in castle windsor for C#

查看:78
本文介绍了如何在Castle Windsor for C#中解决运行时依赖项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



这是我使用静态变量解析依赖关系的代码:

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



可能的考虑可能是我们可以举行一个会议,可以这样应用:

p>

  DynamicParameters((k,d)=> d [ connectionString] =会话[ connectionString])

但这是一个不同的项目,不使用任何Web组件,它只是一个安装程序项目,基本上是设计好的



我的通用存储库如下

 公共类GenericRepository< T> :IGenericRepository< T>其中T:BaseEntity 
{
私有常量字符串IsActive = IsActive,DbContext = dbContext,EntityPropertyName = Entity;

私有字符串connectionString = String.Empty,提供程序= String.Empty;

public GenericRepository(string connectionString,字符串提供者)
{
this.connectionString = connectionString;
this.provider =提供程序;
}
public int Count()
{
string tableName = typeof(T).Name;
字符串查询= SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
int count = DbHelper.ExecuteScalar< int>(查询:查询,commandType:System.Data.CommandType.Text,connectionString:connectionString,provider:提供者,参数:null);
返回计数;
}
}

DBHelper类如下

  public static int ExecuteNonQuery(字符串查询,CommandType commandType = CommandType.StoredProcedure,
IList< DbParameter>参数=空,int?超时=空,字符串connectionString =,字符串提供者=)
{
使用(var connection = CreateDbConnection(connectionString,provider))
{
connection.Open();
使用(DbCommand命令= CreateDbCommand(sqlQuery:查询,参数:参数,
连接:连接,commandType:commandType,超时:超时)))
{
返回命令.ExecuteNonQuery( );
}
}
}

公共静态DbParameter CreateParameter< TValue>(字符串名称,TValue值,DbType dbType,
ParameterDirection parameterDirection = ParameterDirection.Input ,string provider =)
{
DbParameter param = CreateDbProviderFactory(provider).CreateParameter();
param.Value =值;
param.ParameterName =名称;
param.DbType = dbType;
param.Direction = parameterDirection;
返回参数;
}


公共静态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;
返回连接;
}

任何帮助将不胜感激。



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



此处的控制器是从BaseController继承的

 公共类UserController:BaseController 
{
//
// GET:/ Index /
private IUserRepository userRepository;

public UserController(IUserRepository userRepository)
:base(userRepository)
{
this.userRepository = userRepository;
}
}

并且BaseController继承自Controller,在数据库设置中在Base控制器的构造函数中进行设置,因此我们无需在任何地方进行设置

 公共抽象类BaseController:控制器
{
public BaseController(IUserRepository userRepository)
{
userRepository.connectionStringProvider.Provider = WebUtilities.CurrentUserData.Provider;
userRepository.connectionStringProvider.ConnectionString = WebUtilities.CurrentUserData.ConnectionString;
}
}


解决方案

,连接字符串是运行时数据,您不应使用它来构建应用程序组件,如本文。因此,如本文所建议,您应该将连接字符串隐藏在提供程序抽象的后面。例如:

 公共接口IConnectionStringProvider {
string ConnectionString {get; }
}

这样,您的存储库就可以依赖 IConnectionStringProvider 并可以在运行时调用 IConnectionStringProvider.ConnectionString

  public int Count()
{
string tableName = typeof(T).Name;
字符串查询= SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
返回DbHelper.ExecuteScalar< int>(
this.connectionStringProvider.ConnectionString,
provider:provider,parameters:null);
}

创建 IConnectionStringProvider

会很简单code>至将为您获取正确的连接字符串:

  class DatabaseConnectionStringProvider:IConnectionStringProvider 
{
公共字符串ConnectionString =>会话[ connectionString];
}

由于此分类取决于特定于应用程序的内容(本章中的ASP.NET会话)情况),该类不应成为应用程序核心逻辑的一部分。相反,此适配器应位于应用程序的启动路径(即合成根,即配置容器的位置)中。



您甚至可能要考虑不通过沿着 IConnectionStringProvider 进入您的存储库,而是创建一个抽象来创建连接本身。这将掩盖一个事实,即完全有一个连接字符串。


I have a specific scenario here where I need to pass the connection string based on the user, because users may be mapped to the different databases based on his/her enterprise.

This is the code I use to resolve the dependency with a static variable:

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

Because this DefaultConnectionString is supposed to be a dynamic one, I don't want to lock this variable to make it thread safe, as this would degrade the performance. I would want a way so that I can deal with such situation.

Possible consideration which can be that we can give a session, which can be applied as follows:

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

But this is in a different project which doesn't utilize any web component, it's just an installer project which is basically designed for resolving the dependencies only.

My Generic repository looks like following

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 looks like follows

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

Any help would be greatly appreciated.

Note : I couldn't edit steven's answer. [EDIT] To make it more clear it can be implemented as:

Here controller is inherited from BaseController

public class UserController : BaseController
    {
        //
        // GET: /Index/
        private IUserRepository userRepository;

        public UserController(IUserRepository userRepository)
            : base(userRepository)
        {
            this.userRepository = userRepository;
        }
}

and BaseController is inherited from Controller where in the database settings are being set in the constructor of Base controller so that we don't need to set it everywhere

public abstract class BaseController : Controller
    {
        public BaseController(IUserRepository userRepository)
        {
            userRepository.connectionStringProvider.Provider = WebUtilities.CurrentUserData.Provider;
            userRepository.connectionStringProvider.ConnectionString = WebUtilities.CurrentUserData.ConnectionString;
        }
    }

解决方案

Since, the connection string is runtime data, you should not use it to construct your application components, as is described in this article. So as the article advices, you should hide the connection string behind a provider abstraction. For instance:

public interface IConnectionStringProvider {
    string ConnectionString { get; }
}

This way your repositories can depend on IConnectionStringProvider and can call IConnectionStringProvider.ConnectionString at runtime:

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

It will be trivial to create an IConnectionStringProvider to will get the correct connection string for you:

class DatabaseConnectionStringProvider : IConnectionStringProvider
{
    public string ConnectionString => Session["connectionString"];
}

Since this clas depends on application-specifics (the ASP.NET session in this case), the class should not be part of the application's core logic. Instead, this adapter should live in the application's start up path (a.k.a. the composition root, the place where you configure your container).

You might even want to consider not passing along the IConnectionStringProvider into your repositories, but instead create an abstraction that will create a connection itself. This will hide the fact that there is a connection string completely.

这篇关于如何在Castle Windsor for C#中解决运行时依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆