为啥 DbContext 不实现 IDbContext 接口?
Posted
技术标签:
【中文标题】为啥 DbContext 不实现 IDbContext 接口?【英文标题】:Why DbContext doesn't implement IDbContext interface?为什么 DbContext 不实现 IDbContext 接口? 【发布时间】:2012-08-29 02:45:29 【问题描述】:为什么Entity Framework中没有IDbContext
接口?如果有一个现有接口,其中包含 SaveChanges() 等方法,您可以从中派生您的自定义数据库上下文接口,那么测试事情会不会更容易?
public interface ICustomDbContext : IDbContext
// add entity set properties to existing set of methods in IDbContext
IDbSet<SomeEntity> SomeEntities get;
【问题讨论】:
你仍然可以创建这样的接口并在你的派生上下文中实现它,但它真的会not be very helpful for unit testing。 是的,我只是好奇为什么一开始就没有这样的接口,开箱即用。不需要手动创建它。 您可以将 DbContext 包装在存储库中,因此可以模拟它。再次正如 Ladislav 指出的那样,您必须小心不要在存储库上公开任何可能受 linq to entity / linq to sql 影响的内容 @StevenMortimer 我相信上下文比存储库更接近工作单元。存储库更靠近 DbSet。 @Bruno,你是对的,但回购实现必须使用上下文。 “回购使用/拥有一个工作单元”没有意义。有意义的“工作单元使用/拥有一个或多个回购”。再一次,你是对的,一个上下文有一个或多个 DbSet。这警告我们,当围绕上下文构建 repo 并围绕已构建的 repos 构建工作单元时:我们正在做一些多余的事情,将设计模式嵌入到相同的设计模式中......为什么我们这样做?因为缺少原始实现的清晰接口:缺少 IDbRepository(那将是工作单元) 【参考方案1】:我看到了这个IDbContext
:
See this link 然后你用那个接口为你的实体上下文创建一个新的部分类。
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);
YourModelEntities 是你自动生成的分部类,你需要创建一个新的同名分部类,然后添加你的新上下文接口,例如 ContextI
注意:接口并未实现所有方法,因为这些方法是在您的自动生成代码中实现的。
namespace dao
public partial class YourModelEntities :DbContext, ContextI
public string ConnectionString
get
return this.Database.Connection.ConnectionString;
set
this.Database.Connection.ConnectionString = value;
bool AutoDetectChangedEnabled
get
return true;
set
throw new NotImplementedException();
public void ExecuteSqlCommand(string p,params object[] os)
this.Database.ExecuteSqlCommand(p, os);
public void ExecuteSqlCommand(string p)
this.Database.ExecuteSqlCommand(p);
bool ContextI.AutoDetectChangedEnabled
get
return this.Configuration.AutoDetectChangesEnabled;
set
this.Configuration.AutoDetectChangesEnabled = value;
【讨论】:
【参考方案2】:只需创建一个模拟 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)
【讨论】:
【参考方案3】:我也在考虑这个问题,我假设你会用它来嘲笑DbContext
。我找不到这样做的理由,除了你需要在你的模拟类中手动实现你自己的DbSet
(所以无论如何都需要重写你自己的接口)。
【讨论】:
【参考方案4】:没有 IDbContext 因为它没有用,唯一的实现就是 DbContext。
如果您查看此design meeting note,EF 团队也正在使用 IDbSet 进行此操作
对我来说,EF 在单元测试方面的真正问题是 DbContext 中的 DbConnection,幸运的是在 codeplex 上有一个很好的项目 Effort 开始填补这个问题。
Effort 是一个强大的工具,它可以方便地为基于实体框架的应用程序创建自动化测试。 它基本上是一个 ADO.NET 提供程序,它在轻量级进程内主内存数据库而不是传统的外部数据库上执行所有数据操作。它还提供了一些直观的帮助方法,可以很容易地将此提供程序与现有的 ObjectContext 或 DbContext 类一起使用。对现有代码的简单添加可能足以创建可以在没有外部数据库存在的情况下运行的数据驱动测试。
有了这个,您可以让您的 DbContext 和 DbSet 保持原样并轻松地进行单元测试。 唯一的缺点是 Linq 提供者之间的差异,其中一些单元测试可能会通过努力而不是真正的后端。
使用 EF7 更新
我仍然认为 IDbContext 没有用,问题来自 DbConnection。
EF7 也没有 IDbContext,为了进行单元测试,他们现在提供了一个内存提供程序。
您可以在此处看到 Rowan Miller 进行演示:Modern Data Applications with Entity Framework 7
【讨论】:
这根本没有用。现在我们正在使用依赖于 DBContext 实例的 WCF 数据服务。如果它依赖于接口,我们将能够访问 Set以上是关于为啥 DbContext 不实现 IDbContext 接口?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我们在 DbContext 中使用 DependecyInjection 而不是 OnConfiguring 方法?