SSIS 包中的 C# 脚本在 SQL Server 表的数据执行过程中挂起,没有明确的错误消息

Posted

技术标签:

【中文标题】SSIS 包中的 C# 脚本在 SQL Server 表的数据执行过程中挂起,没有明确的错误消息【英文标题】:C# script in SSIS package hangs in the middle of data implementation to SQL Server table with no clear error message 【发布时间】:2021-02-03 11:40:46 【问题描述】:

我不是 C# 专家,但我在 SSIS 脚本任务中有一个 C# 脚本,该脚本使用用户和密码凭据连接到 SQL Server 表。该脚本将地理数据从 WGS 转换为 Lambert。

一开始它运行良好,但脚本在大约 30 分钟后停止执行数据,没有明确的错误消息,我会弹出此消息

调用的目标抛出了异常

à System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) à System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) à System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfoculture) à System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] 修饰符, CultureInfoculture, String[] namedParams) à Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTATaskScriptingEngine.ExecuteScript()

我尝试将包部署到 SQL Server,但它在中间也停止工作。

提前谢谢你

这是脚本

#region Help:  Introduction to the script task

#endregion


#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Text;
#endregion

namespace ST_1e550fb6488d4c91bf7449b1754af4cc

    /// <summary>

    /// </summary>
    [Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
    public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
    
        #region Help:  Using Integration Services variables and parameters in a script
   

        #endregion

        #region Help:  Firing Integration Services events from a script
   
        #endregion

        #region Help:  Using Integration Services connection managers in a script

        #endregion


 
        public void Main()
        
            
          
            string constring = @"Data Source=sqls01-bus.database.windows.net;User ID=*******;password = ******;Initial Catalog=*****;Persist Security Info=True;";
         

            SqlConnection connection = new SqlConnection(constring);

            Dts.TaskResult = (int)ScriptResults.Success;
            using (connection)
            
                SqlCommand command = new SqlCommand(
                  "SELECT MINX, MINY, ID_ELEMENT FROM SPATIALPOINT  order by ID_ELEMENT;",
                  connection);
                connection.Open();

                SqlDataReader reader = command.ExecuteReader();

                if (reader.HasRows)
                
                    while (reader.Read())
                    

                        Point pt = convertToWGS84Deg(double.Parse(reader.GetValue(0).ToString()), double.Parse(reader.GetValue(1).ToString()), Zone.Lambert93);
                        double[] tab = WGS84ToLambert2e(pt.x, pt.y);
                        int idElement = int.Parse(reader.GetValue(2).ToString());
                        insererElementDansLaTableElementComp(pt, constring, idElement, tab, double.Parse(reader.GetValue(0).ToString()), double.Parse(reader.GetValue(1).ToString()));
                    
                
                else
                
                    Console.WriteLine("No rows found.");
                
                reader.Close();
            

            //Point pt = convertToWGS84Deg(668832.5384, 6950138.7285, Zone.Lambert93);




        
        public static double[] WGS84ToLambert2e(double longitude, double latitude)
        
            // Le système de coordonnées géographiques utilisé est postif vers le Nord et vers l'Est
            var orientation_lat = 'N';
            if (latitude < 0)
            
                orientation_lat = 'S';
                latitude = -1 * latitude;
            
            var degree_lat = Math.Truncate(latitude);
            var decPart = latitude - degree_lat;

            var multipliedBy3600 = decPart * 3600;
            var dividedBy60 = multipliedBy3600 / 60.0;
            var minute_lat = Math.Truncate(dividedBy60);
            decPart = dividedBy60 - minute_lat;
            var seconde_lat = decPart * 60;

            // Le système de coordonnées géographiques utilisé est postif vers le Nord et vers l'Est
            var orientation_long = 'E';
            if (longitude < 0)
            
                orientation_long = 'W';
                longitude = -1 * longitude;
            
            var degree_long = Math.Truncate(longitude);
            decPart = longitude - degree_long;

            multipliedBy3600 = decPart * 3600;
            dividedBy60 = multipliedBy3600 / 60.0;
            var minute_long = Math.Truncate(dividedBy60);
            decPart = dividedBy60 - minute_long;
            var seconde_long = decPart * 60;

            return WGS84toLambert2e(degree_long, minute_long, seconde_long, orientation_long, degree_lat, minute_lat, seconde_lat, orientation_lat);
        
        public static double[] WGS84toLambert2e(double d_long, double m_long, double s_long, char orientation_long, double d_lat, double m_lat, double s_lat, char orientation_lat)
        
            double lambda_w, phi_w;

            /**************************************************************************************************************/
            /**        0) degres-minutes-secondes + orientation (d,m,s,o) -> radian                                           **/
            /**************************************************************************************************************/

            // Pour la longitude
            lambda_w = d_long + m_long / 60 + s_long / 3600;
            if (orientation_long == 'W') lambda_w = -1 * lambda_w; // Le système de coordonnées géographiques utilisé est postif vers le Nord et vers l'Est

            lambda_w = lambda_w * Math.PI / 180;

            // Pour la latitude
            phi_w = d_lat + m_lat / 60 + s_lat / 3600;
            if (orientation_lat == 'S') phi_w = -1 * phi_w;          // Le système de coordonnées géographiques utilisé est postif vers le Nord et vers l'Est

            phi_w = phi_w * Math.PI / 180;

            /**************************************************************************************************************/
            /**        1) coordonnées géographiques WGS84 (phi_w,lambda_w) -> coordonnées cartésiennes WGS84 (X_w,Y_w,Z_w)  **/
            /**************************************************************************************************************/

            // J'ai utilisé 2 formules données par l'IGN dans un de leur document ainsi que deux constantes de 
            // l'ellipsoide de référence du système WGS84 (les deux demi-axes) :

            double a_w = 6378137.0;
            double b_w = 6356752.314;

            // d'où
            double e2_w = (a_w * a_w - b_w * b_w) / (a_w * a_w);

            // et on a la grande normale de l'ellipsoide WGS84
            double N = a_w / Math.Sqrt(1 - e2_w * Math.Pow(Math.Sin(phi_w), 2));

            // ainsi on obtient
            double X_w = N * Math.Cos(phi_w) * Math.Cos(lambda_w);
            double Y_w = N * Math.Cos(phi_w) * Math.Sin(lambda_w);
            double Z_w = N * (1 - e2_w) * Math.Sin(phi_w);

            /**************************************************************************************************************/
            /**        2) coordonnées cartésiennes WGS84 (X_w,Y_w,Z_w) -> coordonnées cartésiennes NTF (X_n,Y_n,Z_n)          **/
            /**************************************************************************************************************/

            // Là il n'y a qu'un translation à effectuer :

            // on a donc
            double dX = 168.0;
            double dY = 60.0;
            double dZ = -320.0;

            // et...
            double X_n = X_w + dX;
            double Y_n = Y_w + dY;
            double Z_n = Z_w + dZ;

            /**************************************************************************************************************/
            /**        3) coordonnées cartésiennes NTF (X_n,Y_n,Z_n) -> coordonnées géographiques NTF (phi_n,lambda_n)      **/
            /**************************************************************************************************************/

            // J'ai utilisé 1 formule donnée par l'IGN toujours dans le même document ainsi que deux constantes de l'ellipsoide 
            // de référence du système NTF, Clarke 1880 :

            double a_n = 6378249.2;
            double b_n = 6356515.0;

            // d'où
            double e2_n = (a_n * a_n - b_n * b_n) / (a_n * a_n);

            // on définit une tolérance de convergence
            double epsilon = Math.Pow(10, -10);

            // puis on amorce une boucle de calcul        
            double p0 = Math.Atan(Z_n / Math.Sqrt(X_n * X_n + Y_n * Y_n) * (1 - (a_n * e2_n) / (Math.Sqrt(X_n * X_n + Y_n * Y_n + Z_n * Z_n))));
            double p1 = Math.Atan((Z_n / Math.Sqrt(X_n * X_n + Y_n * Y_n)) / (1 - (a_n * e2_n * Math.Cos(p0)) / (Math.Sqrt((X_n * X_n + Y_n * Y_n) * (1 - e2_n * Math.Pow(Math.Sin(p0), 2))))));

            while (!(Math.Abs(p1 - p0) < epsilon))
            

                p0 = p1; p1 = Math.Atan((Z_n / Math.Sqrt(X_n * X_n + Y_n * Y_n)) / (1 - (a_n * e2_n * Math.Cos(p0)) / (Math.Sqrt((X_n * X_n + Y_n * Y_n) * (1 - e2_n * Math.Pow(Math.Sin(p0), 2))))));

            

            double phi_n = p1;
            double lambda_n = Math.Atan(Y_n / X_n);

            /**********************************************************************************************************************/
            /**        4) coordonnées géographiques NTF (phi_n,lambda_n)  coordonnées projetées en Lambert II étendu (X_l2e, Y_l2e) **/
            /**********************************************************************************************************************/

            // J'utilise les formules de projection et les constantes fournies par l'IGN dans un autre document :

            // avant tout on définit quelques constantes
            double n = 0.7289686274;
            double c = 11745793.39;
            double Xs = 600000.0;
            double Ys = 8199695.768;

            double e_n = Math.Sqrt(e2_n);
            double lambda0 = 0.04079234433198;   //correspond à la longitude en radian de Paris (2°20'14.025" E) par rapport à Greenwich
                                                 // puis on calcule la latitude isométrique
            double L = Math.Log(Math.Tan(Math.PI / 4 + phi_n / 2) * Math.Pow(((1 - e_n * Math.Sin(phi_n)) / (1 + e_n * Math.Sin(phi_n))), (e_n / 2)));

            // enfin on projette

            double X_l2e = Xs + c * Math.Exp((-n * L)) * Math.Sin(n * (lambda_n - lambda0));
            double Y_l2e = Ys - c * Math.Exp((-n * L)) * Math.Cos(n * (lambda_n - lambda0));

            double[] tabXY = new double[2];

            tabXY[0] = X_l2e;
            tabXY[1] = Y_l2e;

            return tabXY;
        
        public static void insererElementDansLaTableElementComp(Point pt, string conString, int idElement, double[] tab, double minx93, double miny93)
        
            string sqlQuery = string.Format("INSERT into COMP_ELEMENTS_COORDONNEES (COORDONNEE_X, COORDONNEE_Y,ELEMENT , MINX_LAMBERT2 , MINY_LAMBERT2 , MINX_LAMBERT93 , MINY_LAMBERT93 , ELEMENT_TYPE, TYPE_SPATIAL) values (@CoordonneX, @CoordonnneeY , @ElementID, @MinXLambert , @MinYLambert , @MinXLambert93 , @MinYLambert93 , @ElementType, @TypeSpatial)");
            SqlConnection con = new SqlConnection(conString);
            SqlCommand s = new SqlCommand(sqlQuery, con);
            s.Parameters.Add("@CoordonneX", SqlDbType.Float).Value = pt.x;
            s.Parameters.Add("@CoordonnneeY", SqlDbType.Float).Value = pt.y;
            s.Parameters.Add("@ElementID", SqlDbType.Int).Value = idElement;
            s.Parameters.Add("@MinXLambert", SqlDbType.Float).Value = tab[0];
            s.Parameters.Add("@MinYLambert", SqlDbType.Float).Value = tab[1];
            s.Parameters.Add("@MinXLambert93", SqlDbType.Float).Value = minx93;
            s.Parameters.Add("@MinYLambert93", SqlDbType.Float).Value = miny93;
            s.Parameters.Add("@ElementType", SqlDbType.VarChar).Value = "ELE";
            s.Parameters.Add("@TypeSpatial", SqlDbType.VarChar).Value = "POINT";
            con.Open();
            s.ExecuteNonQuery();
            con.Close();
        
        public static Point convertToWGS84Deg(double x, double y, Zone zone)
        

            Point pt = new Point(x, y, 0);
            pt = convertToWGS84(pt, zone);
            pt.toDegree();
            return pt;


        
        public static Point convertToWGS84(Point org, Zone zone)
        

            var lzone = new LambertZone(zone);

            if (zone == Zone.Lambert93)
            
                return lambertToGeographic(org, lzone, LambertZone.LON_MERID_IERS, LambertZone.E_WGS84, LambertZone.DEFAULT_EPS);
            
            else
            
                Point pt1 = lambertToGeographic(org, lzone, LambertZone.LON_MERID_PARIS, LambertZone.E_CLARK_IGN, LambertZone.DEFAULT_EPS);

                Point pt2 = geographicToCartesian(pt1.x, pt1.y, pt1.z, LambertZone.A_CLARK_IGN, LambertZone.E_CLARK_IGN);

                pt2.translate(-168, -60, 320);

                //WGS84 refers to greenwich
                return cartesianToGeographic(pt2, LambertZone.LON_MERID_GREENWICH, LambertZone.A_WGS84, LambertZone.E_WGS84, LambertZone.DEFAULT_EPS);
            
        
        private static double lambertNormal(double lat, double a, double e)
        

            return a / Math.Sqrt(1 - e * e * Math.Sin(lat) * Math.Sin(lat));
        

        private static Point geographicToCartesian(double lon, double lat, double he, double a, double e)
        
            double N = lambertNormal(lat, a, e);

            Point pt = new Point(0, 0, 0);

            pt.x = (N + he) * Math.Cos(lat) * Math.Cos(lon);
            pt.y = (N + he) * Math.Cos(lat) * Math.Sin(lon);
            pt.z = (N * (1 - e * e) + he) * Math.Sin(lat);

            return pt;
        

        private static Point cartesianToGeographic(Point org, double meridien, double a, double e, double eps)
        
            double x = org.x, y = org.y, z = org.z;

            double lon = meridien + Math.Atan(y / x);

            double module = Math.Sqrt(x * x + y * y);

            double phi0 = Math.Atan(z / (module * (1 - (a * e * e) / Math.Sqrt(x * x + y * y + z * z))));
            double phiI = Math.Atan(z / module / (1 - a * e * e * Math.Cos(phi0) / (module * Math.Sqrt(1 - e * e * Math.Sin(phi0) * Math.Sin(phi0)))));
            double delta = Math.Abs(phiI - phi0);
            while (delta > eps)
            
                phi0 = phiI;
                phiI = Math.Atan(z / module / (1 - a * e * e * Math.Cos(phi0) / (module * Math.Sqrt(1 - e * e * Math.Sin(phi0) * Math.Sin(phi0)))));
                delta = Math.Abs(phiI - phi0);

            

            double he = module / Math.Cos(phiI) - a / Math.Sqrt(1 - e * e * Math.Sin(phiI) * Math.Sin(phiI));

            Point pt = new Point(lon, phiI, he);

            return pt;
        
        private static double latitudeFromLatitudeISO(double latISo, double e, double eps)
        

            double phi0 = 2 * Math.Atan(Math.Exp(latISo)) - LambertZone.M_PI_2;
            double phiI = 2 * Math.Atan(Math.Pow((1 + e * Math.Sin(phi0)) / (1 - e * Math.Sin(phi0)), e / 2d) * Math.Exp(latISo)) - LambertZone.M_PI_2;
            double delta = Math.Abs(phiI - phi0);

            while (delta > eps)
            
                phi0 = phiI;
                phiI = 2 * Math.Atan(Math.Pow((1 + e * Math.Sin(phi0)) / (1 - e * Math.Sin(phi0)), e / 2d) * Math.Exp(latISo)) - LambertZone.M_PI_2;
                delta = Math.Abs(phiI - phi0);
            

            return phiI;
        

        private static Point lambertToGeographic(Point org, LambertZone zone, double lonMeridian, double e, double eps)
        
            double n = zone.n();
            double C = zone.c();
            double xs = zone.xs();
            double ys = zone.ys();

            double x = org.x;
            double y = org.y;


            double lon, gamma, R, latIso;

            R = Math.Sqrt((x - xs) * (x - xs) + (y - ys) * (y - ys));

            gamma = Math.Atan((x - xs) / (ys - y));

            lon = lonMeridian + gamma / n;

            latIso = -1 / n * Math.Log(Math.Abs(R / C));

            double lat = latitudeFromLatitudeISO(latIso, e, eps);

            Point dest = new Point(lon, lat, 0);
            return dest;
        
        public static Point convertToWGS84(double x, double y, Zone zone)
        

            Point pt = new Point(x, y, 0);
            return convertToWGS84(pt, zone);
        

        #region ScriptResults declaration
        /// <summary>
        /// This enum provides a convenient shorthand within the scope of this class for setting the
        /// result of the script.
        /// 
        /// This code was generated automatically.
        /// </summary>
        enum ScriptResults
        
            Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
            Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
        ;
        #endregion

    
    public enum Zone
    
        LambertI = 0, LambertII = 1, LambertIII = 2, LambertIV = 3, LambertIIExtended = 4, Lambert93 = 5
    
    public enum Unit  Degree, Grad, Radian, Meter ;
    public class Point
    
        private const double radianTodegree = 180.0 / Math.PI;

        public Point(double x, double y, double z)
        
            this.x = x;
            this.y = y;
            this.z = z;
        

        public double x  get; set; 
        public double y  get; set; 
        public double z  get; set; 


        public void translate(double x, double y, double z)
        

            this.x += x;
            this.y += y;
            this.z += z;
        
        private void Scale(double scale)
        
            this.x *= scale;
            this.y *= scale;
            this.z *= scale;
        

        public void toDegree()
        
            Scale(radianTodegree);
        
    

    public class LambertZone
    

        private readonly static double[] LAMBERT_N =  0.7604059656, 0.7289686274, 0.6959127966, 0.6712679322, 0.7289686274, 0.7256077650 ;
        private readonly static double[] LAMBERT_C =  11603796.98, 11745793.39, 11947992.52, 12136281.99, 11745793.39, 11754255.426 ;
        private readonly static double[] LAMBERT_XS =  600000.0, 600000.0, 600000.0, 234.358, 600000.0, 700000.0 ;
        private readonly static double[] LAMBERT_YS =  5657616.674, 6199695.768, 6791905.085, 7239161.542, 8199695.768, 12655612.050 ;

        public readonly static double M_PI_2 = Math.PI / 2.0;
        public readonly static double DEFAULT_EPS = 1e-10;
        public readonly static double E_CLARK_IGN = 0.08248325676;
        public readonly static double E_WGS84 = 0.08181919106;

        public readonly static double A_CLARK_IGN = 6378249.2;
        public readonly static double A_WGS84 = 6378137.0;
        public readonly static double LON_MERID_PARIS = 0;
        public readonly static double LON_MERID_GREENWICH = 0.04079234433;
        public readonly static double LON_MERID_IERS = 3.0 * Math.PI / 180.0;

        public LambertZone(Zone zone)
        
            this.lambertZone = zone;
        

        public double n()
        
            return LAMBERT_N[(int)lambertZone];
        

        public double c()
        
            return LAMBERT_C[(int)lambertZone];
        
        public double xs()
        
            return LAMBERT_XS[(int)lambertZone];
        
        public double ys()
        
            return LAMBERT_YS[(int)lambertZone];
        

        public Zone lambertZone  get; private set; 

    

【问题讨论】:

这里是代码,但我认为这不是问题,因为它可以正常工作大约 30 分钟 在您的脚本中使用 try catch,例如 - ***.com/a/52653538/10376537,这应该会得到更好的错误消息,并可能会为我们指出问题所在的更好方向。 在代码中添加断点并从 Visual Studio 运行。你会得到一个行号和一个真实的错误描述 如果 SELECT MINX, MINY, ID_ELEMENT FROM SPATIALPOINT 其中 MINX 为 null 或 miny 为 null 或 ID_ELEMENT 为 null 返回任何内容,那么这就是您的问题 除了已经提到的try....catch,您还应该注意,您可能会遇到 NULL 结果,因此if (!reader==null &amp;&amp; reader.HasRows) 之类的内容可能会有所帮助。此外,我猜问题可能在一开始就存在:30 分钟的运行时间可能暗示连接或命令超时。但是,只有在您发现异常时才能确定这一点。 【参考方案1】:

我以另一种方式解决了这个问题,因为 Azure 数据库不断切断连接并且无法修改 azure 安全设置。我修改了脚本,现在我有一个工作每周运行一次,只插入新数据。

【讨论】:

以上是关于SSIS 包中的 C# 脚本在 SQL Server 表的数据执行过程中挂起,没有明确的错误消息的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SSIS 包中使用变量和 SQL 代码?

如何在 ssis 包中的变量中获取当前周星期一的日期

在 SQL Server 表中转储 SSIS USER 变量名称和值

如何在 SSIS 包中使用 COM 引用?

安装SSIS包中的数据流任务

查询值和目标字段的数量不同 - C# 脚本任务 SSIS - 使用动态列将 SQL Proc 的结果导出到 Excel