发布时服务炸弹

Service bombs out when released

希望能在这里得到一些帮助。

在 DEV 服务器上,我在 Visual Studio 2013 社区中创建了一个 C# windows 服务。我已经在调试模式下对其进行了测试: 在 Main()

#if DEBUG
   ...run debug code...
#else
   ...run service code...
#endif

在调试模式下它运行得很好。然后我添加了一个安装程序 class 并在 same 服务器上成功安装了该服务并在服务 window 中启动了它.但是,它什么都不做。我检查了事件日志并看到了这条错误消息:

Application: SharenetIFF.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.NullReferenceException
at SharenetIFF.RunValues.GetRunValues()
at SharenetIFF.SearchFiles.LookforIFFFiles(Int32)
at SharenetIFF.Program.DoThis()
at System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Threading.ThreadHelper.ThreadStart()

这是 RunValues 中的代码:

class RunValues
{
    public int id { get; set; }
    public int runTimeSpan { get; set; }
    public int numberOfRunTime { get; set; }
    private SqlConnection myCon = new SqlConnection();

    public List<int> GetRunValues()
    {
        List<int> values = null;
        string destPath = "";

        try
        {
            string mySQL = "select RunFreq, RunTimes from IFFRunValues";

            myCon.ConnectionString = ConfigurationManager.ConnectionStrings["DatabaseConn"].ConnectionString;                
            myCon.Open();

            SqlCommand myCmd = new SqlCommand(mySQL, myCon);
            SqlDataReader runValuesReader = myCmd.ExecuteReader();
            if (runValuesReader.HasRows)
            {
                while (runValuesReader.Read())
                {
                    runTimeSpan = Convert.ToInt16(runValuesReader["RunFreq"]);
                    numberOfRunTime = Convert.ToInt16(runValuesReader["RunTimes"]);
                }
                values = new List<int>();
                values.Add(runTimeSpan);
                values.Add(numberOfRunTime);
            }
            runValuesReader.Close();
            myCon.Close();
            runValuesReader.Dispose();
            myCmd.Dispose();
            myCon.Dispose();
        }
        catch (Exception ex)
        {
            destPath = Path.Combine("C:\",  "error_log.txt");
            File.AppendAllText(destPath, ex.Message);
            values.Clear();
        }

        return values;
    }
}

我认为它在连接字符串上失败了,主要是因为这里没有其他东西。但不知道为什么。所有代码都在 try/catch 块中,那么未处理的异常怎么可能呢?如果将服务发布到开发它的同一台开发机器上,如果 运行 在 visual studio 之外,服务是否需要不同的权限?

这开始是评论,但太长了,所以...

尚不完全清楚该方法应该做什么。一方面,您填充实例的属性。另一方面,您 return 一个 int 的列表,但是您将值转换为 Int16(即 short)而不是 Int32。您在 之后 循环填充该列表,这意味着您仅从 select 语句中获得最后的值 returned,没有 order by 子句意味着行是return以任意顺序编辑,您根本不使用 Id 属性。

此外,请注意史蒂夫关于将文件直接保存到 C:\ 的评论。

那是在我们甚至触及您正在为 SQLConnection 使用字段而不是 using 语句中的局部变量这一事实之前,这将确保它会被关闭并处理一次你已经完成了它,或者你只处理 try 块内的所有东西,这意味着如果你在代码到达那里之前有一个异常,没有任何东西被处理,或者 table 列在数据库中可能可以为空,因此如果 reader 包含 DBNull.Value 而不是实际值,Convert.ToInt 可能会失败。

也就是说,这是您当前代码的改进版本。我只是将它修改到它应该正确处理所有内容,安全地从 dataReader 获取值而不是抛出 NullReferenceException 的程度,但正如我所说,我不太清楚你试图在此实现的目标方法。

class RunValues
{
    public int id { get; set; }
    public int runTimeSpan { get; set; }
    public int numberOfRunTime { get; set; }

    public List<int> GetRunValues()
    {
        var values = new List<int>();

        // I'm guessing it needs " where id = @id" here...
        var mySQL = "select RunFreq, RunTimes from IFFRunValues"; 
        using(var myCon = new SqlConnection(ConfigurationManager.ConnectionStrings["DatabaseConn"].ConnectionString))
        {
            using(var myCmd = new SqlCommand(mySQL, myCon))
            {
                try
                {
                    myCon.Open();
                    SqlDataReader runValuesReader = myCmd.ExecuteReader();
                    if (runValuesReader.HasRows)
                    {
                        while (runValuesReader.Read())
                        {
                            runTimeSpan = runValuesReader.GetValueOrDefault<int>("RunFreq");
                            numberOfRunTime = runValuesReader.GetValueOrDefault<int>("RunTimes");
                            values.Add(runTimeSpan);
                            values.Add(numberOfRunTime);
                        }
                    }
                }
                catch (Exception ex)
                {
                    var destPath = Path.Combine("C:\",  "error_log.txt");
                    File.AppendAllText(destPath, ex.Message);
                    values.Clear();
                }
            }
        }            
        return values;
    }
}

GetValueOrDefault<T> 是我使用了很长时间的扩展方法,我什至不记得我第一次遇到它的地方 - 这是包含它的 class:

/// <summary>
/// Provides extension methods for IDataReader.
/// </summary>
public static class IDataReaderExtensions
{
    /// <summary>
    /// Gets the value of type T from the column specified by the index parameter, or default(T) if it's null.
    /// </summary>
    /// <typeparam name="T">The type of the value to get.</typeparam>
    /// <param name="reader">An instance of a class implementing IDataReader.</param>
    /// <param name="index">The index of the column from where to get the value.</param>
    /// <returns>The value of type T from the specified column, default(T) if null.</returns>
    public static T GetValueOrDefault<T>(this IDataReader reader, int index)
    {
        return (Convert.IsDBNull(reader[index])) ? default(T) : (T)reader.GetValue(index);
    }

    /// <summary>
    /// Gets the value of type T from the column specified by the name parameter, or default(T) if it's null.
    /// </summary>
    /// <typeparam name="T">The type of the value to get.</typeparam>
    /// <param name="reader">An instance of a class implementing IDataReader.</param>
    /// <param name="name">The name of the column from where to get the value.</param>
    /// <returns>The value of type T from the specified column, default(T) if null.</returns>
    public static T GetValueOrDefault<T>(this IDataReader reader, string name)
    {
        return reader.GetValueOrDefault<T>(reader.GetOrdinal(name));
    }
}