简体   繁体   English

使用 xUnit 动态测试所有 Entity Framework Core DbContext DbSet<> 对象

[英]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.

相关问题 如何在 Entity Framework Core 中使用复数 DbSet 属性名称搭建 DbContext? - How to scaffold DbContext with plural DbSet property names in Entity Framework Core? 实体框架中DBContext,DBSet &lt;&gt;的引用 - References for DBContext, DBSet<> in Entity Framework 实体框架核心:如何从派生类型动态获取DbSet? - Entity Framework Core: How to dynamically get the DbSet from a derived Type? DbContext - > DbSet - >缺少Where子句(Entity Framework 6) - DbContext -> DbSet -> Where clause is missing (Entity Framework 6) Entity Framework Core 中的动态 DbSet - Dynamic DbSet in Entity Framework Core 实体框架核心过滤器DbSet - Entity Framework Core Filter DbSet 使用 Entity Framework Core 在 xUnit 测试中验证 [Required] 属性失败 - Validation of [Required] attribute failing in xUnit test with Entity Framework Core 动态加载 DbSet 到 DbContext - Dynamically loading DbSet to DbContext 当我有一个实体时,动态地在 DbContext 中查找指定的通用 DbSet - Find a specified generic DbSet in a DbContext dynamically when I have an entity Entity Framework 6 和 DBContext 或 DBQuery 或 DBSet 或 ObjectQuery 与标量变量 - Entity Framework 6 and DBContext or DBQuery or DBSet or ObjectQuery with scalar variable
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM