Entity framework 为什么DbContext不';不能实现IDbContext接口吗?

Entity framework 为什么DbContext不';不能实现IDbContext接口吗?,entity-framework,unit-testing,testing,mocking,moq,Entity Framework,Unit Testing,Testing,Mocking,Moq,为什么实体框架中没有IDbContext接口?如果有一个包含SaveChanges()等方法的现有接口,您可以从中派生自定义数据库上下文接口,那么测试不是更容易吗 public interface ICustomDbContext : IDbContext { // add entity set properties to existing set of methods in IDbContext IDbSet<SomeEntity> SomeEntities { g

为什么实体框架中没有
IDbContext
接口?如果有一个包含SaveChanges()等方法的现有接口,您可以从中派生自定义数据库上下文接口,那么测试不是更容易吗

public interface ICustomDbContext : IDbContext
{
    // add entity set properties to existing set of methods in IDbContext
    IDbSet<SomeEntity> SomeEntities { get; }
}
公共接口ICustomDbContext:IDbContext { //将实体集属性添加到IDbContext中的现有方法集 IDbSet SomeEntities{get;} }
没有IDbContext,因为它是无用的,它的唯一实现是DbContext

namespace Test.Mocks
{  
    public sealed class MockDatabaseContext : MainProject.Persistence.Database.DatabaseContext
    {
        public MockDatabaseContext(ConfigurationWrapper config) : base(config)
        {

        }      
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {

            var dbPath = "test.db";
            optionsBuilder.UseSqlite($"Filename={dbPath}");


        }
    }
}

namespace Test.Mocks
{

    public class MockInventoryFacade : InventoryFacade
    {        
        public MockInventoryFacade(MockDatabaseContext databaseContext) : base(databaseContext)
        {

        }    
    }
}
如果你看这个的话,EF团队在IDbSet上也是这样

对我来说,当涉及到单元测试时,EF的真正问题是DbContext中的DbConnection,幸运的是codeplex上有一个很好的项目开始解决这个问题

Effort是一个强大的工具,它提供了为基于实体框架的应用程序创建自动化测试的便捷方法。 它基本上是一个ADO.NET提供程序,在一个轻量级进程内内存数据库(而不是传统的外部数据库)上执行所有数据操作。它还提供了一些直观的帮助器方法,使该提供程序与现有的ObjectContext或DbContext类一起使用变得非常容易。对现有代码的简单添加可能足以创建无需外部数据库即可运行的数据驱动测试

这样,您就可以保持DbContext和DbSet不变,轻松地进行单元测试。 这样做的唯一缺点是Linq提供程序之间的差异,在Linq提供程序中,一些单元测试可能通过努力而不是真正的后端

使用EF7更新

我仍然认为IDbContext是无用的,问题来自DbConnection

EF7也不会有IDbContext,为了进行单元测试,他们现在提供了一个内存中的提供程序


您可以看到Rowan Miller在这里做演示:

我看到了
IDbContext

然后,使用该接口为实体上下文创建一个新的分部类

public partial class YourModelEntities : DbContext, IDbContext 
编辑: 我编辑了这篇文章,这对我很有用。 我的背景

namespace dao
{
    public interface ContextI : IDisposable
    {
        DbSet<TEntity> Set<TEntity>() where TEntity : class;
        DbSet Set(Type entityType);
        int SaveChanges();
        IEnumerable<DbEntityValidationResult> GetValidationErrors();
        DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity:class;
        DbEntityEntry Entry(object entity);
        string ConnectionString { get; set; }
        bool AutoDetectChangedEnabled { get; set; }
        void ExecuteSqlCommand(string p, params object[] o);
        void ExecuteSqlCommand(string p);
    }
}

我也在考虑这个问题,我假设您将使用它进行模拟
DbContext
。我找不到这样做的理由,除了您需要在模拟类的anyway中手动实现自己的
DbSet
(因此无论如何都需要重写自己的接口)。

只需创建一个模拟DbContext,扩展生产DbContext,覆盖使测试复杂化的方法即可。这样,对生产DbContext的任何更改都会自动反映在测试中,而覆盖的方法除外。对于处理持久性并采用DbContext的任何其他类,只需将它们扩展为传入扩展的模拟DbContext即可

namespace Test.Mocks
{  
    public sealed class MockDatabaseContext : MainProject.Persistence.Database.DatabaseContext
    {
        public MockDatabaseContext(ConfigurationWrapper config) : base(config)
        {

        }      
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {

            var dbPath = "test.db";
            optionsBuilder.UseSqlite($"Filename={dbPath}");


        }
    }
}

namespace Test.Mocks
{

    public class MockInventoryFacade : InventoryFacade
    {        
        public MockInventoryFacade(MockDatabaseContext databaseContext) : base(databaseContext)
        {

        }    
    }
}

你仍然可以创建这样的接口,并在你的派生上下文中实现它,但它确实会。是的,我只是好奇为什么一开始就没有这样的接口,开箱即用。不需要手动创建它。您可以将DbContext包装到存储库中,因此可以模拟它。正如Ladislav所指出的,您必须小心不要在存储库中公开任何可能受linq to entities/linq to影响的内容sql@StevenMortimer我认为,与存储库相比,上下文更接近于工作单元。存储库更接近数据库集。@Bruno,你说得对,但repo实现必须使用上下文。“回购使用/拥有一个工作单元”没有意义。“工作单位使用/拥有一个或多个回购协议”是有道理的。同样,您是对的,一个上下文有一个或多个DbSet。这提醒我们,当围绕上下文构建repo,并围绕已构建的repo构建一个工作单元时:我们正在做一些冗余的事情,将设计模式嵌入到相同的设计模式中。。。我们为什么要这样做?因为缺少原始实现的清晰接口:IDbRepository(这将是工作单元),所以这一点都不是无用的。现在,我们正在使用依赖于DBContext实例的WCF数据服务。如果它依赖于接口,我们将能够访问Set()方法调用,并提供我们迫切需要的过滤层(而查询拦截器是远远不够的),您可以从DbSet继承并重写所需的方法。MS数据服务直接在DbContext或从DbContext继承的内容上工作。有没有办法让Set方法(即使是EF6也不是虚拟的)返回我自己的DbSet?我真正需要做的是过滤掉实体(只返回其中的一部分)。IDbContext肯定不会无用。由于单元测试是实现存储库模式的主要组件之一,如果我们不能注入模拟上下文而不是实际实现的真实上下文,那么单元测试是不可能的。即使我们不能使用存储库模式,这个问题仍然有效。所有DAL层的主要组件都是上下文,并且必须是可模仿的。当然,if可以通过一些技巧和代码生成器等进行模拟,但是模拟它的通用方法总是基于模拟组件所实现的接口。英孚的设计师们完全忽略了这一点,因为今年你应该读到这一点。您将看到没有接口的模拟。