You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在EF Core内存SQLite中测试一对多关系并初始化种子数据?

内存SQLite + EF Core MVC单元测试:一对多关系种子数据初始化与内存测试限制

一、一对多关系的种子数据初始化方法

针对ResourceRequest(主实体)和ResourceTags(从实体,一对多关联)的场景,初始化测试种子数据有两种实用方式,同时需要修正你现有测试代码中的关键问题:

方式1:从主实体关联子实体(贴合一对多语义)

先构建ResourceRequest实例,直接给它的ResourceTags集合添加标签,再将主实体存入上下文——EF会自动处理外键关联,无需手动设置ResourceId:

[TestMethod]
public async Task AddResourceTags_SavesCorrectly()
{
    using (var factory = new PrismContextFactory())
    {
        // 准备测试数据
        var resourceId = Guid.NewGuid();
        var tagText = "tagtext";

        var resourceRequest = new ResourceRequest
        {
            ID = resourceId.ToString(),
            SubscriptionID = "",
            TenantID = "",
            Name = "",
            ResourceGroup = "",
            ResourceTags = new List<ResourceTags>
            {
                new ResourceTags { Tag = tagText }
            }
        };

        // 保存数据
        using (var context = factory.CreateContext())
        {
            context.Resources?.Add(resourceRequest);
            await context.SaveChangesAsync();
        }

        // 验证数据
        using (var context = factory.CreateContext())
        {
            var tagCount = context.ResourceTags?.Count();
            Assert.AreEqual(1, tagCount);

            var savedTag = await context.ResourceTags?.FirstOrDefaultAsync(r => r.Tag == tagText);
            Assert.IsNotNull(savedTag);
            Assert.AreEqual(resourceId.ToString(), savedTag.ResourceId);
        }
    }
}

方式2:从子实体关联主实体(优化你的原写法)

如果要从ResourceTags反向关联ResourceRequest,需确保外键与主实体主键严格匹配,同时注意异步方法的正确使用:

[TestMethod]
public async Task AddResourceTags_ViaChildEntity()
{
    using (var factory = new PrismContextFactory())
    {
        Random rnd = new Random();
        var resourceId = new byte[32];
        rnd.NextBytes(resourceId);
        var tagText = "tagtext";
        var convertedId = Convert.ToBase64String(resourceId);

        var resourceTag = new ResourceTags
        {
            ResourceId = resourceId,
            Tag = tagText,
            ResourceRequest = new ResourceRequest
            {
                ID = convertedId,
                SubscriptionID = "",
                TenantID = "",
                Name = "",
                ResourceGroup = ""
            }
        };

        using (var context = factory.CreateContext())
        {
            context.ResourceTags?.Add(resourceTag);
            await context.SaveChangesAsync();
        }

        using (var context = factory.CreateContext())
        {
            var count = context.ResourceTags?.Count();
            Assert.AreEqual(1, count);

            var savedTag = await context.ResourceTags?.FirstOrDefaultAsync(r => r.Tag == tagText);
            Assert.IsNotNull(savedTag);
            Assert.IsTrue(savedTag.ResourceId.SequenceEqual(resourceId));
        }
    }
}

关键修正点:

  • 原测试中调用FirstOrDefaultAsync未加await,断言的是Task对象而非实际实体,必须改用异步测试方法并等待异步查询完成。
  • byte[]类型的主键/外键不能直接用==比较,需用SequenceEqual判断内容是否一致。
  • 确保主实体主键与子实体外键的类型、值完全匹配,否则EF无法正确关联。

二、内存SQLite测试的核心限制

  1. 特性支持不足:SQLite本身不支持部分EF Core及生产数据库的特性,比如:

    • 嵌套事务、事务保存点等高级事务功能
    • SQL Server专属函数(如STRING_AGG、DATEFROMPARTS)
    • Timestamp/RowVersion并发令牌的处理逻辑与生产库不一致
    • 全文搜索、空间数据等高级能力
  2. 数据生命周期绑定连接:内存SQLite的数据仅在连接存活期间存在,一旦连接关闭,所有数据丢失。必须让多个上下文实例共享同一连接才能复用测试数据。

  3. 数据类型差异:

    • DateTime精度仅到秒级,与SQL Server的毫秒/微秒精度存在差异
    • decimal类型的精度、范围限制与生产数据库不同
    • 部分.NET类型(如TimeSpan)的存储逻辑存在差异
  4. 并发测试无效:内存数据库是单进程单线程模型,无法模拟生产环境中的多线程并发操作,测试结果无法反映真实并发场景下的行为。

  5. 生产行为不一致风险:即使测试通过,由于SQLite与生产数据库(如SQL Server、PostgreSQL)的语法、执行计划、约束校验逻辑不同,仍可能在生产环境出现问题。

内容的提问来源于stack exchange,提问作者Tim Whatley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 02:24:38