SQLite:从列表中的对象中检索子元素
SQLite: Retrieve child element from object in list
有一个Basket
,存储了一个List<Fruit>
。每个 Fruit
有一个 Pip
。如果我存储此关系并稍后检索它,ForeignKey
PipId
有一个值,但对象 Pip
是 null
,尽管我使用 CascadeRead
.
如果我尝试在 FruitList
上使用 CascadeOperation.All
,我会得到 Constraint
at SQLite.PreparedSqlLiteInsertCommand.ExecuteNonQuery (System.Object[] source) [0x00116] in /Users/fak/Dropbox/Projects/sqlite-net/src/SQLite.cs:2507
at SQLite.SQLiteConnection.Insert (System.Object obj, System.String extra, System.Type objType) [0x0014b] in /Users/fak/Dropbox/Projects/sqlite-net/src/SQLite.cs:1386
at SQLite.SQLiteConnection.Insert (System.Object obj) [0x00008] in /Users/fak/Dropbox/Projects/sqlite-net/src/SQLite.cs:1224
at SQLiteNetExtensions.Extensions.WriteOperations.InsertElement (SQLite.SQLiteConnection conn, System.Object element, System.Boolean replace, System.Reflection.PropertyInfo primaryKeyProperty, System.Boolean isAutoIncrementPrimaryKey, System.Collections.Generic.ISet`1[T] objectCache) [0x0005a] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:270
at SQLiteNetExtensions.Extensions.WriteOperations.InsertElements (SQLite.SQLiteConnection conn, System.Collections.IEnumerable elements, System.Boolean replace, System.Collections.Generic.ISet`1[T] objectCache) [0x00069] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:238
at SQLiteNetExtensions.Extensions.WriteOperations.InsertValue (SQLite.SQLiteConnection conn, System.Object value, System.Boolean replace, System.Boolean recursive, System.Collections.Generic.ISet`1[T] objectCache) [0x0002c] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:219
at SQLiteNetExtensions.Extensions.WriteOperations.InsertChildrenRecursive (SQLite.SQLiteConnection conn, System.Object element, System.Boolean replace, System.Boolean recursive, System.Collections.Generic.ISet`1[T] objectCache) [0x0004c] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:200
at SQLiteNetExtensions.Extensions.WriteOperations.InsertWithChildrenRecursive (SQLite.SQLiteConnection conn, System.Object element, System.Boolean replace, System.Boolean recursive, System.Collections.Generic.ISet`1[T] objectCache) [0x0002b] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:181
at SQLiteNetExtensions.Extensions.WriteOperations.InsertWithChildren (SQLite.SQLiteConnection conn, System.Object element, System.Boolean recursive) [0x00000] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:59
at SQLiteNetExtensionsAsync.Extensions.WriteOperations+<>c__DisplayClass1_0.<InsertWithChildrenAsync>b__0 () [0x00013] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensionsAsync-PCL\Extensions\WriteOperations.cs:55
at System.Threading.Tasks.Task.InnerInvoke () [0x0000f] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Threading.Tasks.Task.Execute () [0x00010] in <d18287e1d683419a8ec3216fd78947b9>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <d18287e1d683419a8ec3216fd78947b9>:0
at TestSQLite.Database+<StoreBasketAsync>d__5.MoveNext () [0x0021b] in C:\Users\some-user\Documents\Visual Studio 2015\Projects\TestSQLite\TestSQLite\TestSQLite\Database.cs:189
此外,我尝试在InsertWithChildrenAsync()
上使用recursive: true
,但Pip
也是null
。这是示例:
数据模型
public class Basket
{
private string number;
private List<Fruit> fruitList;
[PrimaryKey]
public string Number
{
get { return this.number; }
set { this.number = value; }
}
public string Name { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.CascadeRead)]
public List<Fruit> FruitList
{
get { return this.fruitList; }
set { this.fruitList = value; }
}
public Basket()
{
}
}
public class Fruit
{
private string number;
private Pip pip;
[PrimaryKey]
public string Number
{
get { return this.number; }
set { this.number = value; }
}
public string Type { get; set;}
[ForeignKey(typeof(Pip))]
public string PipId { get; set; }
[OneToOne]
public Pip Pip
{
get { return this.pip; }
set { this.pip = value; }
}
[ForeignKey(typeof(Basket))]
public string BasketId { get; set; }
public Fruit()
{
}
}
public class Pip
{
private string number;
private string title;
[PrimaryKey]
public string Number
{
get { return this.number; }
set { this.number = value; }
}
public string Title
{
get { return this.title; }
set { this.title = value; }
}
public Pip()
{
}
}
数据库操作
public class Database
{
private readonly SQLiteAsyncConnection database;
public Database(string databasePath)
{
this.database = new SQLiteAsyncConnection(databasePath);
this.database.CreateTableAsync<Basket>().Wait();
this.database.CreateTableAsync<Fruit>().Wait();
this.database.CreateTableAsync<Pip>().Wait();
}
public async Task<Basket> GetBasketAsync(string basketId)
{
try
{
var queryResult = await this.database.Table<Basket>().Where(b => b.Number == basketId).CountAsync();
if (queryResult > 0)
{
return await this.database.GetWithChildrenAsync<Basket>(basketId, true);
}
else
{
return null;
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return null;
}
}
public async Task<Fruit> GetFruitAsync(string number)
{
try
{
var queryResult = await this.database.Table<Fruit>().Where(f => f.Number == number).CountAsync();
if (queryResult > 0)
{
return await this.database.GetWithChildrenAsync<Fruit>(number, true);
}
else
{
return null;
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return null;
}
}
public async Task<Pip> GetPipAsync(string number)
{
try
{
var queryResult = await this.database.Table<Pip>().Where(p => p.Number == number).CountAsync();
if (queryResult > 0)
{
return await this.database.GetAsync<Pip>(number);
}
else
{
return null;
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return null;
}
}
public async Task StoreBasketAsync(Basket basket)
{
if (basket == null)
return;
try
{
await this.StoreFruitListAsync(basket.FruitList);
var foundItem = await this.GetBasketAsync(basket.Number);
if (foundItem != null)
{
await this.database.UpdateWithChildrenAsync(basket);
}
else
{
await this.database.InsertWithChildrenAsync(basket);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
public async Task StoreFruitListAsync(List<Fruit> fruitList)
{
if (fruitList == null || fruitList.Count == 0)
return;
try
{
foreach (Fruit fruit in fruitList)
{
await this.StorePipAsync(fruit.Pip);
var foundItem = await this.GetFruitAsync(fruit.Number);
if (foundItem != null)
{
await this.database.UpdateWithChildrenAsync(fruit);
}
else
{
await this.database.InsertWithChildrenAsync(fruit);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
public async Task<int> StorePipAsync(Pip pip)
{
if (pip == null)
return 0;
try
{
var foundItem = await this.GetPipAsync(pip.Number);
if (foundItem != null)
{
return await this.database.UpdateAsync(pip);
}
else
{
return await this.database.InsertAsync(pip);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return 0;
}
}
}
测试用例
public MainPage()
{
InitializeComponent();
Pip pip = new Pip();
pip.Number = "4";
pip.Title = "pip from apple";
Fruit apple = new Fruit();
apple.Number = "1";
apple.Pip = pip;
Basket basket = new Basket();
basket.Number = "10";
basket.Name = "grandma";
basket.FruitList = new List<Fruit>() { apple };
this.basket = basket;
}
protected override async void OnAppearing()
{
base.OnAppearing();
await App.Database.StoreBasketAsync(this.basket);
Basket existingBasket = await App.Database.GetBasketAsync(this.basket.Number);
}
我正在使用最新的 SQLiteNetExtensions.Async v2.0.0-alpha2
NuGet 包。如何正确检索子元素 Pip
?
现在我读了documentation,上面写着
Cascade read operations allow you to fetch a complete relationship tree from the database starting at the object that you are fetching and continuing with all the relationships with CascadeOperations
set to CascadeRead
我的 Fruit
class 现在看起来像这样
public class Fruit
{
[OneToOne(CascadeOperations = CascadeOperation.CascadeRead)]
public Pip Pip
{
get { return this.pip; }
set { this.pip = value; }
}
}
并且它按预期工作。我想,只有当我有一个 object
时才需要 CascadeRead
,它有一些进一步的关系,但事实并非如此。您需要 CascadeRead
所有对象,无论对象是如何构建的,都应该递归获取这些对象。
有一个Basket
,存储了一个List<Fruit>
。每个 Fruit
有一个 Pip
。如果我存储此关系并稍后检索它,ForeignKey
PipId
有一个值,但对象 Pip
是 null
,尽管我使用 CascadeRead
.
如果我尝试在 FruitList
上使用 CascadeOperation.All
,我会得到 Constraint
at SQLite.PreparedSqlLiteInsertCommand.ExecuteNonQuery (System.Object[] source) [0x00116] in /Users/fak/Dropbox/Projects/sqlite-net/src/SQLite.cs:2507
at SQLite.SQLiteConnection.Insert (System.Object obj, System.String extra, System.Type objType) [0x0014b] in /Users/fak/Dropbox/Projects/sqlite-net/src/SQLite.cs:1386
at SQLite.SQLiteConnection.Insert (System.Object obj) [0x00008] in /Users/fak/Dropbox/Projects/sqlite-net/src/SQLite.cs:1224
at SQLiteNetExtensions.Extensions.WriteOperations.InsertElement (SQLite.SQLiteConnection conn, System.Object element, System.Boolean replace, System.Reflection.PropertyInfo primaryKeyProperty, System.Boolean isAutoIncrementPrimaryKey, System.Collections.Generic.ISet`1[T] objectCache) [0x0005a] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:270
at SQLiteNetExtensions.Extensions.WriteOperations.InsertElements (SQLite.SQLiteConnection conn, System.Collections.IEnumerable elements, System.Boolean replace, System.Collections.Generic.ISet`1[T] objectCache) [0x00069] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:238
at SQLiteNetExtensions.Extensions.WriteOperations.InsertValue (SQLite.SQLiteConnection conn, System.Object value, System.Boolean replace, System.Boolean recursive, System.Collections.Generic.ISet`1[T] objectCache) [0x0002c] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:219
at SQLiteNetExtensions.Extensions.WriteOperations.InsertChildrenRecursive (SQLite.SQLiteConnection conn, System.Object element, System.Boolean replace, System.Boolean recursive, System.Collections.Generic.ISet`1[T] objectCache) [0x0004c] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:200
at SQLiteNetExtensions.Extensions.WriteOperations.InsertWithChildrenRecursive (SQLite.SQLiteConnection conn, System.Object element, System.Boolean replace, System.Boolean recursive, System.Collections.Generic.ISet`1[T] objectCache) [0x0002b] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:181
at SQLiteNetExtensions.Extensions.WriteOperations.InsertWithChildren (SQLite.SQLiteConnection conn, System.Object element, System.Boolean recursive) [0x00000] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensions\Extensions\WriteOperations.cs:59
at SQLiteNetExtensionsAsync.Extensions.WriteOperations+<>c__DisplayClass1_0.<InsertWithChildrenAsync>b__0 () [0x00013] in C:\home\mk\work\frameworks\sqlite-net-extensions\SQLiteNetExtensionsAsync-PCL\Extensions\WriteOperations.cs:55
at System.Threading.Tasks.Task.InnerInvoke () [0x0000f] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Threading.Tasks.Task.Execute () [0x00010] in <d18287e1d683419a8ec3216fd78947b9>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <d18287e1d683419a8ec3216fd78947b9>:0
at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <d18287e1d683419a8ec3216fd78947b9>:0
at TestSQLite.Database+<StoreBasketAsync>d__5.MoveNext () [0x0021b] in C:\Users\some-user\Documents\Visual Studio 2015\Projects\TestSQLite\TestSQLite\TestSQLite\Database.cs:189
此外,我尝试在InsertWithChildrenAsync()
上使用recursive: true
,但Pip
也是null
。这是示例:
数据模型
public class Basket
{
private string number;
private List<Fruit> fruitList;
[PrimaryKey]
public string Number
{
get { return this.number; }
set { this.number = value; }
}
public string Name { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.CascadeRead)]
public List<Fruit> FruitList
{
get { return this.fruitList; }
set { this.fruitList = value; }
}
public Basket()
{
}
}
public class Fruit
{
private string number;
private Pip pip;
[PrimaryKey]
public string Number
{
get { return this.number; }
set { this.number = value; }
}
public string Type { get; set;}
[ForeignKey(typeof(Pip))]
public string PipId { get; set; }
[OneToOne]
public Pip Pip
{
get { return this.pip; }
set { this.pip = value; }
}
[ForeignKey(typeof(Basket))]
public string BasketId { get; set; }
public Fruit()
{
}
}
public class Pip
{
private string number;
private string title;
[PrimaryKey]
public string Number
{
get { return this.number; }
set { this.number = value; }
}
public string Title
{
get { return this.title; }
set { this.title = value; }
}
public Pip()
{
}
}
数据库操作
public class Database
{
private readonly SQLiteAsyncConnection database;
public Database(string databasePath)
{
this.database = new SQLiteAsyncConnection(databasePath);
this.database.CreateTableAsync<Basket>().Wait();
this.database.CreateTableAsync<Fruit>().Wait();
this.database.CreateTableAsync<Pip>().Wait();
}
public async Task<Basket> GetBasketAsync(string basketId)
{
try
{
var queryResult = await this.database.Table<Basket>().Where(b => b.Number == basketId).CountAsync();
if (queryResult > 0)
{
return await this.database.GetWithChildrenAsync<Basket>(basketId, true);
}
else
{
return null;
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return null;
}
}
public async Task<Fruit> GetFruitAsync(string number)
{
try
{
var queryResult = await this.database.Table<Fruit>().Where(f => f.Number == number).CountAsync();
if (queryResult > 0)
{
return await this.database.GetWithChildrenAsync<Fruit>(number, true);
}
else
{
return null;
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return null;
}
}
public async Task<Pip> GetPipAsync(string number)
{
try
{
var queryResult = await this.database.Table<Pip>().Where(p => p.Number == number).CountAsync();
if (queryResult > 0)
{
return await this.database.GetAsync<Pip>(number);
}
else
{
return null;
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return null;
}
}
public async Task StoreBasketAsync(Basket basket)
{
if (basket == null)
return;
try
{
await this.StoreFruitListAsync(basket.FruitList);
var foundItem = await this.GetBasketAsync(basket.Number);
if (foundItem != null)
{
await this.database.UpdateWithChildrenAsync(basket);
}
else
{
await this.database.InsertWithChildrenAsync(basket);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
public async Task StoreFruitListAsync(List<Fruit> fruitList)
{
if (fruitList == null || fruitList.Count == 0)
return;
try
{
foreach (Fruit fruit in fruitList)
{
await this.StorePipAsync(fruit.Pip);
var foundItem = await this.GetFruitAsync(fruit.Number);
if (foundItem != null)
{
await this.database.UpdateWithChildrenAsync(fruit);
}
else
{
await this.database.InsertWithChildrenAsync(fruit);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
public async Task<int> StorePipAsync(Pip pip)
{
if (pip == null)
return 0;
try
{
var foundItem = await this.GetPipAsync(pip.Number);
if (foundItem != null)
{
return await this.database.UpdateAsync(pip);
}
else
{
return await this.database.InsertAsync(pip);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return 0;
}
}
}
测试用例
public MainPage()
{
InitializeComponent();
Pip pip = new Pip();
pip.Number = "4";
pip.Title = "pip from apple";
Fruit apple = new Fruit();
apple.Number = "1";
apple.Pip = pip;
Basket basket = new Basket();
basket.Number = "10";
basket.Name = "grandma";
basket.FruitList = new List<Fruit>() { apple };
this.basket = basket;
}
protected override async void OnAppearing()
{
base.OnAppearing();
await App.Database.StoreBasketAsync(this.basket);
Basket existingBasket = await App.Database.GetBasketAsync(this.basket.Number);
}
我正在使用最新的 SQLiteNetExtensions.Async v2.0.0-alpha2
NuGet 包。如何正确检索子元素 Pip
?
现在我读了documentation,上面写着
Cascade read operations allow you to fetch a complete relationship tree from the database starting at the object that you are fetching and continuing with all the relationships with
CascadeOperations
set toCascadeRead
我的 Fruit
class 现在看起来像这样
public class Fruit
{
[OneToOne(CascadeOperations = CascadeOperation.CascadeRead)]
public Pip Pip
{
get { return this.pip; }
set { this.pip = value; }
}
}
并且它按预期工作。我想,只有当我有一个 object
时才需要 CascadeRead
,它有一些进一步的关系,但事实并非如此。您需要 CascadeRead
所有对象,无论对象是如何构建的,都应该递归获取这些对象。