停止使用DB

时间:2015-07-16 16:10:44

标签: c# mysql multithreading

我正在处理自动完成脚本。

当用户停止输入时,他的输入会发送到服务器,该服务器将在DB中查找一些匹配项。

例如:如果寻找人“Obam”应该返回“Barack Obama”。

我希望此搜索限制在~500ms。如果需要更长时间,我想中止搜索,只返回及时发现的结果。

我首先寻找一个完美的匹配(这个不会被打断),然后我正在寻找一个部分匹配(这个可以被打断):

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 30s

    bool 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;
    }
    

目标是快速获得结果(不一定全部)。

问题

  • 我在conn.Close();行〜30秒后随机获得Timeoutexception。
  • 我在Cmd.ExecuteReader();的第一次FindOtherBrands来电中随机获取NullReferenceException。

任何人都可以解释为什么吗? 我做错了什么还是有解决方法?

我想TimeoutException是因为我试图在命令执行期间关闭连接,我可以删除/取消该查询吗?

1 个答案:

答案 0 :(得分:1)

我会采取不同的方法。由于您要查询自然异步的数据库,因此可以使用async-await来查询数据。有了它,你可以传递一个设置为超时的CancellationToken,你每次读取都会监视它:

例如:

private async Task FindOtherBrands(string term, 
                                   Dictionary<int, Scoring> results, 
                                   CancellationToken cancellationToken)
{
    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 = await cmd.ExecuteReaderAsync();
    while (Rd != null && await Rd.ReadAsync())
    {
        cancellationToken.ThrowIfCancellationRequested();

        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 = await Cmd.ExecuteReaderAsync();
    while (Rd != null && await Rd.ReadAsync())
    {
        cancellationToken.ThrowIfCancellationRequest();
        int id = int.Parse(Rd["id"].ToString());
        if (!results.ContainsKey(id))
        {
            results.Add(id, new Scoring()
            {
                score = 10,
                value = Rd["label"].ToString()
            });
        }
    }
    Rd.Close();
}

当你调用它时,你只需要将它包装在try-catch中并传递CancellationToken

private async Task<bool> RunWithTimeoutAsync(TimeSpan timeout)
{
    bool finished;
    try
    {
        var cancellationTokenSource = new CancellationTokenSource(timeout);
        await FindOtherBrandsAsnyc(term, 
              results, 
              cancellationTokenSource.CancellationToken);
        finished = true;
    }
    catch (OperationCanceledException e)
    {
        // Handle
    }

    return finished;
}

旁注 - 您的查询很容易出现SQL注入。你不应该使用字符串连接。改为使用查询参数。