C#查询Win10 Photos SQLite数据库缺失NoCaseUnicode排序规则的解决问询
我来帮你搞定这个问题——Windows Photos的SQLite数据库用了自定义的NoCaseUnicode排序规则,这个规则是Photos应用自己实现的,SQLite默认没有内置,所以你的C#程序执行关联查询时才会报错。下面是几个可行的解决方案,按推荐程度排序:
1. 在C#中注册自定义的NoCaseUnicode排序规则(推荐)
这是一劳永逸的方法,只需要在打开数据库连接后注册一次自定义规则,就能让SQLite识别NoCaseUnicode,不需要修改任何SQL语句。我们可以用.NET的全球化API模拟这个规则的核心行为:不区分大小写的Unicode字符串比较。
using System.Data.SQLite; using System.Globalization; public void QueryPhotosDatabase() { // 替换为你的Photos数据库实际路径 var dbPath = @"C:\Users\<YourUsername>\AppData\Local\Packages\Microsoft.Windows.Photos_8wekyb3d8bbwe\LocalState\MediaDb.sqlite"; var connectionString = $"Data Source={dbPath};Version=3;"; using (var conn = new SQLiteConnection(connectionString)) { conn.Open(); // 注册自定义的NoCaseUnicode排序规则,模拟不区分大小写的Unicode比较 conn.CreateCollation("NoCaseUnicode", (string x, string y) => { if (x == null && y == null) return 0; if (x == null) return -1; if (y == null) return 1; // 使用不变文化的不区分大小写比较,和Photos的规则行为一致 return CultureInfo.InvariantCulture.CompareInfo.Compare(x, y, CompareOptions.IgnoreCase); }); // 现在可以正常执行带关联的查询了,不需要额外加COLLATE BINARY var query = @" SELECT i.Item_FileName FROM Item i JOIN AlbumItem ai ON i.ItemId = ai.ItemId WHERE i.Item_FileName = '2.jpg'"; using (var cmd = new SQLiteCommand(query, conn)) using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { Console.WriteLine($"找到文件:{reader["Item_FileName"]}"); } } } }
这个方案的好处是完全贴合数据库的默认配置,后续所有查询、关联操作都能正常使用NoCaseUnicode规则,不需要改动SQL语句。
2. 在所有查询和关联中显式指定COLLATE BINARY
如果你不想写C#代码注册规则,可以在SQL语句的每个涉及排序规则的地方(包括JOIN的ON子句)显式加上COLLATE BINARY,覆盖表的默认排序规则:
SELECT i.Item_FileName FROM Item i JOIN AlbumItem ai ON i.ItemId COLLATE BINARY = ai.ItemId COLLATE BINARY WHERE i.Item_FileName = '2.jpg' COLLATE BINARY;
注意:必须给JOIN的关联字段也加上COLLATE BINARY,因为关联时SQLite会使用字段的默认排序规则(也就是缺失的NoCaseUnicode),不加的话还是会触发报错。这个方案适合简单查询,但复杂查询会让SQL语句变得冗长。
3. 创建临时视图统一转换排序规则
对于复杂的多表查询,你可以在连接数据库后创建临时视图,把需要的表的所有字段都转换为COLLATE BINARY,然后用视图来执行查询:
using (var conn = new SQLiteConnection(connectionString)) { conn.Open(); // 创建Item表的临时视图,统一转换为BINARY排序规则 var createItemViewCmd = new SQLiteCommand(@" CREATE TEMP VIEW Item_Binary AS SELECT ItemId COLLATE BINARY AS ItemId, Item_FileName COLLATE BINARY AS Item_FileName, Item_Path COLLATE BINARY AS Item_Path -- 其他需要用到的字段也按此格式添加 FROM Item;", conn); createItemViewCmd.ExecuteNonQuery(); // 同样创建AlbumItem表的临时视图 var createAlbumViewCmd = new SQLiteCommand(@" CREATE TEMP VIEW AlbumItem_Binary AS SELECT ItemId COLLATE BINARY AS ItemId, AlbumId COLLATE BINARY AS AlbumId FROM AlbumItem;", conn); createAlbumViewCmd.ExecuteNonQuery(); // 现在查询视图就不会报错了 var query = @" SELECT i.Item_FileName FROM Item_Binary i JOIN AlbumItem_Binary ai ON i.ItemId = ai.ItemId WHERE i.Item_FileName = '2.jpg'"; // 执行查询并处理结果... }
这个方案把转换逻辑封装在视图里,查询语句会更简洁,但每次打开连接都需要重新创建临时视图。
总结
优先推荐方案1,注册自定义排序规则后,你的C#程序就能完全适配Windows Photos的数据库,不需要修改任何查询逻辑。如果只是临时执行简单查询,方案2更快捷;复杂多表查询可以考虑方案3。
内容的提问来源于stack exchange,提问作者SimonKravis

