Как я правильно использую Единицу для передачи ConnectionString моим классам репозитория?

Я буквально только что начал пользоваться библиотекой Unity Application Blocks Dependency Injection от Microsoft, и я провалился.

Это - мой класс МОК, это обработает инстанцирование моих реальных классов к их интерфейсным типам (таким образом, я не должен буду сохранять названную Твердость на контейнере МОК каждым разом, когда я хочу репозиторий в своем контроллере):

public class IoC
{
    public static void Intialise(UnityConfigurationSection section, string connectionString)
    {
        _connectionString = connectionString;
        _container = new UnityContainer();
        section.Configure(_container);
    }

    private static IUnityContainer _container;
    private static string _connectionString;

    public static IMovementRepository MovementRepository
    {
        get { return _container.Resolve<IMovementRepository>(); }
    }
}

Так, идея состоит в том, что от моего Контроллера, я могу просто сделать следующее:

_repository = IoC.MovementRepository;

Я в настоящее время получаю ошибку:

Исключение: InvalidOperationException - Строка типа не может быть создана. Необходимо настроить контейнер для предоставления этого значения.

Теперь, я предполагаю, что это вызвано тем, что моя отображенная конкретная реализация требует отдельного строкового параметра для своего конструктора. Реальный класс следующие:

public sealed class MovementRepository : Repository, IMovementRepository
{
    public MovementRepository(string connectionString) : base(connectionString) { }
}

Который наследовался:

public abstract class Repository
{
    public Repository(string connectionString)
    {
        _connectionString = connectionString;
    }

    public virtual string ConnectionString
    {
        get { return _connectionString; }
    }
    private readonly string _connectionString;
}

Теперь, я делаю это корректный путь? Разве у меня не должно быть конструктора в моей конкретной реализации слабо связанного типа? Т.е. если я удаляю конструктора и просто заставляю свойство a ConnectionString Получить/Установить так, я могу сделать следующее:

public static IMovementRepository MovementRepository
{
   get
   {
      return _container.Resolve<IMovementRepository>(
         new ParameterOverrides
         {
            { 
               "ConnectionString", _connectionString 
            }
         }.OnType<IMovementRepository>() );
   }
}

Так, я в основном хочу знать, как получить мою строку подключения к моему конкретному типу корректным способом, который соответствует правилам МОК и сохраняет мой Контроллер и бетонные репозитории слабо связанными, таким образом, я могу легко изменить DataSource позднее.

РЕДАКТИРОВАНИЕ 9:52:

Только для повторения то, что я после. Я хочу знать корректный способ передать ConnectionString, или объект IRepositoryConfiguration (предпочтите что идея, МГц спасибо) к реальному классу от Единицы. Я не слишком суечусь, какому я передаю, как я передаю его при поддержании слабой связи.

18
задан abatishchev 9 August 2011 в 19:20
поделиться

4 ответа

Вероятно, самый простой способ сделать это - создать секцию конструктора для вашего отображаемого типа. в конфигурации Unity и внутри раздела конструктора есть элемент параметра для строки подключения, который передает значение имени для строки подключения, которую вы определили в разделе connectionStrings своей веб-конфигурации.

Внутри кода конструктора для класса Repository имейте некоторый код, который использует значение имени строки подключения для получения полной строки подключения из раздела connectionStrings.

РЕДАКТИРОВАТЬ:

Вот пример использования Unity 2.0

В вашем web.config укажите строку подключения и сопоставление для единства, чтобы сопоставить IRepository с SqlRepository . Основываясь на других ваших вопросах, мы предположим, что IRepository находится в вашем модельном проекте, а SqlRepository находится в вашем проекте DAL.

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
    </configSections>
    <connectionStrings>
        <add name="SqlConnection" connectionString="data source=(local)\SQLEXPRESS;Integrated Security= SSPI; Initial Catalog= DatabaseName;" providerName="System.Data.SqlClient"/>
    </connectionStrings>
    <unity>
        <containers>
            <container>
                <types>
                    <type type="ModelProject.IRepository`1, ModelProject" mapTo="DALProject.SqlRepository`1, DALProject">
                        <constructor>
                            <param name="connectionString">
                                <value value="SqlConnection" />
                            </param>
                        </constructor>
                    </type>
                </types>
            </container>
        </containers>
  </unity>
</configuration>

Теперь об интерфейсе IRepository в модельном проекте. В этом примере я также собираюсь использовать LINQ to SQL для возврата объектов из базы данных SQL

namespace ModelProject
{
    /// <summary>
    /// Interface implemented by a Repository to return
    /// <see cref="IQueryable`T"/> collections of objects
    /// </summary>
    /// <typeparam name="T">Object type to return</typeparam>
    public interface IRepository<T>
    {
        IQueryable<T> Items { get; }
    }
}

и класса SQLRepository в проекте DAL

namespace DALProject
{
    /// <summary>
    /// Generic class for returning an <see cref="IQueryable`T"/>
    /// collection of types
    /// </summary>
    /// <typeparam name="T">object type</typeparam>
    public class SqlRepository<T> : IRepository<T> where T : class
    {
        private Table<T> _table;

        public SqlRepository(string connectionString)
        {
            // use the connectionString argument value to get the
            // connection string from the <connectionStrings> section
            // in web.config
            string connection = ConfigurationManager.ConnectionStrings[connectionString].ConnectionString;

            _table = (new DataContext(connection)).GetTable<T>();
        }

