2016-05-06 5 views
1

У меня есть конкретный сценарий здесь, где мне нужно передать строку соединения на основе пользователя, поскольку пользователи могут быть сопоставлены с различными базами данных на основе его/ее предприятия.Как разрешить зависания во время выполнения в виндсерке замка для 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 должен быть динамичным, я не хочу, чтобы заблокировать эту переменную, чтобы сделать нить так как это ухудшит производительность. Я бы хотел, чтобы я мог справиться с такой ситуацией.

Возможное соображение, которое может быть то, что мы можем дать сеанс, который может быть применен следующим образом:

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

Но это в другом проекте, который не использует какой-либо веб-компонент, это просто инсталлятор проект, который в основном предназначен только для разрешения зависимостей.

Мой 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; 
     } 

Любая помощь будет принята с благодарностью.

Примечание: я не мог отредактировать ответ Стивена. [EDIT] Для того, чтобы сделать его более ясным, он может быть реализован как:

Здесь контроллер наследуется от BaseController

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

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

и BaseController наследуется от контроллера, где в настройках базы данных создаются в конструкторе Базовый контроллер, поэтому нам не нужно его устанавливать везде

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

Данные времени выполнения [не должны вставляться в конструктор ваших компонентов] (https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99). – Steven

+0

@Steven, я понимаю вашу точку зрения, но у меня есть ситуация, когда мои репозитории зависят от переменной строки подключения и поставщиков. Можете ли вы предложить мне лучший подход? –

+0

Прежде всего, поскольку у вас будет возможно много репозиториев, для которых требуется соединение, скройте строку подключения за абстракцией 'IConnectionFactory' или' IDbContextProvider' (в зависимости от ваших потребностей). При такой абстракции становится тривиальным создание реализации адаптера, которая получает правильную строку соединения во время выполнения и создает либо новый SqlConnection, либо DbContext. Репозиторий, чем вызовы 'connectionFactory.CreateConnection()' или 'dbContextProvider.CurrentContext'. – Steven

ответ

2

Поскольку строка соединения представляет собой данные времени выполнения, вы не должны использовать ее для создания компонентов приложения, как описано в 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), класс sho uld не является частью основной логики приложения. Вместо этого этот адаптер должен находиться в пути запуска приложения (a.k.a. корневой состав, место, где вы настраиваете свой контейнер).

Возможно, вы даже не захотите, чтобы вы не проходили вдоль IConnectionStringProvider в свои репозитории, а вместо этого создавали абстракцию, которая сама создаст соединение. Это скроет тот факт, что строка подключения полностью.

+0

Но ситуация в том, что мои репозитории находятся в другом слое, который напрямую не взаимодействует с Session. Не могли бы вы сделать это немного более ясным, я думаю, что это будет большой помощью. Даже я не хочу использовать динамические зависимости. –

+0

@AjaySuwalka: Это красота абстракций. Вы можете разместить абстракцию в другой сборке, чем поместить ее реализацию. По сути, реализация должна быть частью вашего [состава root] (http://blog.ploeh.dk/2011/07/28/CompositionRoot/). Поскольку ваш корневой состав является специфичным для приложения, вы можете зависеть от всего, что характерно для этого приложения, например сеанса. – Steven

0

Что вы ищете - это аренда нескольких объектов. Вы можете найти «castle windsor multi tenancy» и найти ряд полезных статей.

Вот аналогичный Stackoverflow question, который ссылается на некоторые хорошие статьи о Виндзоре и многоквартирном доме. В частности, посмотрите интерфейс Windsor IHandlerSelector.

+0

Я ценю, что вы пытались ответить на мой вопрос, но я не мог связать свою ситуацию с ответами, которые вы предоставили. Спасибо. –