Я работаю над сценарием автозаполнения.Остановить поток, работающий с DB
Когда пользователь перестает печатать, его ввод отправляется на сервер, который будет искать некоторые совпадения в БД.
Например: При поиске людей «Обам» должен вернуться «Барак Обама».
Я хочу, чтобы этот поиск ограничивался ~ 500 мс. Если это займет больше времени, я хочу прервать поиск и вернуть результаты только вовремя.
Я начинаю с поиска идеального соответствия (это не будет прервана), то я ищу частичного совпадения (это один может быть прерван):
private static MySqlConnection conn = new MySqlConnection(ConfigurationManager.AppSettings["CxMySql"].ToString());
protected void Page_Load(object sender, EventArgs e)
{
conn.Open();
SearchTerm(table,term,domaine);
conn.Close(); // TimeoutException HERE
conn.Dispose();
}
private void SearchTerm(string table,string term,string domaine)
{
Dictionary<int, Scoring> results = new Dictionary<int, Scoring>();
var requete = "SELECT m.id, m.nom as label FROM marque m WHERE m.nom = '" + term + "'";
var Cmd = new MySqlCommand(requete, conn);
using (var Rd = Cmd.ExecuteReader())
{
while (Rd.Read())
{
results.Add(int.Parse(Rd["id"].ToString()), new Scoring()
{
score = 1000,
value = Rd["label"].ToString()
});
}
}
// Here it should be 500, but I put 1 to force troubles to appear.
RunWithTimeout(() => FindOtherBrands(term, ref results), TimeSpan.FromMilliseconds(1));
var resultsList = results.ToList();
resultsList.Sort(
delegate(KeyValuePair<int, Scoring> firstPair,
KeyValuePair<int, Scoring> nextPair)
{
return nextPair.Value.score - firstPair.Value.score;
}
);
}
private void FindOtherBrands(string term, ref Dictionary<int, Scoring> results)
{
MySqlCommand Cmd;
string requete;
requete = "SELECT m.id, m.nom as label FROM marque m WHERE m.nom LIKE '" + term + "%'";
Cmd = new MySqlCommand(requete, conn);
var Rd = Cmd.ExecuteReader(); // NullReferenceException HERE
while (Rd != null && Rd.Read())
{
int id = int.Parse(Rd["id"].ToString());
if (!results.ContainsKey(id))
{
results.Add(id, new Scoring()
{
score = 100,
value = Rd["label"].ToString()
});
}
}
Rd.Close();
requete = "SELECT m.id, m.nom as label FROM marque m WHERE m.nom LIKE '%" + term + "%'";
Cmd = new MySqlCommand(requete, conn);
Rd = Cmd.ExecuteReader();
while (Rd != null && Rd.Read())
{
int id = int.Parse(Rd["id"].ToString());
if (!results.ContainsKey(id))
{
results.Add(id, new Scoring()
{
score = 10,
value = Rd["label"].ToString()
});
}
}
Rd.Close();
}
Я нашел
RunWithTimeout
метод здесь: stop executing code in thread after 30sbool RunWithTimeout(ThreadStart threadStart, TimeSpan timeout) { Thread workerThread = new Thread(threadStart); workerThread.Start(); bool finished = true; if (!workerThread.Join(timeout)) { workerThread.Abort(); finished = false; } return finished; }
Скоринг является структурой для легкой сортировки результатов
private struct Scoring { public string value; public int score; }
Цель состоит в том, чтобы иметь результаты (не обязательно все) быстро.
ПРОБЛЕМЫ- я случайно получить TimeoutException после ~ 30s на
conn.Close();
линии. - Я случайно получаю NullReferenceException при первом
Cmd.ExecuteReader();
звоните вFindOtherBrands
.
Может ли кто-нибудь объяснить мне, почему? Я делаю что-то неправильно или есть обходной путь?
Я думаю, что исключение TimeoutException связано с тем, что я пытаюсь закрыть соединение во время выполнения команды, могу ли я отбросить/отменить этот запрос?
С какой версией .NET вы работаете? –
Было бы разумно закрыть ресурсы базы данных, прежде чем прерывать поток. – HashPsi
К сожалению, в базах данных нет способа сказать все результаты, которые вы можете получить в течение определенного периода времени.Вы можете запросить все результаты, но нет гарантии, что база данных начнет отправлять их вам быстро, как только они их обнаружат. Единственный верный способ сделать это - запросить n-й результат в цикле и установить тайм-аут команды. Это крайне неэффективно. –