C#ASP.Net处理问题 - 删除行时读表

时间:2016-01-25 19:13:47

标签: c# sql-server-2012 locking

简单地说,我有一个应用程序,它有一个页面可以删除,然后每隔30秒将记录重新添加/刷新到一个表中。我有另一个页面,每45秒运行一次,读取表数据并构建图表。

问题是,在读取/查看页面中,每隔一段时间我得到一个0值(从最大计数),图表什么都没有显示。我有一种感觉,这种情况正在发生,因为删除页面删除了表中的所有记录但尚未刷新/重新添加它们的同时完成了读取。

我的应用程序中是否有一种方法可以在刷新表时暂缓读取?

最诚挚的问候, 安迪

  • C#
  • ASP.Net 4.5
  • SQL Server 2012

下面的代码是在ASP.Net 4.5内置的Windows服务中运行的。它删除ActualPlot表中的所有记录,然后每30秒从文本文件刷新/添加新记录。我基本上需要阻止(锁定?)任何用户在删除和刷新记录时读取ActualPlot表。你能帮我改变我的代码吗?

    private void timer1_Tick(object sender, ElapsedEventArgs e)
    {
        // Open the SAP text files, clear the data in the tables and repopulate the new SAP data into the tables.

        var cnnString = ConfigurationManager.ConnectionStrings["TaktBoardsConnectionString"].ConnectionString;
        SqlConnection conn = new SqlConnection(cnnString);
        SqlConnection conndetail = new SqlConnection(cnnString);
        SqlConnection connEdit = new SqlConnection(cnnString);
        SqlCommand cmdGetProductFile = new SqlCommand();
        SqlDataReader reader;
        string sql;

        // Delete all the records from the ActualPlot and the ActualPlotPreload tables.  We are going to repopulate them with the data from the text file.

        sql = "DELETE FROM ActualPlotPreload";
        try
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn);
            cmd.ExecuteNonQuery();
        }
        catch (System.Data.SqlClient.SqlException ex)
        {
            string msg = "Delete Error:";
            msg += ex.Message;
            Library.WriteErrorLog(msg);

        }
        finally
        {
            conn.Close();
        }


        sql = "DELETE FROM ActualPlot";
        try
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn);
            cmd.ExecuteNonQuery();
        }
        catch (System.Data.SqlClient.SqlException ex)
        {
            string msg = "Delete Error:";
            msg += ex.Message;
            Library.WriteErrorLog(msg);
        }
        finally
        {
            conn.Close();
        }

        // Read the SAP text file and load the data into the ActualPlotPreload table

        sql = "SELECT DISTINCT [BoardName], [ProductFile], [ProductFileIdent] FROM [TaktBoards].[dbo].[TaktBoard] ";
        sql = sql + "JOIN [TaktBoards].[dbo].[Product] ON [Product].[ProductID] = [TaktBoard].[ProductID]";

        cmdGetProductFile.CommandText = sql;
        cmdGetProductFile.CommandType = CommandType.Text;
        cmdGetProductFile.Connection = conn;

        conn.Open();
        reader = cmdGetProductFile.ExecuteReader();

        string DBProductFile = "";
        string DBTischID = "";
        string filepath = "";

        string[] cellvalues;

        DateTime dt, DateCheckNotMidnightShift;
        DateTime ldSAPFileLastMod = DateTime.Now;
        string MyDateString;
        int FileRecordCount = 1;

        while (reader.Read())
        {
            DBProductFile = (string)reader["ProductFile"];
            DBTischID = (string)reader["ProductFileIdent"];

            filepath = "c:\\inetpub\\wwwroot\\WebApps\\TaktBoard\\FilesFromSAP\\" + DBProductFile;
            FileInfo fileInfo = new FileInfo(filepath); // Open file
            ldSAPFileLastMod = fileInfo.LastWriteTime; // Get last time modified

            try
            {
                StreamReader sr = new StreamReader(filepath);

                FileRecordCount = 1;

                // Populate the AcutalPlotPreload table from with the dates from the SAP text file.

                sql = "INSERT into ActualPlotPreload (ActualDate, TischID) values (@ActualDate, @TischID)";
                while (!sr.EndOfStream)
                {
                    cellvalues = sr.ReadLine().Split(';');

                    if (FileRecordCount > 1 & cellvalues[7] != "")
                    {
                        MyDateString = cellvalues[7];
                        DateTime ldDateCheck = DateTime.ParseExact(MyDateString, "M/dd/yyyy", null);

                        DateTime dateNow = DateTime.Now;
                        string lsDateString = dateNow.Month + "/" + dateNow.Day.ToString("d2") + "/" + dateNow.Year;

                        DateTime ldCurrentDate = DateTime.ParseExact(lsDateString, "M/dd/yyyy", null);
                        string lsTischID = cellvalues[119];

                        if (ldDateCheck == ldCurrentDate)
                        {
                            try
                            {
                                conndetail.Open();
                                SqlCommand cmd = new SqlCommand(sql, conndetail);
                                cmd.Parameters.Add("@ActualDate", SqlDbType.DateTime);
                                cmd.Parameters.Add("@TischID", SqlDbType.VarChar);
                                cmd.Parameters["@TischID"].Value = cellvalues[119];

                                MyDateString = cellvalues[7] + " " + cellvalues[55];
                                dt = DateTime.ParseExact(MyDateString, "M/dd/yyyy H:mm:ss", null);

                                cmd.Parameters["@ActualDate"].Value = dt;

                                // Ignore any midnight shift (12am to 3/4am) units built.

                                DateCheckNotMidnightShift = DateTime.ParseExact(cellvalues[7] + " 6:00:00", "M/dd/yyyy H:mm:ss", null);
                                if (dt >= DateCheckNotMidnightShift)
                                {
                                    cmd.ExecuteNonQuery();
                                }

                            }
                            catch (System.Data.SqlClient.SqlException ex)
                            {
                                string msg = "Insert Error:";
                                msg += ex.Message;
                                Library.WriteErrorLog(msg);
                            }
                            finally
                            {
                                conndetail.Close();
                            }
                        }
                    }

                    FileRecordCount++;

                }
                sr.Close();

            }


            catch
            { }


            finally
            { }



        }

        conn.Close();


        // Get the unique TischID's and ActualDate from the ActualPlotPreload table.  Then loop through each one, adding the ActualUnits
        // AcutalDate and TischID to the ActualPlot table.  For each unique TischID we make sure that we reset the liTargetUnits to 1 and 
        // count up as we insert.

        SqlCommand cmdGetTischID = new SqlCommand();
        SqlDataReader readerTischID;
        int liTargetUnits = 0;
        string sqlInsert = "INSERT into ActualPlot (ActualUnits, ActualDate, TischID) values (@ActualUnits, @ActualDate, @TischID)";

        sql = "SELECT DISTINCT [ActualDate], [TischID] FROM [TaktBoards].[dbo].[ActualPlotPreload] ORDER BY [TischID], [ActualDate] ASC ";

        cmdGetTischID.CommandText = sql;
        cmdGetTischID.CommandType = CommandType.Text;
        cmdGetTischID.Connection = conn;

        conn.Open();
        readerTischID = cmdGetTischID.ExecuteReader();

        DBTischID = "";
        DateTime DBActualDate;
        string DBTischIDInitial = "";

        while (readerTischID.Read())
        {
            DBTischID = (string)readerTischID["TischID"];
            DBActualDate = (DateTime)readerTischID["ActualDate"];

            if (DBTischIDInitial != DBTischID)
            {
                liTargetUnits = 1;
                DBTischIDInitial = DBTischID;
            }
            else
            {
                liTargetUnits++;
            }


            try
            {
                conndetail.Open();
                SqlCommand cmd = new SqlCommand(sqlInsert, conndetail);
                cmd.Parameters.Add("@ActualUnits", SqlDbType.Real);
                cmd.Parameters.Add("@ActualDate", SqlDbType.DateTime);
                cmd.Parameters.Add("@TischID", SqlDbType.VarChar);

                cmd.Parameters["@TischID"].Value = DBTischID;
                cmd.Parameters["@ActualDate"].Value = DBActualDate;
                cmd.Parameters["@ActualUnits"].Value = liTargetUnits;
                cmd.ExecuteNonQuery();
                cmd.Parameters.Clear();

            }
            catch (System.Data.SqlClient.SqlException ex)
            {
                string msg = "Insert Error:";
                msg += ex.Message;
                Library.WriteErrorLog(msg);
            }
            finally
            {
                conndetail.Close();
            }
        }

        conn.Close();

        Library.WriteErrorLog("SAP text file data has been imported.");
    }

