【问题标题】:Sqlite Remove Not Null Constraints InMemorySqlite 删除非空约束 InMemory
【发布时间】:2020-12-08 08:26:13
【问题描述】:

如何删除 SQL Lite 内存数据库中所有表的所有非空约束? 我有 Foreign Keys = false,要删除外键。

var InMemoryConnectionString = "DataSource=:memory:;Foreign Keys=False";
var _connection = new SqliteConnection(InMemoryConnectionString);

_connection.Open();
var options = new DbContextOptionsBuilder<TestContext>()
        .UseSqlite(_connection)
        .Options;
var testContext = new TestContext(options);
testContext.Database.EnsureCreated();

寻找删除所有约束将是一个更好的答案。

如果未填写所有必填字段,则会出现 Not null 错误。

 var seedData= new List<Product>
{
    new Product
    { ProductId = 1, Description = "test" },
    new Product
    { ProductId = 2, Description = "test2" }
};

testContext.Product.AddRange(seedData);
testContext.SaveChanges();

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

【问题讨论】:

  • 这是用于测试还是应用程序不需要非空约束?
  • 这是 Code First 还是 Database First?我以前没有使用过内存数据库,但我之所以问,是因为如果它是 Code First,我相信您需要删除 Class 文件中的 FK。我不相信您可以将 Foreign Keys=False 添加到连接字符串并删除 FK。
  • 向我们展示产品的模型。
  • 嗨@rick 问题适用于所有表格
  • 你可以随时创建一个没有约束的新表,复制数据,然后删除旧表

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


【解决方案1】:

首先.. 尝试在 SaveChanges() 之前调用 Update()

要深入了解它,只需执行标准的开发人员步骤并对其进行调试。将该调用包装在 try catch 中并从堆栈中跟踪问题。在 catch 块中注销到文件并查看生成的自动回滚日志

Console.WriteLine("ERROR: " + ex.Message + (ex.InnerException?.Message?.Replace("\r\n", " :: "))

您会看到如下内容: 发生一个或多个错误。约束失败 :: NOT NULL 约束失败:Product.ProductId

您可以从那里追溯根本原因和代码。

【讨论】:

    【解决方案2】:

    虽然执行约束的是 SQLite,但它这样做是因为您的实体框架 (EF) 模型。设置上下文时,如果模型属性被修饰为[Required],那么 EF 会在数据库中添加一个非空约束。如果您希望允许它为 null,则应删除 [Required] 属性。

    失败模型示例

    public class Product
    {
        public int ProductId { get; set; }
    
        [Required]
        public string Description { get; set; }
    }
    

    将抛出一个SQLite Error 19(如您所见):

    ctx.Products.Add(new Product() { ProductId = 3});
    ctx.SaveChanges(); //SQLite Error 19 on Products.Description
    

    传递模型示例

    public class Product
    {
        public int ProductId { get; set; }
    
        public string Description { get; set; }
    }
    

    愉快地添加一个带有空描述的项目:

    ctx.Products.Add(new Product() { ProductId = 3});
    ctx.SaveChanges(); //All good!
    

    分析

    由于您在示例中将上下文命名为 testContext,因此您可能正在尝试对内存中的 SQLite 执行单元/集成测试,而不是您的默认生产上下文。也许在您的生产环境中,您要求这些列不能为空(这就是模型如此装饰的原因) - 如果是这种情况,您的 测试模型应该设置为与生产相同 - 这些列中不应允许使用空值。否则你测试的不是同一个东西,测试代码的价值会大大减少(错误会偷偷通过测试,因为值是空的,但由于数据库限制而在生产中失败)。

    这同样适用于删除 Foreign Keys 约束 - 如果在生产中您希望外键被强制执行并且(例如)删除以级联,您应该在测试设置中具有相同的行为。

    破解内存中的 SQLite 表

    SQLite 仅支持有限的ALTER TABLE 子集,不包括更改列定义。我们可以通过尝试以下操作来了解 EF 是如何设置表的:

    var cmd = new SqliteCommand("SELECT sql FROM sqlite_master WHERE name='Products'", _connection);
    Console.WriteLine(cmd.ExecuteScalar().ToString());
    

    哪些报告:

    CREATE TABLE "Products" (
        "ProductId" INTEGER NOT NULL CONSTRAINT "PK_Products" PRIMARY KEY AUTOINCREMENT,
        "Description" TEXT NOT NULL
    )
    

    要删除约束(不推荐)需要更改表(不支持)。解决此问题的唯一方法是遵循Making Other Kinds of Table Schema Changes,其中(概述)涉及创建第二个临时表(没有约束)、复制数据、删除现有表、将临时表重命名为原始名称。 (对于您正在使用的每个模型)。如果您在ctx.Database.EnsureCreated(); 之后立即执行此操作,您可以只在DROP 当前表和CREATE 它们没有任何您希望忽略的NOT NULL 约束(无需复制数据,因为尚未加载任何数据)。 EF 依赖数据库来强制执行约束,因此您现在可以在模型上拥有[Required] 属性,该属性可以为空,因为 SQLite 允许它 - 这是一个 hack,不推荐.

    【讨论】:

      猜你喜欢
      • 2016-07-12
      • 2020-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-02
      • 2010-12-25
      • 2021-12-30
      • 2011-02-02
      相关资源
      最近更新 更多