我可以使用 EF Core 5 InMemory 数据库进行集成测试吗

Can I Use EF Core 5 InMemory Database For Integration Testing

我正在努力从 net core 3.1 迁移到 net 5(EF Core 3 到 EF Core 5)。我们使用 EF Core 3 InMemory 数据库进行集成测试。迁移测试不再通过且查询抛出错误后:

System.InvalidOperationException: The LINQ expression 'ShapedQueryExpression: QueryExpression: InMemoryQueryExpression: ServerQueryExpression: InMemoryTableExpression: Entity: RatingExclusionProduct .Where(valueBuffer => IsFalse(ValueBufferTryReadValue(valueBuffer, 0, Property: RatingExclusion.Id (UUId) Required PK AfterSave:Throw).Equals(null)) && object.Equals( objA: ExpressionExtensions.ValueBufferTryReadValue( valueBuffer: valueBuffer, index: 0, property: Property: RatingExclusion.Id (UUId) Required PK AfterSave:Throw), objB: ExpressionExtensions.ValueBufferTryReadValue( valueBuffer: valueBuffer, index: 0, property: Property: RatingExclusionProduct.ExclusionId (UUId) Required PK FK AfterSave:Throw))) ProjectionMapping: Member: EmptyProjectionMember Projection: EntityProjectionExpression: Property: RatingExclusionProduct.ExclusionId (UUId) Required PK FK AfterSave:Throw -> ExpressionExtensions.ValueBufferTryReadValue( valueBuffer: valueBuffer, index: 0, property: Property: RatingExclusionProduct.ExclusionId (UUId) Required PK FK AfterSave:Throw) Property: RatingExclusionProduct.ProductId (UUId) Required PK FK Index AfterSave:Throw -> ExpressionExtensions.ValueBufferTryReadValue( valueBuffer: valueBuffer, index: 1, property: Property: RatingExclusionProduct.ProductId (UUId) Required PK FK Index AfterSave:Throw) ,

ShaperExpression: EntityShaperExpression: 
        EntityType: RatingExclusionProduct
        ValueBufferExpression: 
            ProjectionBindingExpression: EmptyProjectionMember
        IsNullable: False

.AsQueryable()
.LeftJoin(
    inner: DbSet<ControlProduct>(), 
    outerKeySelector: o0 => EF.Property<UUId>(o0, "ProductId"), 
    innerKeySelector: c => EF.Property<UUId>(c, "Id"), 
    resultSelector: (o, i) => new TransparentIdentifier<RatingExclusionProduct, ControlProduct>(
        Outer = o, 
        Inner = i
    ))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly

by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'

我们能否以某种方式解决这个问题,或者我们需要切换到另一种模拟数据库的方法?据我所知,有重大变化:

https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/breaking-changes#no-client-methods

我的问题与它有关吗?我在网上搜索过,有些人建议使用真实数据库进行测试,但我担心这会是一个非常慢的解决方案,而且我们有多个上下文使用单个数据库,因此很难在测试开始时创建数据库。有些人建议创建上下文聚合(仅包含用于数据库创建目的的所有实体的单个上下文),但我不知道如何实现它。我认为我的问题看起来更像是结果问题的组合,但我对这个话题有点迷茫。非常感谢您的关注。

更新: UUId 是自定义类型,用作我们所有表的主键。 为了解决它,我们使用:

    services
        .AddDbContext<RatingsContext>(opt =>
            opt.AddRelationalTypeMappingSourcePlugin<UUIdTypeMapperPlugin>()
                .UseMySql(connectionString, mso => mso
                    .ServerVersion(new Version(5, 7, 29), ServerType.MySql)
                    .EnableRetryOnFailure()
                )
        );

其中 UUIdTypeMapperPlugin:

public class UUIdTypeMapperPlugin : ITypeMappingSourcePlugin, IRelationalTypeMappingSourcePlugin
{
    public CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo)
    {
        return mappingInfo.ClrType == typeof(UUId) ? new UUIdTypeMapper() : null;
    }

    public RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo)
    {
        return mappingInfo.ClrType == typeof(UUId) ? new UUIdTypeMapper() : null;
    }
}

和 UUIdTypeMapper:

public class UUIdTypeMapper : RelationalTypeMapping
{
    private static readonly ValueConverter<UUId, byte[]> _converter
        = new ValueConverter<UUId, byte[]>(uuid => uuid.ToByteArray(),
            byteArray => new UUId(byteArray));

    protected UUIdTypeMapper(RelationalTypeMappingParameters parameters) : base(parameters)
    {
    }

    public UUIdTypeMapper() : base(new RelationalTypeMappingParameters(
        new CoreTypeMappingParameters(typeof(UUId), _converter), "binary(16)")
    )
    {
    }

    protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
    {
        return new UUIdTypeMapper(parameters);
    }

    public override string GenerateSqlLiteral(object value)
    {
        return $"0x{value}";
    }
}

经过广泛研究后,我发现 EF Core GitHub 上有一个与我的问题相关的未决问题,它是一个错误。

https://github.com/dotnet/efcore/issues/24318

我认为我们的团队应该转向在真实数据库上进行集成测试。有一个有用的link: https://medium.com/nerd-for-tech/run-ef-core-integration-tests-in-a-repeatable-and-isolated-way-using-a-public-docker-image-with-a-e912a89c7bf4