[英]Dynamically Test All Entity Framework Core DbContext DbSet<> objects with xUnit
I'm using xUnit and FluentAssertions to write an integration test to validate that our models are correctly mapped.我正在使用 xUnit 和 FluentAssertions 编写集成测试来验证我们的模型是否正确映射。 We have dozens of EF contexts and for each context, there are one or more
DbSet<>
properties like this:我们有几十个 EF 上下文,对于每个上下文,都有一个或多个
DbSet<>
属性,如下所示:
public class SomeContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder builder) { // ... }
public virtual DbSet<User> Users { get; set; }
}
They are all laid out the same way so this would be an ideal candidate for using [MemberData]
to get each context dynamically as input to the test and the DbSet<>
property, invoke it, and just make sure it doesn't fail doing a simple query.它们都以相同的方式布置,因此这将是使用
[MemberData]
动态获取每个上下文作为测试和DbSet<>
属性的输入,调用它,并确保它不会失败的理想选择一个简单的查询。 The Container.GetInstance()
call is a call to my DI container to get the actual DbContext
object: Container.GetInstance()
调用是对我的 DI 容器的调用,以获取实际的DbContext
对象:
public class ContextTests
{
public static IEnumerable<object[]> EntitiesMappedInCode =>
typeof(DbContextInitializer).Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(DbContext)) && !t.IsAbstract)
.SelectMany(dbContextType =>
{
var context = (DbContext) Container.GetInstance(dbContextType);
var entities = context.Model.GetEntityTypes();
var dbSetProperties = context.GetType().GetProperties().Where(p =>
typeof(DbSet<>).IsAssignableFrom(p.PropertyType.GetGenericTypeDefinition()));
return dbSetProperties.Select(dbSet => new [] {context, dbSet.GetValue(context)});
});
[Theory]
[MemberData(nameof(EntitiesMappedInCode))]
public void EntitiesDefinedInCode_ExistsInDatabase(DbContext context, object dbSetObject)
{
var dbSet = dbSetObject as DbSet<dynamic>;
dbSet.Invoking(dbSet => Queryable.FirstOrDefault<dynamic>(dbSet))
.Should().NotThrow<SqlException>("the entity framework model should match the database");
}
}
The problem is the reflection does not work properly to give back the runtime instance and fails at p.PropertyType.GetGenericTypeDefinition()
with an error.问题是反射无法正常工作以返回运行时实例,并且在
p.PropertyType.GetGenericTypeDefinition()
处失败并出现错误。
Has anyone dynamically retrieved the DbSet<>
properties of contexts and successfully invoked queries on them?有没有人动态检索上下文的
DbSet<>
属性并成功调用它们的查询?
Exception is thrown because GetGenericTypeDefinition()
does not work on non-generic types, so you should first check if type is actually generic before invoking it:抛出异常是因为
GetGenericTypeDefinition()
不适用于非泛型类型,因此您应该在调用它之前首先检查类型是否实际上是泛型的:
var dbSetProps = typeof(HwContext).GetProperties().Where(c =>
c.PropertyType.IsGenericType &&
typeof(DbSet<>).IsAssignableFrom(c.PropertyType.GetGenericTypeDefinition()));
Also do not cast to DbSet<dynamic>
, just use dynamic
:也不要
DbSet<dynamic>
为DbSet<dynamic>
,只需使用dynamic
:
foreach (var prop in dbSetProps)
{
dynamic dbSet = prop.GetValue(context);
// this should not throw
Queryable.FirstOrDefault(dbSet);
}
Thanks to Evk I was able to get it working like this:感谢 Evk,我能够让它像这样工作:
public class ContextTests
{
public static IEnumerable<object[]> EntitiesMappedInCode =>
typeof(DbContextInitializer).Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(DbContext)) && !t.IsAbstract)
.SelectMany(dbContextType =>
{
var dbSetProps = dbContextType.GetProperties()
.Where(c => c.PropertyType.IsGenericType
&& typeof(DbSet<>).IsAssignableFrom(c.PropertyType.GetGenericTypeDefinition()));
var context = Container.GetInstance(dbContextType);
return dbSetProps.Select(dbSetProp => new [] {context, dbSetProp.GetValue(context)});
});
[Theory]
[MemberData(nameof(EntitiesMappedInCode))]
public void EntitiesDefinedInCode_ExistsInDatabase(DbContext context, dynamic dbSet)
{
context.Invoking(dbContext => Queryable.FirstOrDefault(dbSet))
.Should().NotThrow<SqlException>("the entity framework model should match the database");
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.