我违反了DI模式规则吗?

时间:2018-12-03 09:44:38

标签: c# dependency-injection .net-core asp.net-core-mvc

我对DI很陌生,所以我的知识不是最深刻的。我目前正在研究Asp.Net Core MVC应用程序。

假设我有一个返回DataSet的DLL(我完全控制了该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内部的ConfigureServices(IServiceCollection services)方法中,执行以下操作:

services.AddScoped<IFoo, Foo>();

所以我的问题是,通过在new Foo(f)内执行Select()是否打破了DI模式?如果是,请问如何解决这个问题。

2 个答案:

答案 0 :(得分:7)

我会通过使用存储库数据传输对象(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; }
    }

一些内部实现,对于界面用户而言是隐藏的:

    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;

答案 1 :(得分:3)

是的,您违反了DI的精神,但可能并非出于您的考虑。

其中一条评论指出,您可能会违反Foo的SRP。您曾经说过,它“仅包含两个属性”,但是它包含逻辑并接受并存储DataStore引用。如果这两个属性确实是Foo包含的全部,则本质上将是DTO ...愚蠢的属性,没有行为或业务规则,在 ,我想说的是,您本质上不会通过仅仅调用new Foo(properties)来侵犯DI的精神,因为匿名对象或Tuple可以达到相同的目的……实际上将Foo简化为内容。 DI并不是关于“从不调用new”,而是关于逻辑代码不解析其自身的依存关系来执行其逻辑。

由于在您的示例中,您还赋予了Foo询问DataSource以查找具有这些属性的记录的责任,因此您 do 在Foo内部拥有了数据检索逻辑,您肯定会进入一些灰色区域。如前所述,您的班级现在有两个不同且不交叉的职责。您开始接近DDD /富域概念,其中Foo将负责传输其自己的数据 并处理有关处理该数据的业务问题。但是DDD is not the most DI-friendly of architectures通常被认为比小域更适合大型复杂域。

DI的整体思想是,代码(类)单元不应解析其自身的依赖性,以帮助管理/组织/降低复杂性。如果您将GetFooData()上的Foo方法分解为DI提供的 的其他某个类,并将Foo简化为一个简单的DTO /属性包,那么我想您会至少有一个参数,您没有违反DI的精神,因为Foo并不是将DataRow转换为Foo的代码的行为依赖。 >