        /// <summary>
        /// Gets an <see cref="IQueryable`T"/> collection of objects
        /// </summary>
        public IQueryable<T> Items
        {
            get { return _table; }
        }
    }
}

. фабрика контроллеров, чтобы позволить единству вернуть нам контроллеры. Таким образом, unity будет внедрять любые зависимости, которые имеют контроллеры

В global.asax

namespace WebApplicationProject
{
    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            // your routes
        }

        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory());
        }
    }

    public class UnityControllerFactory : DefaultControllerFactory
    {
        private IUnityContainer _container;

        public UnityControllerFactory()
        {
            _container = new UnityContainer();

            var controllerTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
                                  where typeof(IController).IsAssignableFrom(t)
                                  select t;

            foreach (Type t in controllerTypes)
                _container.RegisterType(t, t.FullName);

            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");

            section.Configure(_container);
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            // see http://stackoverflow.com/questions/1357485/asp-net-mvc2-preview-1-are-there-any-breaking-changes/1601706#1601706
            if (controllerType == null) { return null; }

            return (IController)_container.Resolve(controllerType);
        }
    }

}

А вот пример контроллера. Размер страницы может быть определен на базовом контроллере или на контроллере как свойство.

namespace WebApplicationProject.Controllers
{
    public class CustomersController : Controller
    {
        private IRepository<Customer> _customerRepository;
        public int PageSize { get; set; }

        public CustomersController() { }

        public CustomersController(IRepository<Customer> customerRepository)
        {
            this._customerRepository = customerRepository;
            // let's set it to 10 items per page.
            this.PageSize = 10; 
        }

        public ViewResult List(string customerType, int page)
        {
            var customerByType = (customerType == null) ?
                customerRepository.Items : customerRepository.Items.Where(x => x.CustomerType == customerType);

            int totalCustomers = customerByType.Count();
            ViewData["TotalPages"] = (int)Math.Ceiling((double)totalCustomers/ PageSize);
            ViewData["CurrentPage"] = page;
            ViewData["CustomerType"] = customerType;

            // get the right customers from the collection
            // based on page number and customer type.    
            return View(customerByType
                .Skip((page - 1) * PageSize)
                .Take(PageSize)
                .ToList()
            );
        }

    }
}

Когда вызывается действие контроллера списка клиентов, unity правильно создает экземпляр SqlRepository для контроллера и вводит его в конструктор. строка connectionString, используемая для SqlRepository , задается в конфигурации единства и передается в конструктор для типизированного SqlRepository .

15
ответ дан 30 November 2019 в 06:54
поделиться

Вы можете настроить контейнер единства для этого:

IUnityContainer container = new UnityContainer()
  .RegisterType<IMovementRepository, MovementRepository>(
    new InjectionConstructor("connectionstring goes here"));

в XLM, который, вероятно, будет примерно таким:

<type type="foo.IMovementRepository,foo" mapTo="foo.MovementRepository,foo">
  <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">     
   <constructor>
     <param name="connectionString" parameterType="System.String" >
       <value value="conectionstring goes here" type="System.String"/>
     </param>           
   </constructor>
  </typeConfig>
</type>

или обернуть строку соединения, как указывает mcaaltuntas.

22
ответ дан 30 November 2019 в 06:54
поделиться

Я не использовал Unity, но я использовал объекты конфигурации в этих ситуациях. Например, вы можете написать свой код следующим образом

class Configuration:IRepositoryConfiguration,IMailConfiguration
{
    private string connectionString;

    //IRepository configurations
    public string ConnectionString
    {
        //read connection string from somewhere
        get { return connectionString; }
    }

    //EMail configurations
    public string Smtp
    {
        get { return smpt; }
    }

}

interface IRepositoryConfiguration
{
    string ConnectionString { get;}
}



public abstract class Repository
    {
        public Repository(IRepositoryConfiguration configuration)
        {
            _connectionString = configuration.ConnectionString;
        }

        public virtual string ConnectionString
        {
            get { return _connectionString; }
        }
        private readonly string _connectionString;
    }

Таким образом, вы можете зарегистрировать IRepositoryConfiguration, и Unity разрешит ваши объекты конфигурации. Также вы можете легко добавить дополнительные параметры в этот подход.

Обновление

Я считаю нормальным иметь конструктор, принимающий объект IRepositoryConfiguration в ваших конкретных классах (абстрактный репозиторий и MovementRepository). Поскольку они являются деталями реализации и конкретными реализациями IMovementRepository. Поэтому им необходимо знать строку подключения.

Внедрение через сеттер или конструктор

Я предпочитаю внедрение конструктора над инъекцией через сеттер. Я думаю, что внедрение конструктора приводит к более доступным API. при внедрении конструктора, как только вы захотите создать экземпляр объекта, вы увидите, что объекту необходимо для работы, но при внедрении Setter вы должны узнать, какое свойство установить для использования API.Для получения подробной информации вы можете прочитать Constructor ve Setter Injection

4
ответ дан 30 November 2019 в 06:54
поделиться

Я добавлю другой способ :)

Вы можете использовать параметры внедрения, которые передаются в конструктор при регистрации типа. Таким образом, вы все еще можете использовать инъекцию конструктора.

class Repository : IRepository {
  readonly string m_ConnectionString;
  public Repository(string connectionString) {
    ConnectionString = connectionString;
  }
}

//When registering
container.RegisterType<IRepository, Repository>(new InjectionConstructor("connectionstring"));
2
ответ дан 30 November 2019 в 06:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: