不允许每种类型的多个对象

时间:2013-12-04 13:38:57

标签: c# entity-framework unit-testing asp.net-mvc-4 visual-studio-2012

基于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'.

为什么会这样?我根据这个存储库模式做了一切,但它只是不起作用。

2 个答案:

答案 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; }

并弄清楚如何从课外“设置”数据。构造函数?公共方法?