Я нарушаю правила шаблона DI - PullRequest
0 голосов
/ 03 декабря 2018

Я очень плохо знаком с DI, поэтому мои знания не самые лучшие.В настоящее время я работаю над приложением Asp.Net Core MVC.

Предположим, у меня есть DLL, которая возвращает DataSet (у меня есть полный контроль над этой DLL и я могу изменить ее при необходимости).

Таким образом, код в DLL выглядит следующим образом:

public class DataStore : IDataStore
{       
   private readonly IConfiguration _configuration;       

   public DataStore(IConfiguration configuration)
   {
       _configuration = configuration;
   }

   public DataSet GetDataBySP(string spName, SqlParameter[] parameters = null)
   {
      DataSet ds = new DataSet();
      using(var con = new SqlConnection(_configuration.GetConnectionString("conStr")))
      {
         using(var cmd = new SqlCommand(spName, con))
         {
              cmd.CommandType = CommandType.StoredProcedure;

              if (parameters?.Length > 0)
                 cmd.Parameters.AddRange(parameters);

              con.Open();

              using (var da = new SqlDataAdapter(cmd))
              {
                 da.Fill(ds);
              }
         }
      }
      return ds;
   }
}

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

public interface IFoo
{
   Foo GetFooData();
}

public class Foo : IFoo
{
    private readonly IDataStore _dataStore;

    public int CurrentCount { get; set; }
    public int MaxCount { get; set; }

    public Foo(DataRow dr)
    {
        CurrentCount = dr.Field<int>("CurrentCount");
        MaxCount = dr.Field<int>("MaxCount");
    }

    public Foo(IDataStore dataStore)
    {
        _dataStore = dataStore;
    }
}

В Foo У меня есть метод с именем GetFooData следующим образом:

public Foo GetFooData()
{
    var ds = _dataStore.GetDataBySP("sp name");

    //is the below code breaking the DI pattern rule?
    return ds.Tables[0].AsEnumberable().Select(f => new Foo(f)).FirstOrDefault();
}

В методе Startup.cs inside ConfigureServices(IServiceCollection services) я делаю следующее:

services.AddScoped<IFoo, Foo>();

Итак, мой вопрос,делая new Foo(f) внутри Select() я нарушаю шаблон DI?Если да, то можете ли вы дать совет, как это преодолеть, пожалуйста.

Ответы [ 2 ]

0 голосов
/ 12 декабря 2018

Да, вы нарушаете дух DI, но, возможно, не по той причине, о которой думаете.

Как отмечается в одном из комментариев, вы потенциально нарушаете SRP с помощью Foo.В какой-то момент вы говорите, что он «содержит только два свойства», но он также содержит логику и принимает и хранит ссылку DataStore.Если бы эти два свойства действительно были все , которые содержал Foo, это, по сути, было бы DTO ... тупой пакет свойств без поведения или бизнес-правил, и в этом случае ясказал бы, что вы не нарушаете дух DI, просто вызывая, по сути, new Foo(properties) ... потому что анонимный объект или кортеж может служить той же цели ... это существенно уменьшит Foo до content ,DI не о том, чтобы «никогда не вызывать new», а о том, что логический код не разрешает свои собственные зависимости для выполнения своей логики.

Поскольку в вашем примере вы также дали Foo ответственность за опросDataSource чтобы найти записи, которые имеют эти свойства, у вас do есть логика поиска данных внутри Foo, и вы определенно попадаете в некоторую серую область.Как уже упоминалось, у вашего класса теперь есть две разные и непересекающиеся обязанности.Вы начинаете приближаться к концепциям DDD / rich domain, где Foo будет отвечать за транспортировку своих собственных данных и , решая проблемы бизнеса, связанные с манипулированием этими данными.Но DDD не является самой дружественной к DI архитектуре и обычно считается более подходящей для больших и сложных доменов, чем для маленьких.

Вся идея DI заключается в том, что единицы кода (классы) не должны разрешать свои собственные зависимости, чтобы помочь управлять / организовать / уменьшить сложность.Если вы выделите метод GetFooData() в Foo для какого-то другого класса, который предоставлен DI, и уменьшите Foo до простого пакета DTO / свойства, то я думаю, что у вас по крайней мере будет аргумент , что вы не нарушаете дух DI, потому что тогда Foo не является поведенческой зависимостью кода, который преобразует DataRow в Foo.

0 голосов
/ 03 декабря 2018

Я бы решил вашу проблему с помощью репозиториев и объектов передачи данных (DTO) , например:

Некоторые общедоступные интерфейсы, которые вы сможетедля внедрения в ваш код:

    public interface IFooRepository
    {
        FooDto GetFirst();
    }

    public interface IDataSetFactory
    {
        DataSet GetDataSet(string spName, SqlParameter[] = null);
    }

Некоторый DTO для передачи данных из одного класса в другой (и только данные):

    public class FooDto
    {
        public int CurrentCount { get; set; }
        public int MaxCount { get; set; }
    }

Некоторая внутренняя реализация, скрытая от глаз интерфейсаuser:

    internal sealed class FooRepository : IFooRepository
    {
        private readonly IDataSetFactory _dsFactory;
        public FooRepository(IDataSetFactory dsFactory)
        {
            _dsFactory = dsFactory;
        }

        public FooDto GetFirst()
        {
            return _dsFactory.GetDataSet("sp name")
                             .Tables[0]
                             .AsEnumberable()
                             .Select(Map)
                             .FirstOrDefault();
        }
        private FooDto Map(DataRow dr)
        {
            return new FooDto
                   {
                       CurrentCount = dr.Field<int>("CurrentCount"),
                       MaxCount = dr.Field<int>("MaxCount")
                   };
        }
    }

    internal sealed class DataSetFactory : IDataSetFactory
    {
        public DataSetFactory() { }
        private DataSet GetDataSet(string spName, SqlParameter[] = null)
        {
            //set up sql parameters, open connection
            return ds;
        }
    }

И, учитывая некоторую специфическую структуру DI (Ninject, DryIoc и т. д.), вы можете внедрить эти интерфейсы в любое место кода.Огромное преимущество использования DI - оно заставляет вас правильно проектировать ваше приложение (интерфейсы отделены от реализации), поэтому в какой-то момент вы можете реализовать свой IFooRepository для чтения данных со звезд на небе или изфайл, или чтобы вернуть пустые результаты в тестировании, или все, что вы хотите, чтобы он делал.

Если вы спроектировали неправильно - он, вероятно, не будет компилироваться или будет слишком сложным.Хорошее использование DI - сложный вопрос, но, по оценкам, 99% ваших * .cs файлов НЕ будут ссылаться на ЛЮБЫЕ ссылки инфраструктуры DI:

using System.IO;
using Ninject; //<-- I mean, this one. If it is everywhere, something is overcomplexed and you are not using framework correctly.
using System.Media;
...