Подтвердить что ты не робот

Как правильно использовать Unity для передачи ConnectionString в мои классы репозитория?

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

Это мой класс IoC, который будет обрабатывать экземпляры моих конкретных классов для их типов интерфейсов (поэтому мне не нужно постоянно вызывать Resolve в контейнере IoC каждый раз, когда я хочу репозиторий в своем контроллере):

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

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

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

Итак, я в основном хочу знать, как правильно получить строку подключения к вашему конкретному типу, которая соответствует правилам IoC, и держит мой контроллер и конкретные репозитории свободными, поэтому я могу легко изменить DataSource на более позднюю дату.

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

Просто повторить, что мне нужно. Я хочу знать правильный способ передать объект ConnectionString или объект IRepositoryConfiguration (предпочитайте эту идею, спасибо Mc) конкретному классу из Unity. Я не слишком суетился о том, что я прохожу, как я передаю его, сохраняя свободную связь.

4b9b3361

Ответ 1

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

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

EDIT:

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

В вашем web.config укажите строку соединения и сопоставление для единства, чтобы сопоставить IRepository<T> с SqlRepository<T>. Исходя из ваших других вопросов, мы предположим, что IRepository<T> находится в вашем проектном проекте, а SqlRepository<T> - в вашем проекте 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<T> в проекте модели. В этом примере я также буду использовать 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<T> в проекте 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; }
        }
    }
}

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

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

}

И вот пример контроллера. PageSize может быть определен на базовом контроллере или на контроллере в качестве свойства.

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

    }
}

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

Ответ 2

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

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.

Ответ 3

Я не использовал 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 разрешит ваши объекты конфигурации. Также вы можете легко добавить дополнительные параметры в этот подход.

Update

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

Установка сеттера или конструктора

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

Ответ 4

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

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

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

//When registering
container.RegisterType<IRepository, Repository>(new InjectionConstructor("connectionstring"));