【问题标题】:FOREIGN KEY constraint failed in SQLite unit testsSQLite 单元测试中的 FOREIGN KEY 约束失败
【发布时间】:2019-01-26 10:59:09
【问题描述】:

我有一个数据库,其中有两个不同路径到某个实体,一个路径为DeleteBehavior.Cascade,另一个路径为DeleteBehavior.Restrict,以避免在创建数据库时出现多级联路径错误。当在内存中使用 IIS Express 和 Visual Studio 的 SQL Server 在本地运行时,这可以正常工作,但在测试相同功能的单元测试中会抛出错误。

例子:

public class Account
{
    public string Id { get; set; }
}

每个帐户都可以有订阅:

public class Subscription
{
    public string Id { get; set; }

    // relationships

    public string AccountId { get; set; }

    public Account Account { get; set; }
}

帐户和订阅都可以有联系人(这可能没有多大意义,但这只是一个示例,实际上我的模型更复杂,但基本上可以归结为这一点):

public class Contact
{
    public string Id { get; set; }

    // relationships

    public string AccountId { get; set; }

    public Account Account { get; set; }

    public string SubscriptionId { get; set; }

    public Subscription Subscription { get; set; }
}

如果帐户或订阅被删除,我希望删除所有联系人。这是我的联系人模型构建器配置:

public void Configure(EntityTypeBuilder<Contact> entity)
{
    entity.HasOne(e => e.Account)
        .WithMany()
        .HasForeignKey(e => e.AccountId)
        .OnDelete(DeleteBehavior.Restrict);

    entity.HasOne(e => e.Subscription)
        .WithMany()
        .HasForeignKey(e => e.SubscriptionId)
        .OnDelete(DeleteBehavior.Cascade);
}

订阅配置:

public void Configure(EntityTypeBuilder<Subscription> entity)
{
    entity.HasOne(e => e.Account)
        .WithMany()
        .HasForeignKey(e => e.AccountId)
        .OnDelete(DeleteBehavior.Cascade);
}

想法是,当一个帐户被删除时,这将通过Cascade删除行为删除订阅,这将删除联系人,帐户和联系人之间的Restrict行为解决了“多级联路径”错误.

这在我在本地运行时有效,我可以删除一个帐户并删除所有订阅和联系人,没有错误。问题在于在内存中使用 SQLite 的单元测试(使用 xUnit)。我想测试一下删除账号会删除所有联系人:

[Fact]
public async Task DeleteAccount_ContactIsDeleted()
{
    using (var factory = new ContextFactory()) // same connection will be used within using block
    {
        using (var context = factory.CreateContext())
        {
            await context.SeedDatabaseOneContactAsync(); // inserts account, subscription and contact into database
        }

        using (var context = factory.CreateContext())
        {
            Assert.Single(context.Contacts);
            Assert.Single(context.Subscriptions);
            Assert.Single(context.Accounts);

            var account = await context.Accounts.SingleAsync();
            context.Remove(account);
            await context.SaveChangesAsync();

            Assert.Empty(context.Contacts);
            Assert.Empty(context.Accounts);
            Assert.Empty(context.Subscriptions);
        }
    }
}

但是这会引发以下错误

消息:Microsoft.EntityFrameworkCore.DbUpdateException:更新条目时出错。有关详细信息,请参阅内部异常。 ---- Microsoft.Data.Sqlite.SqliteException : SQLite 错误 19: 'FOREIGN KEY 约束失败'。

上线

await context.SaveChangesAsync();

我想知道这是否是 SQLite 的问题,或者我的模型或配置是否有问题?

注意:我使用的是 Entity Framework Core 2.1.1,这些关系是必需的(不能为空)。

【问题讨论】:

  • 如果对您有帮助,请将答案标记为已接受。

标签: c# sqlite entity-framework-core xunit.net


【解决方案1】:

默认情况下,FK 约束在 SQLite 中是禁用的。 http://www.sqlite.org/foreignkeys.html

默认情况下禁用外键约束(为了向后兼容),因此必须为每个数据库连接单独启用。 (但是请注意,SQLite 的未来版本可能会更改,以便默认启用外键约束。细心的开发人员不会对默认情况下是否启用外键做出任何假设,而是会根据需要启用或禁用它们。)

您可以通过将foreign keys=true 添加到您的连接字符串来启用它们。 (完整示例为data source=test.db;foreign keys=true

【讨论】:

    【解决方案2】:

    不确定这是否是您的问题,但是当 fluent API 关系配置与实际表结构不匹配时,我遇到了同样的错误。

    在我的例子中,流畅的 API 设置了一对一的关系,但实际的底层表结构是一对多的关系。

    【讨论】:

      【解决方案3】:

      老问题,但我遇到了同样的问题。

      到目前为止,我认为这与列是一个字符串有关,您不能将其设置为可为空的值类型。例如。细绳?如果您将其更改为Guid?还是整数?那么你就没有同样的问题了。

      我认为这可能是实体框架中的一个错误,它认为该列实际上是不可为空的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多