我正在开发一个包含许多互连表的字典应用程序,因此,当用户进行查询时,该应用程序将搜索几乎所有这些表以及它们的许多字段,这将导致对数据库的大量查询和UI冻结。 / p>
那么,每次使用结果更新UI时,如何在一个单独的任务中异步进行这些查询?
我见过针对该领域的android版本的findAllAsync之类的东西,但是对于.net,我找不到任何替代方案,每次尝试运行异步时,我都尝试按照数据库其他地方的建议重新初始化,但是不知何故,工作,给我同样的错误。
System.Exception: 'Realm accessed from incorrect thread.'
当我尝试将领域结果转换为普通列表以在UI上处理时,错误会抛出ToList(),请帮助解决此问题
这是我的代码
using Data.Models;
using Microsoft.EntityFrameworkCore.Internal;
using Realms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data
{
public class RealmAsync
{
IQueryable<Entry> Entries { get; set; }
public async void Run()
{
Realm RealmDatabase = Realm.GetInstance();
Entries = RealmDatabase.All<Entry>();
var entries = await FindAsync("а", Entries);
Console.WriteLine($"async result async {entries.Count()}");
}
public async Task<IEnumerable<Entry>> FindAsync(string needle, IQueryable<Entry> haystack)
{
var foregroundRealm = Realm.GetInstance();
var haystackRef = ThreadSafeReference.Create<Entry>(haystack);
var filteredEntriesRef = await Task.Run(() =>
{
using (var realm = Realm.GetInstance())
{
var bgHaystack = realm.ResolveReference(haystackRef);
return ThreadSafeReference.Create(bgHaystack.Where(entry => entry.Content.ToLower().StartsWith(needle)));
}
});
var result = foregroundRealm.ResolveReference(filteredEntriesRef).ToArray();
return result;
}
}
条目模型类:
using System.ComponentModel.DataAnnotations.Schema;
using Realms;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System;
namespace Data.Models
{
[Table("entry")]
public class Entry : RealmObject
{
public class EntryType
{
public const byte Word = 1;
public const byte Phrase = 2;
public const byte Text = 3;
};
[Key]
[PrimaryKey]
[Column("entry_id")]
public int Id { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("source_id")]
public int SourceId { get; set; }
[Indexed]
[Column("type")]
public byte Type { get; set; }
[Column("rate")]
public int Rate { get; set; }
[Column("created_at")]
public string CreatedAt { get; set; }
[Column("updated_at")]
public string UpdatedAt { get; set; }
[NotMapped]
public Phrase Phrase { get; set; }
[NotMapped]
public Word Word { get; set; }
[NotMapped]
public Text Text { get; set; }
[NotMapped]
public IList<Translation> Translations { get; }
[NotMapped]
public string Content
{
get {
switch (Type)
{
case EntryType.Phrase:
return Phrase?.Content;
case EntryType.Word:
return Word?.Content;
case EntryType.Text:
return Text?.Content;
}
return "";
}
}
}
}
答案 0 :(得分:1)
因此,如果有人需要此功能,这就是我最终实现了效果很好的真实实现的方式(这不是问题中的示例代码,但我认为这可能更有用和实用)-让我知道是否您将需要有关它的更多信息:
public async override Task<List<Entry>> FindAsync(string inputText)
{
// Checks for previous results to speed up the search while typing
// For realm to be able to work cross-thread, when having previous results this has to be passed to the search task
ThreadSafeReference.Query<Entry> previousResultsRef = null;
if (PreviousInputText != null)
{
if ((PreviousInputText.Length == inputText.Length - 1) && (inputText == PreviousInputText + inputText.Last()))
{
// Characters are being inserted
if (PreviousResults.ContainsKey(PreviousInputText.Length - 1))
{
previousResultsRef = ThreadSafeReference.Create(PreviousResults[PreviousInputText.Length - 1]);
}
} else if ((PreviousInputText.Length == inputText.Length + 1) && (inputText == PreviousInputText.Substring(0, PreviousInputText.Length - 1)))
{
// Characters are being removed
PreviousResults[PreviousInputText.Length] = null;
PreviousInputText = inputText;
return PreviousResults[PreviousInputText.Length - 1].ToList();
}
}
// Receives reference to the search results from the dedicated task
var resultingEntries = await Task.Run(() =>
{
// Preserves current input text for the further uses
PreviousInputText = inputText;
// Gets the source entries - either previous or all
var sourceEntries = (previousResultsRef == null) ? Realm.GetInstance(DbPath).All<Entry>() : Realm.GetInstance(DbPath).ResolveReference(previousResultsRef);
// Because realm doesn't support some of the LINQ operations on not stored fields (Content)
// the set of entries @sourceEntries is assigned to a IEnumerable through conversion when passing to Search method as a parameter
IEnumerable<Entry> foundEntries = Search(inputText, sourceEntries);
// Extracts ids
var foundEntryIds = ExtractIdsFromEntries(foundEntries);
if (foundEntryIds.Count() == 0)
{
return null;
}
// Select entries
var filteredEntries = FilterEntriesByIds(sourceEntries, foundEntryIds.ToArray());
if (filteredEntries == null)
{
// Something went wrong
return null;
}
// Creates reference for the main thread
ThreadSafeReference.Query<Entry> filteredEntriesRef = ThreadSafeReference.Create(filteredEntries);
return filteredEntriesRef;
});
if (resultingEntries == null)
{
// Next search will be a new search
PreviousResults[inputText.Length - 1] = null;
return new List<Entry>();
}
var results = RealmDatabase.ResolveReference(resultingEntries);
// Preserve the current results and the input text for the further uses
PreviousInputText = inputText;
if (PreviousResults.ContainsKey(inputText.Length - 1))
{
PreviousResults[inputText.Length - 1] = results;
} else
{
PreviousResults.Add(inputText.Length - 1, results);
}
return Sort(results, inputText).ToList();
}
这是一种在键入时进行异步搜索的方法,这就是为什么有PreviousInputText
和PreviousResults
来避免多余查询的原因
答案 1 :(得分:1)
在文档的Threading部分中,内容为:
Realm,RealmObject和IQueryable的持久化实例从 RealmObjects的Realm.All或IList属性只能在 在其上创建它们的线程,否则将引发异常
在您的代码中,您正在Task.Run
内创建Realm实例,但是在返回结果后尝试使用结果。除了返回包含RealmObject实例的列表之外,您还可以向正在执行的查询返回ThreadSafeReference并在原始线程上对其进行解析:
public async Task<IEnumerable<Entry>> FindAsync(string needle, IQueryable<Entry> haystack)
{
var foregroundRealm = Realm.GetInstance();
var haystackRef = haystack;
var filteredEntriesRef = await Task.Run(() =>
{
using (var realm = Realm.GetInstance())
{
var bgHaystack = realm.ResolveReference(haystackRef);
return ThreadSafeReference.Create(bgHaystack.Where(entry => entry.Content.ToLower().StartsWith(needle)));
}
});
return foregroundRealm.ResolveReference(filteredEntriesRef).ToArray();
}
答案 2 :(得分:0)
请用以下代码替换您的代码行public async void Run()
public async Task Run()
仅当将函数异步触发并且忘记方法时,才应将其标记为异步无效,如果要等待它,则应将其标记为异步任务,而不是异步无效。