C# SQL数据库的多线程处理

C# SQL数据库的多线程处理,c#,sql-server,multithreading,C#,Sql Server,Multithreading,我想为一个程序创建一个用户。如果用户allready存在,我不想使用该用户名创建用户 我有两条线 线程1:处理套接字连接 线程2:处理SQL连接 线程1将用户信息(姓名、电话号码等)发送到线程2。 如果用户allready存在,我希望线程2通知线程1 将用户信息添加到SQL数据库没有问题。我只需要那个通知 public void saveUser(string fullName, string CPR, string password, string kontakt) {

我想为一个程序创建一个用户。如果用户allready存在,我不想使用该用户名创建用户

我有两条线 线程1:处理套接字连接 线程2:处理SQL连接

线程1将用户信息(姓名、电话号码等)发送到线程2。 如果用户allready存在,我希望线程2通知线程1

将用户信息添加到SQL数据库没有问题。我只需要那个通知

public void saveUser(string fullName, string CPR, string password, string kontakt)
       {

           ADBconn.Open();

           SqlCommand cmd = new SqlCommand("INSERT INTO ADBregister (fuldeNavn, password, borger_cprnr, kontaktPersonNummer)" + "VALUES (@fuldeNavn, @password, @borger_cprnr, @kontaktPersonNummer)", ADBconn);

           cmd.Parameters.AddWithValue("@fuldeNavn", fullName);
           cmd.Parameters.AddWithValue("@password", password);
           cmd.Parameters.AddWithValue("@borger_cprnr", CPR);
           cmd.Parameters.AddWithValue("@kontaktPersonNummer", kontakt);

           ADBconn.Close();
       }

不要使用动态SQL。相反,调用一个可以同时执行用户存在性检查和错误处理的进程。例如:

BEGIN TRY

    IF (EXISTS(
                SELECT  *
                FROM    ADBregister -- see note below about NOLOCK
                WHERE   fuldeNavn = @fuldeNavn
                )
        )
    BEGIN
        ;THROW 50005, 'FullName already taken!', 2
    END

    INSERT INTO INTO ADBregister (fuldeNavn, [password], borger_cprnr, kontaktPersonNummer)
    VALUES (@fuldeNavn, @password, @borger_cprnr, @kontaktPersonNummer)

END TRY
BEGIN CATCH

    -- possible additional error handling logic
    ;THROW;
    RETURN

END CATCH
在C代码中,将Execute放在try/catch(SqlException)中其中catch块将查找您在抛出中所做的自定义错误以及更通用的唯一约束冲突错误,该错误将导致此线程成功通过IF EXISTS,同时另一个线程正在提交此处请求的全名。但IF-EXISTS应该能够处理大多数情况

关于NOLOCK的注意事项:
通过在FROM子句中添加“WITH(NOLOCK)”,可以让IF EXISTS捕获在高度事务性系统中以相同毫秒发生的更多情况,但这有两个问题:

  • 这将捕获正在提交的某些条目是的,但也将 通过捕获试图删除的条目来产生误报 提交但由于某些原因(即脏读)被回滚。在里面 在这种情况下,全名在技术上可以作为另一个全名使用 线程未提交
  • 它不会捕获所有实例。没有 捕获所有实例的方法,因此即使您增加了 通过NOLOCK捕获正在使用的条目,您仍然需要捕获 偶尔会发生的唯一约束冲突,因此 未保存任何编码/逻辑
  • 关于多线程的注意事项:

    如果您有一个可以同时连接到数据库的多个进程的系统,则此问题并不特定于多线程。如果您的桌面应用程序使用本地数据库(因此是真正的单用户),那么您不需要这种方法。但是,如果它是一个共享数据库,并且其他人可以尝试同时插入一个新用户,那么即使您的代码使用单个线程,它仍然需要这种方法。即使环境不是高度事务性的,也可能是两个人试图在完全相同的时间做相同的事情,即使这是程序在给定的一天中仅采取的两个操作。

    不要在此处使用try/catch来确定(业务逻辑)。而是创建一个单独的
    方法
    ,该方法接受用户信息并查询数据库以查看用户是否存在,然后返回
    true或false
    。从那里可以执行插入


    因为线程1似乎正在处理单个用户的信息,所以不要为了执行SQL验证/插入例程而派生另一个线程。没必要

    请注意,您在
    catch
    块中有
    UserSaved(false)
    ,但在
    finally
    块中也有
    UserSaved(true)
    ,这意味着两者都将被调用。似乎不是故意的,或者至少不清楚,这不是故意的。“UserSaved”是我试图通知另一个“线程”的事件。我不确定“catch”块是否获得数据库中存在用户allready的异常。“finally”块可能被删除。线程1是您的主线程吗?如果你想让线程1'等待线程2',那么你真的需要线程2吗?@StudyTowel我需要两个线程是的。线程2正在另一个解决方案中工作。请尝试进一步了解您的需求。如果用户已经存在,这是一件坏事吗?因此有例外吗?或者您只是将异常用作业务逻辑的一部分?你能用更多的要求更新你的问题吗?在许多情况下,简单地尝试
    插入
    并捕获任何由此产生的错误更容易、更有效。这在很大程度上取决于预计发生冲突的频率。@HABO,只有在冲突率较低的情况下,才会有大量对proc的调用。在这种情况下,可以使用WITH(NOLOCK)来减少SELECT所使用的锁。但总的来说,我发现不依赖错误是一种更好的做法,尤其是当您无法轻松预测用户输入数据的冲突率时。