BackgroundWorker выдает ошибку при выполнении - PullRequest
0 голосов
/ 20 октября 2019

В приложении WPF у меня есть запрос SQL, выполнение которого занимает несколько раз, и я хочу реализовать BackgroundWorker с индикатором выполнения, чтобы пользователь мог видеть, когда он завершится.

Вот часть XAML:

<Grid Grid.Row="2">
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <ProgressBar x:Name="BgProgBar" Margin="5"/>
    <TextBlock x:Name="Perc_TB" HorizontalAlignment="Center"
               VerticalAlignment="Center"
               Text="{Binding Path=Value,ElementName=BgProgBar}"/>
</Grid>

и вот мой BackgroundWorker в коде:

  public partial class Supp_Stocks : Page
  {
    private string _user = Settings.Default.User;
    private DataTable dt;
    SqlConnection conn;
    SqlCommand comm;
    SqlConnectionStringBuilder connStringBuilder;
    BackgroundWorker Bg = new BackgroundWorker();
    public Supp_Stocks()
    {
        InitializeComponent();
        ConnectToDB();
        Bg.DoWork += Bg_DoWork;
        Bg.ProgressChanged += Bg_ProgressChanged;
        Bg.WorkerReportsProgress = true;

    }

    #region BackGroundWorker
    private void Bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        BgProgBar.Value = e.ProgressPercentage;
    }

    private void Bg_DoWork(object sender, DoWorkEventArgs e)
    {
        for(int i = 0; i < 100; i++)
        {
            try
            {
                comm = new SqlCommand("select STOCK_ENT.ENT_ID,ENT_DATE_ENT,ENT_NUMPAL,CLI_NOM,ART_LIBELLE1,ENT_PICKING,SUM(det_pnet)" +
                " as POIDS_NET from STOCK_DET, STOCK_ENT, CLIENTS, FICHES_ARTICLES, MVTS_SEQUENCE where SEQ_STATUT <> 'V' and" +
                " STOCK_ENT.ENT_ID = STOCK_DET.ENT_ID and STOCK_ENT.ENT_PROP = CLI_CODE and STOCK_ENT.ART_CODE = FICHES_ARTICLES.ART_CODE" +
                " and STOCK_ENT.ENT_ID = MVTS_SEQUENCE.ENT_ID group by STOCK_ENT.ENT_ID, ENT_DATE_ENT, ENT_NUMPAL, CLI_NOM, ART_LIBELLE1, ENT_PICKING order by ART_LIBELLE1", conn);
                SqlDataAdapter dap = new SqlDataAdapter(comm);
                dt = new DataTable();
                dap.Fill(dt);
                Stocks_DT.ItemsSource = dt.DefaultView;
                Bg.ReportProgress(i);
            }
            catch (Exception ex)
            {

                var stList = ex.StackTrace.ToString().Split('\\');
                Messages.ErrorMessages($"ERREUR : \n{ex.Message}\n\nException at : \n{stList[stList.Count() - 1]}");
            }

        }
    }
    private void GenerateStk_Btn_Click(object sender, RoutedEventArgs e)
    {
        Bg.RunWorkerAsync();
    }
    #endregion

    private void ConnectToDB()
    {
        connStringBuilder = new SqlConnectionStringBuilder
        {
            DataSource = @"VM-VISUALSTORE\SQLEXPRESS,1433",
            InitialCatalog = "GSUITE",
            Encrypt = true,
            TrustServerCertificate = true,
            ConnectTimeout = 30,
            AsynchronousProcessing = true,
            MultipleActiveResultSets = true,
            IntegratedSecurity = true,
        };
        conn = new SqlConnection(connStringBuilder.ToString());
        comm = conn.CreateCommand();
    }
}

, но при выполнении кода он возвращает ошибку:

Error Message

Извините, но по-французски, но он говорит, что "Поток не может получить доступ к этому объекту, потому что другой поток владеет им.

1 Ответ

1 голос
/ 20 октября 2019

Вы не сказали, какая строка в вашем коде выдает ошибку, но я почти уверен, что именно эта, верно?

Stocks_DT.ItemsSource = dt.DefaultView;

И Stocks_DT - это элемент управления в вашем окне, верно?

Это источник вашей проблемы. Как говорится в сообщении об ошибке, вы можете получить доступ только к элементам пользовательского интерфейса из потока пользовательского интерфейса (основного потока). Поскольку вы выполняете указанный выше код в фоновом потоке, он выдает это исключение.

Вместо этого вам необходимо передать результаты запроса обратно в основной поток , а затем назначить егоStocks_DT.ItemsSource. Я бы рекомендовал использовать свойство DoWorkEventArgs.Result. Я бы предложил использовать DataTable в качестве результата, чтобы код был e.Result = dt. Вы можете получить доступ к Result из события BackgroundWorker.RunWorkerCompleted.


Вышесказанное разрешит исключение, но я считаю, что есть ряд других вещей, которые я должен рассмотреть в вашем коде, просто взаинтересованность в том, чтобы помочь вам.

Прежде всего, вы, кажется, повторяете один и тот же точный запрос 100 раз без каких-либо изменений. Похоже, это было бы пустой тратой времени, так как вы бы получили один и тот же результат 100 раз. Если это так, вам, вероятно, вообще не нужен индикатор выполнения.

Во-вторых, обычно рекомендуется открывать новый SqlConnection, когда он нужен, и закрывать его, когда он закончен,вместо того, чтобы держать одно соединение открытым в течение всего времени существования программы / окна. Это особенно верно, когда вы вступаете в многопоточность. Вы можете найти больше информации в этом вопросе . При создании этих соединений они должны быть заключены в оператор using.

В-третьих, C # поддерживает многострочные строки, как показано в этого вопроса . Использование этого метода сделает запрос SQL более читабельным, избавившись от всех "..." + "..."

...