SQLite 和 EF6 的愚蠢行为

Wer*_*ied 5 c# sqlite entity-framework

我不能相信我是第一个遇到这个问题的人,但在网上没有找到任何类似的讨论。

这是简单的完整代码示例:

using SQLite.CodeFirst;
using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

namespace ConsoleApp1
{
  class Program
  {
    static void Main(string[] args)
    {
      Entity entity = new Entity();
      Guid id = entity.Id;
      using (var context = new MyDbContext())
      {
        context.Entities.Add(entity);
        context.SaveChanges();

        // this finds an entry
        var item = context.Entities.Find(id);
      }

      using (var context = new MyDbContext())
      {
        // here it returns null
        var item = context.Entities.Find(id);
      }
    }
  }

  public class MyDbContext : DbContext
  {
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
      Database.SetInitializer(new SqliteDropCreateDatabaseWhenModelChanges<MyDbContext>(modelBuilder));
    }

    public MyDbContext() : base("MyConnection") {}

    public DbSet<Entity> Entities { get; set; }
  }

  public class Entity
  {
    [Key]
    public Guid Id { get; set; } = Guid.Parse("D46D98F3-C262-468A-9C28-83D81080CF18");

    public string Name { get; set; } = "Test";
  }
}
Run Code Online (Sandbox Code Playgroud)

问题已经在代码中标记出来了。第一个“查找”返回新添加的条目。

但是获取上下文的新实例时,找不到该条目。

即使我第二次运行该应用程序,跳过将条目添加到表中的代码,它也不会找到该项目。问题似乎不是“Find”方法,因为我已经尝试了其他几个 linq 语句,但结果相同。

当我在搜索之前首先从表中获取所有项目时,它适用于“查找”,但不适用于 linq。

这是示例:

        using (var context = new MyDbContext())
        {
            // this returns all items
            var allItems = context.Entities.ToArrayAsync().Result;
            // this finds the item
            var item1 = context.Entities.Find(id);
            // this doesn't find the item
            var item2 = context.Entities.Where(x => x.Id == id).FirstOrDefault();
        }

        using (var context = new MyDbContext())
        {
            // this doesn't find the item
            var item1 = context.Entities.Find(id);
            // this also doesn't find the item
            var item2 = context.Entities.Where(x => x.Id == id).FirstOrDefault();
        }
Run Code Online (Sandbox Code Playgroud)

有人有解释吗?将密钥更改为 string 或 int 而不是 GUID,它按预期工作。

Wer*_*ied 0

好的,我会尝试回答一些言论。

我了解 SQLite 中的 Guid 用法。但因为我想处理外键,所以我认为使用 Guid 而不是数据库生成的键更容易。

查看该表可以看出,Guid 按预期存储为 16 字节 BLOB,并且 DB 中的字节与我使用的 Guid 相对应。

这是该表的表创建语句:

创建表“实体”([Id] uniqueidentifier NOT NULL PRIMARY KEY,[Name] nvarchar)

是的,我在关闭应用程序后阅读了数据库。第二个代码示例是我为此使用的代码示例。我在那里描述了,我第一次使用块它使用 Find 方法找到条目,但不是使用 Linq,并且只有在我之前获取整个表时才找到条目(请参阅我的评论)。之前没有读取所有条目,因为在第二个使用块中没有找到任何条目。

我知道,EF 使用 ADO.NET,但是 EF “生成”SQL 语句来查询数据库,并且 Find 和 linq 之间可能存在某些错误/不同,并且某些内容被缓存,因为当我获取之前整张桌子。生成 SQL 语句是 EF 的一部分,而不是 CodeFirst。所以我认为这个问题与CF无关。即使我使用现有的数据库,在没有 CF 的情况下也会有相同的行为。

没有内存数据库,我在一个应用程序中使用相同的连接字符串运行这两个块。如果我使用数据库浏览器更改“名称”字段,我也会读取这些更改。

我发布了整个代码来测试。可能有人投入一些时间并且可以重现该行为。连接字符串是通用的:

<add name="MyConnection" connectionString="Data Source=c:\test\test.s3db" providerName="System.Data.SQLite.EF6" />
Run Code Online (Sandbox Code Playgroud)