1

I am trying to fill my SQL Server database with 3 different .txt files I accomplished this using regular for but it takes a long time. Then since the order does not matter (in my database) I tried to use Parallel.ForEach but I get this error error

Here is my code

using System;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Data;

namespace LogwithDataBase
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        SqlConnection con = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=LogsUploaded;Integrated Security=SSPI");

        void filldata()
        {
            SqlDataAdapter da = new SqlDataAdapter("Select *From LogsTable", con);
            DataSet ds = new DataSet();
            con.Open();

            da.Fill(ds, "LogsTable");

            dataGridView1.DataSource = ds.Tables["LogsTable"];
            dataGridView1.Columns[1].DefaultCellStyle.Format = "yyyy-MM-dd HH:mm:ss.fff";
            con.Close();
        }

        private void datab_connect_Click(object sender, EventArgs e)
        {
            con.Open();
            IEnumerable<string> lines1 = File.ReadLines(openFileDialog1.FileName); //HWLog

            Parallel.ForEach(lines1, line1 =>
            {
                string timeStmp = line1.Split('\t')[0];
                string atmIdd = line1.Split('\t')[1];
                string fileTyp = "HWLog";
                string evenTex = line1.Split('\t')[2];
                string query = "INSERT INTO LogsTable (timestampof_log,atmID,fileType,eventText) values (@timeStmp, @atmIdd, @fileTyp, @evenTex)";
                SqlCommand cmd = new SqlCommand(query, con);
                cmd.Parameters.AddWithValue("@timeStmp", DateTime.Parse(timeStmp));
                cmd.Parameters.AddWithValue("@atmIdd", atmIdd);
                cmd.Parameters.AddWithValue("@fileTyp", fileTyp);
                cmd.Parameters.AddWithValue("@evenTex", evenTex);
                cmd.ExecuteNonQuery();
            });

            IEnumerable<string> lines2 = File.ReadLines(openFileDialog2.FileName); //SWLog
            Parallel.ForEach(lines2, line2 =>
            {
                string timeStmp = line2.Split('\t')[0];
                string atmIdd = line2.Split('\t')[1];
                string fileTyp = "SWLog";
                string evenTex = line2.Split('\t')[2];
                string query = "INSERT INTO LogsTable (timestampof_log,atmID,fileType,eventText) values (@timeStmp, @atmIdd, @fileTyp, @evenTex)";
                SqlCommand cmd = new SqlCommand(query, con);
                cmd.Parameters.AddWithValue("@timeStmp", DateTime.Parse(timeStmp));
                cmd.Parameters.AddWithValue("@atmIdd", atmIdd);
                cmd.Parameters.AddWithValue("@fileTyp", fileTyp);
                cmd.Parameters.AddWithValue("@evenTex", evenTex);
                cmd.ExecuteNonQuery();
            });

            IEnumerable<string> lines3 = File.ReadLines(openFileDialog3.FileName); //JournalLog
            Parallel.ForEach(lines3, line3 =>
            {
                string timeStmp = line3.Split('\t')[0];
                string atmIdd = line3.Split('\t')[1];
                string fileTyp = "JournalLog";
                string evenTex = line3.Split('\t')[2];
                string query = "INSERT INTO LogsTable (timestampof_log, atmID, fileType, eventText) VALUES (@timeStmp, @atmIdd, @fileTyp, @evenTex)";

                SqlCommand cmd = new SqlCommand(query, con);

                cmd.Parameters.AddWithValue("@timeStmp", DateTime.Parse(timeStmp));
                cmd.Parameters.AddWithValue("@atmIdd", atmIdd);
                cmd.Parameters.AddWithValue("@fileTyp", fileTyp);
                cmd.Parameters.AddWithValue("@evenTex", evenTex);

                cmd.ExecuteNonQuery();
            });

            string querycb = "SELECT DISTINCT fileType FROM LogsTable"; //COMBOBOXA DATA ÇEKMEK
            SqlCommand cmdcb = new SqlCommand(querycb, con); //COMBOBOXA DATA ÇEKMEK
            SqlDataReader drcb = cmdcb.ExecuteReader(); //COMBOBOXA DATA ÇEKMEK
            List<string> list = new List<string>(); //COMBOBOXA DATA ÇEKMEK
            list.Add(""); //COMBOBOXA DATA ÇEKMEK

            while (drcb.Read())
            {
                list.Add(drcb["fileType"].ToString());
            }

            drcb.Close();

            comboBox1.DataSource = list; //COMBOBOXA DATA ÇEKMEK
            con.Close();

            filldata();
        }
    }
}    

I couldn't solve this problem and I don't want to go back to regular for

3
  • You do understand what "IO bound" means, right? Commented Aug 11, 2022 at 5:22
  • 3
    You should not be calling ExecuteNonQuery in a loop to begine with, especially creating a new command object every time. You ought to be populating a DataTable and then saving it in a single batch using a SqlDataAdapter or even SqlBulkCopy. Commented Aug 11, 2022 at 5:24
  • I have an opinion, is it because execute query should be completed before paralleling lines ? If its like this how do we make this parallel or how can I do this faster ? Commented Aug 11, 2022 at 5:25

2 Answers 2

2

In terms of performance i would suggest to use sqlbulkcopy Just fill a DataTable with all the lines and insert all at once, its much faster than make a single query for each line in the file thant could be really really long.

    public static void BulkInsert(IEnumerable<string> lines)
    {
        DataTable dt = new DataTable();
        dt.TableName = "LogsTable";
        
        dt.Columns.Add("timestampof_log");
        dt.Columns.Add("atmID");
        dt.Columns.Add("fileType");
        dt.Columns.Add("eventText");

        foreach (var line in lines)
        {
            string timeStmp = line.Split('\t')[0];
            string atmIdd = line.Split('\t')[1];
            string fileTyp = "JournalLog";
            string evenTex = line.Split('\t')[2];

            dt.Rows.Add(timeStmp, atmIdd, fileTyp, evenTex);

        }

        BulkInsert(dt);
    }

    public static void BulkInsert(DataTable table)
    {
        using (var bulkInsert = new SqlBulkCopy(ConnectionString))
        {
            bulkInsert.DestinationTableName = table.TableName;
            bulkInsert.WriteToServer(table);
        }
    }
Sign up to request clarification or add additional context in comments.

9 Comments

The only caveat which you should point out in your answer, is that SqlBulkCopy does not cause triggers to fire. If the table has triggers that need to be fired, then the solution would be a User Defined Table Type
@JonathanWillcock what you mean by triggers that need to fired? Is it like my filldata() I use it in every button click but I use it at the end
Have a look trigger
@Hytac I believe this method copies data from another datatable/dataset but I have an .txt file its not a data set I have a log file
@U.DenizA. I have edited my answer, check it now.
|
1

Simplest way - open sql connection in each parallel thread.

Parallel.Foreach SQL querying sometimes results in Connection more detailed here

6 Comments

Databasses are IO-bound, not CPU-bound - using additional threads won't be faster - and in-fact will likely slow things down.
Depends of size and and database implementation. Some times all needed data already cached in memory.
When I do that it gives this error " System.InvalidCastException: 'Unable to cast object of type 'System.Data.ProviderBase.DbConnectionClosedConnecting' to type 'System.Data.SqlClient.SqlInternalConnectionTds'.' " Original code is ' ` Parallel.ForEach(lines1, line1 => { con.Open(); same as before con.Close(); });'`
you have to create and open connection in each thread, not just open.
@YuriMyakotin now it says timeout period expired maximum pool connection
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.