Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/286.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/silverlight/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何调用Moq实体框架SqlQuery_C#_Entity Framework_Moq_Dbcontext - Fatal编程技术网

C# 如何调用Moq实体框架SqlQuery

C# 如何调用Moq实体框架SqlQuery,c#,entity-framework,moq,dbcontext,C#,Entity Framework,Moq,Dbcontext,我已经能够使用Moq从实体框架模拟DbSet 但是,我现在想知道如何模拟对SqlQuery的调用。不确定这是否可能或如何实现,因为它依赖于模拟数据库上下文,知道调用了什么“查询” 下面是我试图嘲弄的 var myObjects = DbContext.Database .SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", "some_value") .ToList(); 属性和方法没有标记为virtual,因此它们(使用M

我已经能够使用Moq从实体框架模拟
DbSet

但是,我现在想知道如何模拟对SqlQuery的调用。不确定这是否可能或如何实现,因为它依赖于模拟数据库上下文,知道调用了什么“查询”

下面是我试图嘲弄的

var myObjects = DbContext.Database
    .SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", "some_value")
    .ToList();
属性和方法没有标记为
virtual
,因此它们(使用Moq;您可以使用一个可以解释这一点的方法,但这可能比您希望的更惯性)

您需要使用某种抽象来解决这个问题,例如通过将数据库的整个查询包装在一个helper类中:

public interface IQueryHelper
{
    IList<MyObject> DoYourQuery(string value);
}

public class QueryHelper : IQueryHelper
{
    readonly MyDbContext myDbContext;

    public QueryHelper(MyDbContext myDbContext)
    {
        this.myDbContext = myDbContext;
    }

    public IList<MyObject> DoYourQuery(string value)
    {
        return myDbContext.Database.SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", value).ToList();
    }
}
然后将
IQueryHelper
注入正在测试的类的构造函数中,并对其进行模拟

您将在
DoYourQuery
上丢失测试覆盖率,但现在查询是。

未标记为虚拟,而是标记为虚拟

根据文件

上下文永远不会跟踪此查询的结果,即使 返回的对象类型是实体类型。使用该方法返回由 上下文

和文件

默认情况下,返回的实体由上下文跟踪;这个可以 可以通过调用返回的DbRawSqlQuery上的AsNoTracking进行更改

然后
Database.SqlQuery(String,Object[])
应该与
Set.SqlQuery(String,Object[]).AsNoTracking()等效(仅当
T
是EF实体而不是DTO/VM时)

因此,如果您可以将实现替换为:

var myObjects = DbContext
    .Set<MyObject>()
    .SqlQuery("exec [dbo].[my_sproc] {0}", "some_value")
    .AsNoTracking()
    .ToList();
var myObjects=DbContext
.Set()
.SqlQuery(“exec[dbo].[my_存储过程]{0}”,“some_值”)
.AsNoTracking()
.ToList();
你可以模仿它如下

var list = new[] 
{ 
    new MyObject { Property = "some_value" },
    new MyObject { Property = "some_value" },
    new MyObject { Property = "another_value" }
};

var setMock = new Mock<DbSet<MyObject>>();
setMock.Setup(m => m.SqlQuery(It.IsAny<string>(), It.IsAny<object[]>()))
    .Returns<string, object[]>((sql, param) => 
    {
        // Filters by property.
        var filteredList = param.Length == 1 
            ? list.Where(x => x.Property == param[0] as string) 
            : list;
        var sqlQueryMock = new Mock<DbSqlQuery<MyObject>>();
        sqlQueryMock.Setup(m => m.AsNoTracking())
            .Returns(sqlQueryMock.Object);
        sqlQueryMock.Setup(m => m.GetEnumerator())
            .Returns(filteredList.GetEnumerator());
        return sqlQueryMock.Object;
    });

