基于Microsoft Walkthrough on test-driven development with entity framework 4.0我在MVC4应用程序中创建了存储库模式,以便能够进行单元测试。
我创建了一个IContext
界面,如下所示:
public interface IContext : IDisposable
{
// This interface represents all the important operations on our ObjectContext
// (which is in Context.cs). Now our tests can work against
// the interface rather than against the concrete object type, which will make
// it easier for us to substitute a fake Context. Substituting a fake means
// we can remove the need to interact with the database, which will speed up the
// test and eliminate the hassle of needing to reset the database to its initial
// state after each test run.
IDbSet<Product> Products { get; }
IDbSet<Shop> Shops { get; }
IDbSet<User> Users { get; }
IDbSet<Order> Orders { get; }
IDbSet<Cart> Carts { get; }
IDbSet<Category> Categories { get; }
IDbSet<Transaction> Transactions { get; }
int SaveChanges();
}
除此之外,“真实”上下文继承自此IContext,如下所示:
public DbSet<Product> Products { get; set; }
public DbSet<Shop> Shops { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Cart> Carts { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Transaction> Transactions { get; set; }
IDbSet<Product> ICorveeContext.Products
{
get { return this.Products; }
}
IDbSet<Shop> ICorveeContext.Shops
{
get { return this.Shops; }
}
IDbSet<User> ICorveeContext.Users
{
get { return this.Users; }
}
IDbSet<Order> ICorveeContext.Orders
{
get { return this.Orders; }
}
IDbSet<Cart> ICorveeContext.Carts
{
get { return this.Carts; }
}
IDbSet<Category> ICorveeContext.Categories
{
get { return this.Categories; }
}
IDbSet<Transaction> ICorveeContext.Transactions
{
get { return this.Transactions; }
}
然后我有一个测试来测试OrderController是否有效;
[TestMethod]
public void IndexTest()
{
OrderController orderController = new OrderController();
ActionResult result = orderController.Index();
Assert.IsInstanceOfType(result, typeof(ViewResult));
}
此测试失败并提供以下消息:
Test method Project.Tests.Controllers.OrderControllerTest.IndexTest threw exception:
System.InvalidOperationException: Multiple object sets per type are not supported. The object
sets 'Products' and 'Project.Models.IContext.Products' can [sic?] both contain instances of
type 'Project.Models.Product'.
为什么会这样?我根据这个存储库模式做了一切,但它只是不起作用。
答案 0 :(得分:1)
这是因为DbContext会自动初始化所有IDbSet<T>
属性。
这就是为什么你可以public DbSet<Product> Products { get; set; }
而不必担心通过调用this.Products = this.Set<Product>();
现在,您的表(DbSet<Product>
和IDbSet<Product>
)具有重复的属性,因为DbSet<Product>
实现了IDbSet<Product>
,上下文设置初始化程序失败。
我建议将您的存储库公开为IQueryable<T>
而不是IDbSet<T>
由于IQueryable<T>
没有公开Add,Remove和其他很棒的方法,我会创建另一个接口和类来包装你的IDbSet<T>
。
<强>更新强>
我会实现你的界面有点不同。我只会为演示目的实现一个表。
public class DbContextWrapper : IContext, IDisposable
{
private MyDbContext _context;
public DbContextWrapper()
{
this._context = new MyDbContext();
}
IDbSet<Product> IContext.Products
{
get { return this._context.Products; }
}
public void Dispose()
{
this._context.Dispose();
}
}
这样,当DbContext尝试初始化DbSet
时,您就不会发生冲突。
请记住从DbContext类中删除IDbSet
属性。
答案 1 :(得分:1)
您已将IDbSet
属性公开为公开,但也将具体的DbSet
字段公开为公开。将存储字段更改为private
:
private DbSet<Product> Products { get; set; }
private DbSet<Shop> Shops { get; set; }
private DbSet<User> Users { get; set; }
private DbSet<Order> Orders { get; set; }
private DbSet<Cart> Carts { get; set; }
private DbSet<Category> Categories { get; set; }
private DbSet<Transaction> Transactions { get; set; }
并弄清楚如何从课外“设置”数据。构造函数?公共方法?