如何使用内存数据库提供程序中的Entity Framework Core测试数据库视图?

img*_*gen 9 c# entity-framework-core

当我尝试向视图中添加对象时,它抛出异常unable to track an instance of type because it is a query type。有办法解决这个问题吗?

Iva*_*oev 6

根据定义,查询类型只读的(对于所有数据库提供程序,不仅限于内存中的查询类型):

  • 永远不会跟踪DbContext上的更改,因此永远不会在数据库上插入,更新或删除。

但是,除了它们通常的使用场景之外,

  • 映射到数据库视图。
  • 映射到未定义主键的表。

他们允许

  • 映射到模型中定义的查询。

或者换句话说

  • 可以映射到定义查询 -定义查询是模型中声明的辅助查询,该查询充当查询类型的数据源。

这是通过ToQuery fluent API 实现的:

配置用于提供查询类型数据的查询。

因此,对于在内存数据库中测试查询类型,您应该利用定义的查询映射功能。

例如,在OnModelCreating覆盖中,您可以添加以下内容:

if (Database.IsInMemory())
{
   // In memory test query type mappings
    modelBuilder.Query<MyQueryType>().ToQuery(() => LINQ_query);
    // ... similar for other query types
}
else
{
    // Database query type mappings
    modelBuilder.Query<MyQueryType>().ToView("MyQueryTypeView");
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

其中LINQ_query在正常LINQ查询访问上下文DbSetS和DbQueryS和伸出MyQueryType

然后,测试将为涉及的实体提供数据,并且使用DbQuerys进行的查询将从定义的查询中检索数据。


上面的方法应该是在内存数据库中测试视图的推荐方法。

仅出于完整性考虑,可以DbQuery通过创建某种查询存储库直接向s提供数据(基本上是模拟它们),但是有以下限制-必须共享static),因为当前EF Core无法正确处理db上下文成员。 (就像全局查询过滤器一样)不是DbSet<T>DbQuery<T>

像这样:

public static class FakeQueryProvider
{
    static Dictionary<Type, IQueryable> queries = new Dictionary<Type, IQueryable>();

    public static void SetQuery<T>(IQueryable<T> query)
    {
        lock (queries)
            queries[typeof(T)] = query;
    }

    public static IQueryable<T> GetQuery<T>()
    {
        lock (queries)
            return queries.TryGetValue(typeof(T), out var query) ? (IQueryable<T>)query : Enumerable.Empty<T>().AsQueryable();
    }

    public static QueryTypeBuilder<T> ToFakeQuery<T>(this QueryTypeBuilder<T> builder)
        where T : class
    { 
        return builder.ToQuery(() => GetQuery<T>());
    }
}
Run Code Online (Sandbox Code Playgroud)

然后代替

.ToQuery(() => LINQ_query);
Run Code Online (Sandbox Code Playgroud)

你会用

.ToFakeQuery(); 
Run Code Online (Sandbox Code Playgroud)

并像这样将其喂入测试

 List<MyQueryType> data = ...;
 FakeQueryProvider.SetQuery(data.AsQueryable());
Run Code Online (Sandbox Code Playgroud)

由于共享存储限制了MyQueryType并行运行相关测试的能力,因此我仍然建议第一种方法。