SQLite.net 扩展多次加载同一个实体而不是返回同一个引用
SQLite.net Extensions loads the same entity multiple times rather than returning the same reference
我在 Windows 10 通用应用程序中使用 SQLite.Net 扩展的 PCL 版本。这是我第一次使用它。它通常似乎可以正常工作,但它似乎是多次加载一个实体,而不是重复使用对同一对象的引用。
根据 SQLite.Net 扩展文档:
SQLite-Net Extensions will ensure that any object is loaded only once from the database and will resolve circular dependencies and inverse relationships while maintaining integral reference. This means that any returned object of the same class with the same identifier will be a reference to exactly the same object.
这似乎并没有发生在我身上。这是我的代码:
public class Group {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
public string GroupName { get; set; }
public override string ToString() {
return string.Format("Group [ID: {0}, HashCode: {1}] GroupName={2}", Id.ToString().Last(4), GetHashCode(), GroupName);
}
[ManyToMany(typeof(GroupMember), CascadeOperations = CascadeOperation.CascadeRead)]
public List<Member> Members { get; set; }
public void DebugIt() {
Debug.WriteLine(this);
foreach (var member in Members) Debug.WriteLine(" " + member);
}
}
public class Member {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
public string Name { get; set; }
public override string ToString() {
return string.Format("Member [ID: {0}, HashCode: {1}] Name={2}", Id.ToString().Last(4), GetHashCode(), Name);
}
[ManyToMany(typeof (GroupMember), CascadeOperations = CascadeOperation.CascadeRead)]
public List<Group> Groups { get; set; }
public void DebugIt() {
Debug.WriteLine(this);
foreach (var group in Groups) Debug.WriteLine(" " + group);
}
}
public class GroupMember {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
[ForeignKey(typeof(Group))]
public Guid GroupID { get; set; }
[ForeignKey(typeof(Member))]
public Guid MemberId { get; set; }
}
public class DatabaseGroups {
private const string FileName = "db.sqlite";
private SQLiteConnection _db;
public async Task<bool> LoadAsync() {
var exists = await FileHelper.DoesFileExistAsync(FileName);
_db = new SQLiteConnection(new SQLitePlatformWinRT(), DatabaseFullPath,
exists ? SQLiteOpenFlags.ReadWrite : SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite);
if (!exists) InitializeWithDefaults();
return await FileHelper.DoesFileExistAsync(FileName);
}
private void InitializeWithDefaults() {
_db.CreateTable<Group>();
_db.CreateTable<Member>();
_db.CreateTable<GroupMember>();
var group1 = new Group {GroupName = "Group 1"};
var group2 = new Group {GroupName = "Group 2"};
var member1 = new Member {Name = "Bob"};
var member2 = new Member {Name = "Jane"};
_db.Insert(group1);
_db.Insert(group2);
_db.Insert(member1);
_db.Insert(member2);
group1.Members = new List<Member> {member1, member2};
_db.UpdateWithChildren(group1);
group2.Members = new List<Member> {member1, member2};
_db.UpdateWithChildren(group2);
}
private static StorageFolder DatabaseFolder {
get { return ApplicationData.Current.LocalFolder; }
}
private static string DatabaseFullPath {
get { return Path.Combine(DatabaseFolder.Path, FileName); }
}
public void DebugIt() {
foreach (var groupId in _db.Table<Group>().Select(g => g.Id)) {
var group = _db.GetWithChildren<Group>(groupId);
group.DebugIt();
}
foreach (var memberId in _db.Table<Member>().Select(m => m.Id)) {
var member = _db.GetWithChildren<Member>(memberId);
member.DebugIt();
}
}
}
protected override async void OnLaunched(LaunchActivatedEventArgs e) {
_db = new DatabaseGroups();
await _db.LoadAsync();
_db.DebugIt();
运行时,我会创建一些初始数据。然后,我使用 GetWithChildren 加载这些对象并对其进行调试。以下是结果:
Group[ID: 4858, HashCode: 51192825] GroupName = Group 1
Member[ID: dbfa, HashCode: 64971671] Name = Jane
Member[ID: b047, HashCode: 30776584] Name = Bob
Group[ID: 30f0, HashCode: 53439890] GroupName = Group 2
Member[ID: dbfa, HashCode: 36062904] Name = Jane
Member[ID: b047, HashCode: 9089598] Name = Bob
Member[ID: b047, HashCode: 20305449] Name = Bob
Group[ID: 30f0, HashCode: 9648315] GroupName = Group 2
Group[ID: 4858, HashCode: 29803642] GroupName = Group 1
Member[ID: dbfa, HashCode: 36899882] Name = Jane
Group[ID: 30f0, HashCode: 23318221] GroupName = Group 2
Group[ID: 4858, HashCode: 60865449] GroupName = Group 1
如您所见,对象似乎加载正确,但第 1 组(例如)的对象引用不同(请参阅哈希码)。
我是否误解了 SQLite.Net 扩展处理对象引用的方式?也许它可以在对 GetWithChildren 的单个调用中处理对象引用的重用,但不能在同一 SQLiteConnection 的多个调用中处理?
如果是这样,您应该如何加载具有这些关系的更复杂的对象图?
你是对的,SQLite-Net Extensions 缓存递归调用的对象以避免引用循环和处理反向关系,但它不缓存调用之间的对象。
SQLite-Net Extensions 只是 SQLite.Net 上的一层薄薄的层,如果你对整体参考很重要,你可以返回到手动查询以进行更复杂的操作。
如果您有任何建议或拉取请求,随时欢迎他们;)
我在 Windows 10 通用应用程序中使用 SQLite.Net 扩展的 PCL 版本。这是我第一次使用它。它通常似乎可以正常工作,但它似乎是多次加载一个实体,而不是重复使用对同一对象的引用。
根据 SQLite.Net 扩展文档:
SQLite-Net Extensions will ensure that any object is loaded only once from the database and will resolve circular dependencies and inverse relationships while maintaining integral reference. This means that any returned object of the same class with the same identifier will be a reference to exactly the same object.
这似乎并没有发生在我身上。这是我的代码:
public class Group {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
public string GroupName { get; set; }
public override string ToString() {
return string.Format("Group [ID: {0}, HashCode: {1}] GroupName={2}", Id.ToString().Last(4), GetHashCode(), GroupName);
}
[ManyToMany(typeof(GroupMember), CascadeOperations = CascadeOperation.CascadeRead)]
public List<Member> Members { get; set; }
public void DebugIt() {
Debug.WriteLine(this);
foreach (var member in Members) Debug.WriteLine(" " + member);
}
}
public class Member {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
public string Name { get; set; }
public override string ToString() {
return string.Format("Member [ID: {0}, HashCode: {1}] Name={2}", Id.ToString().Last(4), GetHashCode(), Name);
}
[ManyToMany(typeof (GroupMember), CascadeOperations = CascadeOperation.CascadeRead)]
public List<Group> Groups { get; set; }
public void DebugIt() {
Debug.WriteLine(this);
foreach (var group in Groups) Debug.WriteLine(" " + group);
}
}
public class GroupMember {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
[ForeignKey(typeof(Group))]
public Guid GroupID { get; set; }
[ForeignKey(typeof(Member))]
public Guid MemberId { get; set; }
}
public class DatabaseGroups {
private const string FileName = "db.sqlite";
private SQLiteConnection _db;
public async Task<bool> LoadAsync() {
var exists = await FileHelper.DoesFileExistAsync(FileName);
_db = new SQLiteConnection(new SQLitePlatformWinRT(), DatabaseFullPath,
exists ? SQLiteOpenFlags.ReadWrite : SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite);
if (!exists) InitializeWithDefaults();
return await FileHelper.DoesFileExistAsync(FileName);
}
private void InitializeWithDefaults() {
_db.CreateTable<Group>();
_db.CreateTable<Member>();
_db.CreateTable<GroupMember>();
var group1 = new Group {GroupName = "Group 1"};
var group2 = new Group {GroupName = "Group 2"};
var member1 = new Member {Name = "Bob"};
var member2 = new Member {Name = "Jane"};
_db.Insert(group1);
_db.Insert(group2);
_db.Insert(member1);
_db.Insert(member2);
group1.Members = new List<Member> {member1, member2};
_db.UpdateWithChildren(group1);
group2.Members = new List<Member> {member1, member2};
_db.UpdateWithChildren(group2);
}
private static StorageFolder DatabaseFolder {
get { return ApplicationData.Current.LocalFolder; }
}
private static string DatabaseFullPath {
get { return Path.Combine(DatabaseFolder.Path, FileName); }
}
public void DebugIt() {
foreach (var groupId in _db.Table<Group>().Select(g => g.Id)) {
var group = _db.GetWithChildren<Group>(groupId);
group.DebugIt();
}
foreach (var memberId in _db.Table<Member>().Select(m => m.Id)) {
var member = _db.GetWithChildren<Member>(memberId);
member.DebugIt();
}
}
}
protected override async void OnLaunched(LaunchActivatedEventArgs e) {
_db = new DatabaseGroups();
await _db.LoadAsync();
_db.DebugIt();
运行时,我会创建一些初始数据。然后,我使用 GetWithChildren 加载这些对象并对其进行调试。以下是结果:
Group[ID: 4858, HashCode: 51192825] GroupName = Group 1
Member[ID: dbfa, HashCode: 64971671] Name = Jane
Member[ID: b047, HashCode: 30776584] Name = Bob
Group[ID: 30f0, HashCode: 53439890] GroupName = Group 2
Member[ID: dbfa, HashCode: 36062904] Name = Jane
Member[ID: b047, HashCode: 9089598] Name = Bob
Member[ID: b047, HashCode: 20305449] Name = Bob
Group[ID: 30f0, HashCode: 9648315] GroupName = Group 2
Group[ID: 4858, HashCode: 29803642] GroupName = Group 1
Member[ID: dbfa, HashCode: 36899882] Name = Jane
Group[ID: 30f0, HashCode: 23318221] GroupName = Group 2
Group[ID: 4858, HashCode: 60865449] GroupName = Group 1
如您所见,对象似乎加载正确,但第 1 组(例如)的对象引用不同(请参阅哈希码)。
我是否误解了 SQLite.Net 扩展处理对象引用的方式?也许它可以在对 GetWithChildren 的单个调用中处理对象引用的重用,但不能在同一 SQLiteConnection 的多个调用中处理?
如果是这样,您应该如何加载具有这些关系的更复杂的对象图?
你是对的,SQLite-Net Extensions 缓存递归调用的对象以避免引用循环和处理反向关系,但它不缓存调用之间的对象。
SQLite-Net Extensions 只是 SQLite.Net 上的一层薄薄的层,如果你对整体参考很重要,你可以返回到手动查询以进行更复杂的操作。
如果您有任何建议或拉取请求,随时欢迎他们;)