var contextMock = new Mock<MyDbContext>();
contextMock.Setup(m => m.Set<MyObject>()).Returns(setMock.Object);
var list=new[]
{ 
新的MyObject{Property=“some_value”},
新的MyObject{Property=“some_value”},
新建MyObject{Property=“另一个值”}
};
var setMock=new Mock();
setMock.Setup(m=>m.SqlQuery(It.IsAny(),It.IsAny())
.Returns((sql,param)=>
{
//按属性筛选。
变量filteredList=param.Length==1
?list.Where(x=>x.Property==param[0]作为字符串)
:列表;
var sqlQueryMock=new Mock();
Setup(m=>m.AsNoTracking())
.Returns(sqlQueryMock.Object);
Setup(m=>m.GetEnumerator())
.Returns(filteredList.GetEnumerator());
返回sqlQueryMock.Object;
});
var contextMock=new Mock();
Setup(m=>m.Set()).Returns(setMock.Object);

您可以将虚拟方法添加到数据库上下文中,以便在单元测试中重写:

public partial class MyDatabaseContext : DbContext
{
    /// <summary>
    /// Allows you to override queries that use the Database property
    /// </summary>
    public virtual List<T> SqlQueryVirtual<T>(string query)
    {
        return this.Database.SqlQuery<T>(query).ToList();
    }
}
公共部分类MyDatabaseContext:DbContext { /// ///允许您覆盖使用数据库属性的查询 /// 公共虚拟列表SqlQueryVirtual(字符串查询) { 返回this.Database.SqlQuery(query.ToList(); } }
如果有人遇到这个问题。我用几种方法解决了这个问题。这只是解决这个问题的另一种方法

  • 我的上下文是通过一个接口抽象出来的。我只需要以下几种方法:

    public interface IDatabaseContext
    {
        DbSet<T> Set<T>() where T : class;
        DbEntityEntry<T> Entry<T>(T entity) where T : class;
        int SaveChanges();
        Task<int> SaveChangesAsync();
        void AddOrUpdateEntity<TEntity>(params TEntity[] entities) where TEntity : class;
    
    公共接口IDatabaseContext
    {
    DbSet Set(),其中T:class;
    DbEntityEntry条目(T实体),其中T:类;
    int SaveChanges();
    任务saveChangesSync();
    void AddOrUpdateEntity(参数tenty[]实体),其中tenty:class;
    
    }

  • 我所有的数据库访问都是通过异步方法进行的。这就带来了一系列全新的问题。幸运的是,您得到的异常与IDbAsyncEnumerable缺少的mock有关。使用提供的解决方案-我只是将其扩展了一点,以便有一个助手返回模拟所有预期属性的Mock>对象

    public static Mock<DbSqlQuery<TEntity>> CreateDbSqlQuery<TEntity>(IList<TEntity> data)
        where TEntity : class, new()
    {
        var source = data.AsQueryable();
        var mock = new Mock<DbSqlQuery<TEntity>>() {CallBase = true};
        mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression);
        mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType);
        mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
        mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider));
        mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
        mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity());
        mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; });
        mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; });
        return mock;
    }
    
    publicstaticmock-CreateDbSqlQuery(IList数据)
    其中tenty:class,new()
    {
    var source=data.AsQueryable();
    var mock=new mock(){CallBase=true};
    mock.As().Setup(m=>m.Expression).Returns(source.Expression);
    mock.As().Setup(m=>m.ElementType).Returns(source.ElementType);
    mock.As().Setup(m=>m.GetEnumerator()).Returns(source.GetEnumerator());
    mock.As().Setup(m=>m.Provider).Returns(新的TestDbAsyncQueryProvider(source.Provider));
    mock.As().Setup(m=>m.GetAsyncEnumerator()).Returns(新的TestDbAsyncEnumerator(data.GetEnumerator());
    mock.As().Setup(m=>m.Create()).Returns(newtenty());
    mock.As().Setup(m=>m.Add(It.IsAny())).Returns(i=>{data.Add(i);return i;});
    mock.As().Setup(m=>m.Remove(It.IsAny())).Returns(i=>{data.Remove(i);return i;});
    返回模拟;
    }
    
  • 最后,使用@Yulium Chandra提供的解决方案,我用模拟上下文测试原始SQL如下:

        public Mock<DbSet<TestModel>> MockDbSet { get; }
    
        ....
    
        MockDbSet.Setup(x => x.SqlQuery(It.IsAny<string>))
              .Returns<string,object[]>
              ((sql, param) => 
              {
                    var sqlQueryMock = MockHelper.CreateDbSqlQuery(Models);
    
                    sqlQueryMock.Setup(x => x.AsNoTracking())
                      .Returns(sqlQueryMock.Object);
    
                    return sqlQueryMock.Object;
                });
    
    public Mock MockDbSet{get;}
    ....
    MockDbSet.Setup(x=>x.SqlQuery(It.IsAny))
    .返回
    ((sql,param)=>
    {
    var sqlQueryMock=MockHelper.CreateDbSqlQuery(模型);
    sqlQueryMock.Setup(x=>x.AsNoTracking())
    .Returns(sqlQueryMock.Object);
    返回sqlQueryMock.Object;
    });
    

  • 这对我很有效。对我来说,这比将查询逻辑抽象为助手更好,就像上面接受的答案一样。
    public static Mock<DbSqlQuery<TEntity>> CreateDbSqlQuery<TEntity>(IList<TEntity> data)
        where TEntity : class, new()
    {
        var source = data.AsQueryable();
        var mock = new Mock<DbSqlQuery<TEntity>>() {CallBase = true};
        mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression);
        mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType);
        mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
        mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider));
        mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
        mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity());
        mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; });
        mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; });
        return mock;
    }
    
        public Mock<DbSet<TestModel>> MockDbSet { get; }
    
        ....
    
        MockDbSet.Setup(x => x.SqlQuery(It.IsAny<string>))
              .Returns<string,object[]>
              ((sql, param) => 
              {
                    var sqlQueryMock = MockHelper.CreateDbSqlQuery(Models);
    
                    sqlQueryMock.Setup(x => x.AsNoTracking())
                      .Returns(sqlQueryMock.Object);
    
                    return sqlQueryMock.Object;
                });