我无法相信我是遇到这个问题的第一人,但是在网上找不到任何类似的讨论。
这是简单的完整代码示例:
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";
}
}
该问题已在代码中标记。第一个“查找”返回新添加的条目。
但是获取上下文的新实例时,找不到该条目。
即使我第二次运行该应用程序,也跳过了将条目添加到表中的代码,它也找不到该项目。问题似乎不是“查找”方法,因为T尝试了其他几个具有相同结果的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();
}
有人有解释吗?将键更改为string或int而不是GUID,它可以按预期工作。
答案 0 :(得分:1)
该bug上的链接使我想到了使用连接字符串添加“ BinaryGUID = True;”的想法。
比预期的要好。谢谢。
答案 1 :(得分:0)
好的,我会尝试回答一些言论。
我了解SQLite中的Guid使用情况。但是因为我想处理外键,所以我认为使用Guid代替DB生成的键更容易。
看一下表,发现Guid按预期存储为16字节BLOB,并且DB ar中的字节与我使用的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" />
答案 2 :(得分:0)
问题在于SQLite数据库提供程序将GUID转换为Blob。然后,此Blob作为字节数组而不是字符串存储在数据库中。然后,当您尝试直接使用LINQ查询数据库时,问题就会显现出来,因为
context.Entities.Where(x => x.Id == id).FirstOrDefault()
获取翻译为
SELECT Key, Name
FROM Entity
WHERE Entity.Key = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
因此它正在将字符串文字与字节数组进行比较,这显然会返回错误的结果。将所有条目加载到内存中,然后对其进行查询,将导致GUID从其字节数组表示形式转换为实际的GUID,LINQ可以正确比较并选择正确的GUID。
此问题的解决方案是在连接字符串中添加BinaryGUID=True
部分。