4 个答案:

答案 0 :(得分:1)

如果在删除之后立即重新添加数据(基本上你知道在清空表之前要重新添加什么),你可以在同一个SQL事务中同时进行这两个操作,这样数据就可以用到仅在重新添加时才显示其他页面。

我的意思是这样的:

public bool DeleteAndAddData(string connString)
    {
        using (OleDbConnection conn = new OleDbConnection(connString))
        {
            OleDbTransaction tran = null;

            try
            {
                conn.Open();
                tran = conn.BeginTransaction();
                OleDbCommand deleteComm = new OleDbCommand("DELETE FROM Table", conn);
                deleteComm.ExecuteNonQuery();

                OleDbCommand reAddComm = new OleDbCommand("INSERT INTO Table VALUES(1, 'blabla', 'etc.'", conn);
                reAddComm.ExecuteNonQuery();

                tran.Commit();
            }
            catch (Exception ex)
            {
                tran.Rollback();
                return false;
            }

        }

        return true;
    }

答案 1 :(得分:0)

如果您的查询执行时间过长,则可以以7.5秒的差异启动两个查询,因为当读/写完成3个周期时,每90秒发生一次冲突,并且读取/视图完成2个周期。

话虽如此,它不是一个万无一失的解决方案,只是一个基于假设的技巧,万一你不能完全确定读/写周期是不会发生读/写周期发生了,尝试考虑使用Read Lock。我建议您阅读Understanding how SQL Server executes a queryLocking in the Database Engine

希望有所帮助。

答案 2 :(得分:0)

我会尝试一些事情:

  1. 确保您在一次交易中发生DELETE + INSERT次操作:

    BEGIN TRAN
        DELETE FROM ...
        INSERT INTO ...
    COMMIT
    
  2. 如果这不是忙表,请尝试锁定提示SELECT语句。例如:

    SELECT ...
    FROM Table
    WITH (UPDLOCK, HOLDLOCK)
    

    如果更新事务在SELECT语句运行时启动,则会导致该事务等待SELECT完成。不幸的是,它也会阻止其他SELECT语句,但你不会冒险阅读脏数据。

答案 3 :(得分:0)

我无法解决这个问题,但我更改了代码,因此程序没有删除ActualPlot表中的所有行,而是检查行是否在那里,如果没有从文本文件中添加新行。