我即将开始一个新项目,它将使用实体框架(EF)作为 ORM 与 SQL Server 链接。我一直在进行大量阅读以了解在抽象方面使用 EF 并最终使我的代码可测试的好方法。
问题是我读得越多,我就越对这一切感到困惑。
我认为是一个好方法:
我打算沿着存储库路线走,使它们可以通过接口轻松注入并且易于模拟以进行测试,从我似乎找到的许多示例中,有很多人发誓这种方法。对于与 EF 交互的存储库部分,我只想进行集成测试,因为我的存储库中没有业务逻辑,我会将其转移到服务/控制器类中来处理它。
有人说存储库模式是一种毫无根据的抽象
http://ayende.com/blog/3955/repository-is-the-new-singleton
还有其他链接,但我不想用太多链接来解决这个问题
我有点明白这一点,我知道 EF 及其实现本质上是一种抽象,但对我来说它似乎是一个具体的(它触及应用程序范围之外的东西......数据库)对我来说问题是它为我(至少在初始架构中)推动了测试问题,我可能会在我的服务中与数据上下文和工作单元进行交互,但这感觉就像我将数据访问登录与业务逻辑混合在一起水平,然后我该怎么做单元测试呢?
我似乎读了很多书,以至于我现在没有明确的方向,并且不可避免的编码器块已经设置。我正在尝试寻找一种干净的方法,并且我可以进行测试。
更新 - 我要开始的解决方案
我走了一条与 simon 给出的答案略有不同的路径,但多亏了我读过的一篇文章,但他再次提交了,所以我又重新看了一遍。首先,我使用 Code First,因为我有一个现有的数据库,并且由于某种原因不喜欢设计器工具。我创建了一些简单的 POCO 对象和一些映射,为了简单起见,我将只展示一个 POCO/Mapping 和其他的。
POCO
public abstract class BaseEntity<T>
{
[Key]
public T Id { get; set; }
public string Name { get; set; }
public DateTime CreatedOn { get; set; }
}
public class Project : BaseEntity<int>
{
public virtual ICollection<Site> Sites { get; set; }
public bool Active { get; set; }
}
工作单元接口
public interface IUnitOfWork
{
IDbSet<Project> Projects { get; }
void Commit();
}
语境
internal class MyContext : DbContext
{
public MyContext(string connectionString)
: base(connectionString)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ProjectMap());
}
}
具体的工作单元
public class UnitOfWork : IUnitOfWork
{
readonly MyContext _context;
const string ConnectionStringName = "DBConn";
public UnitOfWork()
{
var connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
_context = new FosilContext(connectionString);
}
public IDbSet<Project> Projects
{
get { return _context.Set<Project>(); }
}
public void Commit()
{
_context.SaveChanges();
}
}
基本测试代码
var _repo = new UnitOfWork().Projects;
var projects = from p in _repo
select p;
foreach (var project in projects)
{
Console.WriteLine(string.Format("{0} - {1} - {2} - {3})", project.Id, project.Name, project.Active.ToString(), project.CreatedOn.ToShortDateString()));
}
Console.Read();
UnitOfWork 是不言自明的,但基于具体的上下文,我可以创建一个假的 IUnitOfWork 并将其传递给一个假的 IDBSet 以进行测试。对我来说,这可能会在架构上对我产生影响,但我越深入,但在这个抽象的 EF 之上没有回购(我认为)。正如文章所解释的,IDbSet 等同于 Repo,所以这就是我正在使用的。我有点把我的自定义上下文隐藏在 UoW 后面,但我会看看结果如何。
我现在想的是我可以在服务层中使用它,它将封装对数据和业务规则的检索,但应该是可单元测试的,因为我可以伪造 EF 特定项目。希望这将使我的控制器非常精简,但我们会看到:)