如何使用 GUID 作为 System.Data.Sqlite 的主键
How to use GUID as Primary Key with System.Data.Sqlite
我并不是说它是可取的,但有时你继承了一些只需要工作的东西。在这种情况下,它是主键的 Guids...
开箱即用,您会收到有关 System.Guid 无法使用 byte[](如果您使用的是 BinaryGUID=False 则为 String)的错误。
要解决此问题,您需要拦截 byte[] 数组(或字符串),而不是 return Guid 类型。 System.Data.Sqlite 提供商可以做到这一点。
第一个
using System.Data.SQLite;
然后,将其放入代码优先数据库上下文的构造函数中:
var con = (SQLiteConnection)base.Database.Connection;
var bind = System.Data.SQLite.SQLiteTypeCallbacks.Create(
null,
new SQLiteReadValueCallback(GuidInterceptor), null, null);
con.SetTypeCallbacks("uniqueidentifier", bind);
con.SetTypeCallbacks("", bind); //Sometimes, the system just doesn't know
con.Flags |= SQLiteConnectionFlags.UseConnectionReadValueCallbacks;
然后这是神奇的功能:
private void GuidInterceptor(SQLiteConvert convert, SQLiteDataReader reader, SQLiteConnectionFlags flags, SQLiteReadEventArgs args, string typename, int index, object userdata, out bool complete)
{
complete = false;
if (typename == "uniqueidentifier")
{
var e = (SQLiteReadValueEventArgs)args;
var o = reader.GetGuid(index);
e.Value.Value = o;
e.Value.GuidValue = o;
complete = true;
}
else
{
var o = reader.GetValue(index);
if (o is byte[])
{
var b = (byte[])o;
if (b.Length == 16)
{
var e = (SQLiteReadValueEventArgs)args;
var g = new Guid(b);
e.Value.Value = g;
e.Value.GuidValue = g;
complete = true;
}
}
else if (o is string)
{
var s = (string)o;
if (s.Length == 36)
{
var e = (SQLiteReadValueEventArgs)args;
var goGuid = (e.MethodName == "GetGuid");
if (!goGuid)
goGuid = (s[8] == '-' && s[13] == '-' && s[18] == '-' && s[23] == '-');
Guid g;
if (goGuid && Guid.TryParse(s, out g))
{
e.Value.Value = g;
e.Value.GuidValue = g;
complete = true;
}
else
{
}
}
}
}
}
这只修复了数据的读入。如果您尝试在 Guid 成员上 .Where()
,并且您使用的是 BinaryGUID=True
,则不会 return 编辑任何行(应该有)。现在,您需要 BinaryGUID=False
,它确实占用了更多 space,但这是一个简单的解决方案。
如果可以,请尝试将 Guid 属性定义为:
[Key]
[MaxLength(16)]
[MinLength(16)]
public byte[] AccountUserId { get; set; }
您将被迫在您的应用程序层中调用 guidValue.ToByteArray()
。您也许可以创建一些助手,而不必调用该函数。拥有自己的自定义函数代理来创建和解析 Guid 可能会有所帮助,因此您可以轻松更改实现。
就我而言,我没有时间将所有 Guid 属性和用法转换为 byte[]
我并不是说它是可取的,但有时你继承了一些只需要工作的东西。在这种情况下,它是主键的 Guids...
开箱即用,您会收到有关 System.Guid 无法使用 byte[](如果您使用的是 BinaryGUID=False 则为 String)的错误。
要解决此问题,您需要拦截 byte[] 数组(或字符串),而不是 return Guid 类型。 System.Data.Sqlite 提供商可以做到这一点。
第一个
using System.Data.SQLite;
然后,将其放入代码优先数据库上下文的构造函数中:
var con = (SQLiteConnection)base.Database.Connection;
var bind = System.Data.SQLite.SQLiteTypeCallbacks.Create(
null,
new SQLiteReadValueCallback(GuidInterceptor), null, null);
con.SetTypeCallbacks("uniqueidentifier", bind);
con.SetTypeCallbacks("", bind); //Sometimes, the system just doesn't know
con.Flags |= SQLiteConnectionFlags.UseConnectionReadValueCallbacks;
然后这是神奇的功能:
private void GuidInterceptor(SQLiteConvert convert, SQLiteDataReader reader, SQLiteConnectionFlags flags, SQLiteReadEventArgs args, string typename, int index, object userdata, out bool complete)
{
complete = false;
if (typename == "uniqueidentifier")
{
var e = (SQLiteReadValueEventArgs)args;
var o = reader.GetGuid(index);
e.Value.Value = o;
e.Value.GuidValue = o;
complete = true;
}
else
{
var o = reader.GetValue(index);
if (o is byte[])
{
var b = (byte[])o;
if (b.Length == 16)
{
var e = (SQLiteReadValueEventArgs)args;
var g = new Guid(b);
e.Value.Value = g;
e.Value.GuidValue = g;
complete = true;
}
}
else if (o is string)
{
var s = (string)o;
if (s.Length == 36)
{
var e = (SQLiteReadValueEventArgs)args;
var goGuid = (e.MethodName == "GetGuid");
if (!goGuid)
goGuid = (s[8] == '-' && s[13] == '-' && s[18] == '-' && s[23] == '-');
Guid g;
if (goGuid && Guid.TryParse(s, out g))
{
e.Value.Value = g;
e.Value.GuidValue = g;
complete = true;
}
else
{
}
}
}
}
}
这只修复了数据的读入。如果您尝试在 Guid 成员上 .Where()
,并且您使用的是 BinaryGUID=True
,则不会 return 编辑任何行(应该有)。现在,您需要 BinaryGUID=False
,它确实占用了更多 space,但这是一个简单的解决方案。
如果可以,请尝试将 Guid 属性定义为:
[Key]
[MaxLength(16)]
[MinLength(16)]
public byte[] AccountUserId { get; set; }
您将被迫在您的应用程序层中调用 guidValue.ToByteArray()
。您也许可以创建一些助手,而不必调用该函数。拥有自己的自定义函数代理来创建和解析 Guid 可能会有所帮助,因此您可以轻松更改实现。
就我而言,我没有时间将所有 Guid 属性和用法转换为 byte[]