0

У меня есть библиотека уровня доступа к данным, которую я хотел бы сделать «переносной». Причина, по которой мне нравится переносить, - это то, что я хочу работать с SQL Azure & Azure File Storage (например, данные + PDF-отчеты), а также Sql Server 2008R2 и хранилище файловой системы на конкретном сервере.Контейнер для инъекций зависимостей

На основе спецификации предполагается, что система будет работать с последующей реализацией (sql + файловое хранилище), а при встрече с определенным порогом масштабируемости мы планируем перейти на Azure.

Класс доступа к данным, который я использую, реализует интерфейс IDataProvider (который я построил) и определяет методы, которые должны иметь конкретные реализации доступа к данным. Потребляя уровень доступа к данным осуществляется через прохождение интерфейс IDataProvider и вызова методов на него, например:

public Interface IDataProvider 
{ 
    public bool DoSomething(); 
} 

public class AzureDataProvider : IDataProvider 
{ 
    private string ConnectionString; 

    public AzureDataProvider(string connectionString) 
    { 
     this.ConnectionString = connectionString; 
    } 

    public AzureDataProvider():this(
     ConfigurationManager.ConnectionString["conn"].ConnectionString) 
    { 
    } 

    public bool DoSomething() 
    { 
     return false; 
    } 
} 

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

public class DataAccessConsumer 
{ 
    public void SomeOperation() 
    { 
     AzureDataProvider azureProvider = new AzureDataProvider(); 
     IDataProvider dataProvider = (IDataProvider)azureProvider; 

     bool result = dataProvider.DoSomething(); 
    } 
} 

Таким образом, проблема с вышеуказанным кодом заключается в том, что клиент все еще должен знать конкретный класс AzureDataProvider. Я хочу иметь способ предоставления клиенту только интерфейса IDataProvider, не передавая строку соединения для каждого метода интерфейса или не просматривая строку соединения в каждом методе через ConfigurationManager.

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

+0

Какие проекты будут использовать ваш код доступа к данным? –

ответ

1

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

Подключение интерфейса:

public interface IConnection 
{ 
    public string ConnectionString; 
} 

Бетон соединения Реализация

public class Connection: IConnection 
{ 
    public string ConnectionString{ get; set; } 

    public Connection(string connectionString) 
    { 
     this.ConnectionString = connectionString; 
    } 

    public Connection():this(ConfigurtionManager.ConnectionStrings["connection"].ConnectionString) 
    { 
     //Broke DI in the interest of usability. 
    } 
} 

Доступ к данным Layer Interface

public interface IDataProvider 
{ 
    IConnection Connection; 

    public void Foo(); 
} 

Concrete доступа к данным Реализация Layer

public class AzureProvider : IDataProvider 
{ 
    IConnection Connection { get; set; } 

    public AzureProvider(IConnection connection) 
    { 
     this.Connection = connection; 
    } 

    public void Foo() 
    { 

    } 
} 

DI Conainer/Factory (Singleton или статический класс)

public static class ProviderFactory 
{ 
    public static IDataProvider GetProvider() //I'd pass parameters if I had more than 1. 
    { 
     Connection connection = new Connection(); //this is why I broke DI. 
     IConnection iConnection = (IConnection)connection; 

     AzureProvider azureProvider = new AzureProvider(iConnection); 
     IDataProvider iDataProvider = (IDataProvider)azureProvider; 

     return iDataProvider; 
    } 
} 

доступа к данным уровня потребителей (в данном примере это страница):

public class SomePage : Page 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     IDataProvider provider = ProviderFactory.GetProvider(); 
     provider.Foo(); 
    } 
} 

Как вы можете видеть, на странице не нужно знать какие-либо детали реализации уровня доступа к данным. Пока ProviderFactory может плюнуть на IDataProvider, страница счастлива. Поэтому, если мы решили сменить поставщиков, скажем, SqlStorageProvider, если он реализует интерфейс IDataProvider, код страницы не нужно изменять. Это обеспечивает истинное разделение проблем с точки зрения архитектуры программного обеспечения.

+1

Вы должны были пройти фабрику поставщика (а также использовать ConnectionFactory вместо создания самого соединения) в качестве зависимостей для достижения истинного DI. – Arafangion

+0

Вы правы. Я об этом не думал! Соответственно изменит мой дизайн! – bleepzter

1

Во-первых, AzureDataProvider имеет зависимость от ConfigurationManager. Вместо этого это нужно вводить.

Во-вторых, этот DataProvider следует вводить в DataAccessConsumer.

Это означает, что реалистичное приложение будет иметь широкую инъекцию зависимостей, без зависимости от контейнера, однако вам понадобится «проводка» - соединение всех зависимостей вместе. Это боль - используйте DependencyInjectionContainer только в главной точке входа, чтобы помочь решить эту проводку. (Это позволяет использовать более удобный декларативный подход, а не императивный подход, потому что вы можете спросить контейнер «Получите мне DataAccessConsumer», а инфраструктура внедрения зависимостей будет определять зависимости для вас.

Моя любимая зависимость Рамка для инъекций для C# представляет собой NInject2.

+1

+1 Вы также можете проверить этот вопрос, ответив на этот ответ: http://stackoverflow.com/questions/4570750/dependency-injection-turtles-all-the-way-down –

+0

Спасибо Mark , Это был самый полезный пост, с которым я столкнулся. Я ценю комментарий от Arafangion, однако он просто повторяет очевидное. так и Joe R. – bleepzter

Смежные вопросы