我对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模式?如果是,请问如何解决这个问题。
答案 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的代码的行为依赖。 >