【问题标题】:How can I detect changes IDbSet如何检测更改 IDbSet
【发布时间】:2025-12-09 01:30:01
【问题描述】:

我正在创建一个模拟 IDbSet 以允许对实体框架类进行单元测试等。

但是,我真的很难检测到更改,甚至根本不知道如何去做。到目前为止,这是我的课程...

public interface IReportContext
{
    IDbSet<Report> Reports {get;}
    public int SaveChanges();
}

public class MockReportContext : IReportContext
{
    IDbSet<Report> Reports {get;}

    public int SaveChanges()
    {
        //Need to detect changes here???
    }

    public MockReportContext()
    {
       Reports = new MockDbSet<Report>();
    }
}

public class MockDbSet<T> : IDbSet<T>
{
    readonly ObservableCollection<T> _data;
    readonly IQueryable _query;

    public FakeDbSet()
    {
        _data = new ObservableCollection<T>();
        _query = _data.AsQueryable();
    }

    public FakeDbSet(ObservableCollection<T> data)
    {
        _data = data;
        _query = _data.AsQueryable();
    }

    public virtual T Find(params object[] keyValues)
    {
        throw new NotImplementedException();
    }

    public T Add(T item)
    {
        _data.Add(item);
        return item;
    }

    public T Remove(T item)
    {
        _data.Remove(item);
        return item;
    }

    public T Attach(T item)
    {
        _data.Add(item);
        return item;
    }

    public T Detach(T item)
    {
        _data.Remove(item);
        return item;
    }

    public T Create()
    {
        return Activator.CreateInstance<T>();
    }

    public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
    {
        return Activator.CreateInstance<TDerivedEntity>();
    }

    public ObservableCollection<T> Local
    {
        get { return _data; }
    }

    Type IQueryable.ElementType
    {
        get { return _query.ElementType; }
    }

    System.Linq.Expressions.Expression IQueryable.Expression
    {
        get { return _query.Expression; }
    }

    IQueryProvider IQueryable.Provider
    {
        get { return _query.Provider; }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        return _data.GetEnumerator();
    }
}

这适用于添加、删除和检索实体。但是,当我尝试以下操作时:

IReportContext context = new MockReportContext();
context.Reports.Add(new Report()); //Works
Report report = context.Reports.First(); //Works
report.Message = "Hello World!";
context.SaveChanges(); //Does nothing

MockReportContext 怎么知道它返回的报表对象已经改变了?我知道使用实体框架可以做到这一点,所以它一定是可能的,但我不知道如何......

【问题讨论】:

    标签: c# entity-framework ef-code-first dbset


    【解决方案1】:

    我认为你大部分时间都在那里,但我建议使用像 Moq(我的个人偏好)或 Rhino Mocks 这样的模拟框架来模拟 IReportContext 进行单元测试,而不是自找麻烦创建一个Fake 类,如MockReportContext。 (好吧,学习一个 mocking 框架确实会有些麻烦,但它会节省很多 Fake 类的苦差事。)

    我假设您正在对依赖于IReportContext 的代码进行单元测试,因此您无需在SaveChanges() 内执行任何操作,您只需断言您的代码确实在内部调用了SaveChanges()如果应该的话。

    Here's a good overview 将 Moq 与 Entity Framework 的派生 DbContext / DbSet 类一起使用。

    在上面链接的概述中,如果最后的单元测试正在测试内部调用 SaveChanges() 的方法,它还可以通过以下行验证您的方法确实调用了 SaveChanges()

    dc.Verify(db => db.SaveChanges());
    

    您也可以通过在 SaveChanges() 类中将 side 属性设置为 True 并在单元测试结束时检查它来对您的 MockReportContext 类执行此操作,但是模拟框架要灵活得多,并且可以节省需要为单元测试编写额外的类。

    如果您想避免使用模拟框架,那么这里是how to do it with fakes

    【讨论】: