【问题标题】:Dataset Sometimes has no tables after Fill数据集填充后有时没有表
【发布时间】:2010-01-07 00:00:03
【问题描述】:

我们有一个 winforms 应用程序每隔几秒就会调用一个存储过程。存储过程总是返回一个包含 0 行或更多行的结果集,并且客户端填充一个数据集。每隔几天左右,我们就会发现数据集中没有表,我们不知道为什么。触发存储过程的过程确实发生在一个线程中,但是数据库连接和存储过程的执行都发生在同一个线程中,因此这里没有任何数据在线程之间传递。

以前有没有人经历过这样的行为?

这里是相关代码,它是一个更大的应用程序的一部分,所以我尝试只包含相关位。

//Used by the main application to poll a database in a thread
public class Poller
{
    private Thread thProcess_m;
    private readonly Action<Exception> actOnError_m;
    private readonly RuntimeData pRuntime_m;
    private readonly DataAccessLayer dalDB_m;

    private bool bRetry_m;

    public Poller(RuntimeData pData, Action<Exception> actOnError)
    {
        this.thProcess_m = new Thread(Main);
        this.thProcess_m.Name = "Main ScriptOr Polling Thread";

        this.actOnError_m = actOnError;
        this.dalDB_m = new DataAccessLayer(ConfigurationState.ConnectionStrings.DB);
    }

    public void Start()
    {
        this.thProcess_m.Start();
    }

    protected override void Main()
    {
        while (this.pRuntime_m.Running)
        {
            try
            {
                int iQueued;
                Task pTask = this.dalDB_m.GetNextTask(out iQueued);
            }
            catch (Exception ex)
            {
                this.actOnError_m(ex);
            }
        }
    }
}

public class RuntimeData
{
    private bool bRunning_m;
    public bool Running
    {
        get
        {
            return bRunning_m;
        }
        set
        {
            bRunning_m = value;
        }
    }
}

public class DataAcessLayer
{
    public Task GetNextTask(out int iQueued)
    {
        iQueued = 0;
        Task tskNext = null;

        SqlCommand cmdNextTask = new SqlCommand();
        cmdNextTask.CommandType = System.Data.CommandType.StoredProcedure;
        cmdNextTask.CommandText = "pGetNextTask";        

        cmdNextTask.Connection = new System.Data.SqlClient.SqlConnection();
        cmdNextTask.Connection.ConnectionString = "my connection string";
        cmdNextTask.Connection.Open();

        DataSet dsNextTask = new DataSet();

        try
        {
            System.Data.SqlClient.SqlDataAdapter sqlNextTask = new System.Data.SqlClient.SqlDataAdapter(cmdNextTask);            
            sqlNextTask.Fill(dsNextTask);
        }
        finally
        {
            cmdNextTask.Connection.Close();
        }

        tskNext = LoadTask(dsNextTask);
        if (dsTask.Tables[0].Rows.Count > 0)
        {
            iQueued = (int)dsTask.Tables[0].Rows[0]["Queued"];
        }
        return tskNext;
    }

    protected Task LoadTask(DataSet dsTask)
    {
        Task tskNext = null;

        if (dsTask == null)
        {
            throw new ArgumentNullException("LoadTask DataSet is null.");
        }

        if (dsTask.Tables == null)
        {
            throw new NullReferenceException("LoadTask DataSet.Tables is null.");
        }

        //Here's where the exception is being thrown
        if (dsTask.Tables.Count == 0)
        {
            throw new ArgumentOutOfRangeException("LoadTask DataSet.Tables.Count == 0.");
        }

        if (dsTask.Tables[0].Rows.Count > 0)
        {
            DataRow drTask = dsTask.Tables[0].Rows[0];

            tskNext = new InteriorHealth.ScriptOr.ScriptTask((int)(drTask["id"]));
            tskNext.Name = pRow["Name"].ToString();
        }
        return pTask;
    }
}

【问题讨论】:

  • 填充数据集的代码?

标签: .net ado.net


【解决方案1】:

类似的事情发生在我身上的一种可能性是存储过程调用根本不返回。如果超时,它不会返回任何内容,并且该过程根本不会达到填充数据集的地步。但是如果没有看到您尝试做的一些示例代码,就很难提供有效的答案。

【讨论】:

  • 我添加了代码。但是如果调用超时,我应该会看到一个异常,但我没有看到任何类型的异常。
【解决方案2】:

我遇到过这种情况的唯一一次是我的存储过程中有错误处理逻辑,但在处理错误方面不太好。

例如:

IF EXISTS(SELECT * FROM SOMEWHERE WHERE MYTHING = @WHATIWANT)
BEGIN
   //select stuff and do weird things
END

IF @@ERROR <> 0
BEGIN
  SET @MYWEIRDERRORTRAPPER = @ERROR
END

在这种情况下,您基本上会吞下存储过程中的异常。这是一个非常糟糕的做法,IMO,但我过去继承了这样的代码。

【讨论】:

    【解决方案3】:

    你有多大把握 this.actOnError_m(ex) 永远不会被调用?

    另外,您没有正确处理 IDisposable,这是我在事情开始变得奇怪时首先要寻找的东西之一。试试这个:

    public Task GetNextTask(out int iQueued)
    {
        iQueued = 0;
    
        Task tskNext;
        using (SqlCommand cmdNextTask = new SqlCommand())
        {
            cmdNextTask.CommandType = System.Data.CommandType.StoredProcedure;
            cmdNextTask.CommandText = "pGetNextTask";
    
            using (SqlConnection connection = new System.Data.SqlClient.SqlConnection())
            {
                cmdNextTask.Connection = connection;
                cmdNextTask.Connection.ConnectionString = "my connection string";
                cmdNextTask.Connection.Open();
    
                using (DataSet dsNextTask = new DataSet())
                {
                    using (
                        System.Data.SqlClient.SqlDataAdapter sqlNextTask =
                            new System.Data.SqlClient.SqlDataAdapter(
                                cmdNextTask))
                    {
                        sqlNextTask.Fill(dsNextTask);
                    }
    
                    tskNext = LoadTask(dsNextTask);
                    if (dsNextTask.Tables[0].Rows.Count > 0)
                    {
                        iQueued = (int) dsNextTask.Tables[0].Rows[0]["Queued"];
                    }
                }
            }
        }
        return tskNext;
    }
    

    并不是我认为这是解决方案,而是少了一件需要担心的事情。

    【讨论】:

    • 您已删除关闭连接的部分,我不明白将 dataadaptor 包装在用户中或将数据集包装在 using 中会有什么帮助,或者您为什么要这样做。它们将正常超出范围并被垃圾收集器处理。
    • 我强烈建议您阅读stackoverflow.com/questions/1070667/… 并了解 IDisposable 接口的工作原理。在 using 块结束时,即使抛出异常,也会调用 Dispose 方法。 SqlConnection 的 Dispose 与 Close 相同。垃圾收集器不一定会调用 Dispose,这是清理非托管资源所必需的。
    【解决方案4】:

    我将 DataAccessLayer 的实例化从构造函数移到 Main() 方法中。从那以后我还没有看到这个问题,所以我认为它解决了这个问题。我认为问题在于构造函数发生在主应用程序线程中,因此 DataAccessLayer 实例位于线程之外。将实例化移动到 Main() 方法中允许线程拥有对象。

    【讨论】:

    • 原来是线程问题。存在连接对象被多个线程使用的错误。即使它从来没有同时发生,它仍然会引起问题。
    猜你喜欢
    • 1970-01-01
    • 2017-03-19
    • 2020-01-07
    • 2019-02-19
    • 2014-11-15
    • 2013-06-13
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    相关资源
    最近更新 更多