使用SMO在并行执行ExecuteNonQuery时抛出TargetInvocationException

时间:2015-09-21 02:29:36

标签: c# task-parallel-library smo

概述

我已经工作了很长时间,试图找到一个解决我的特定情况的帖子,但是已经空了。我正在尝试创建一个C#.NET控制台应用程序,该应用程序将解析具有多个数据库还原定义的XML文件,然后使用SQL管理对象并行启动这些还原。每次还原后,用户可以指定他们希望针对新还原的数据库运行的零到多个SQL文件。每当我并行尝试这些作业中的4个以上时,处理针对数据库执行SQL文件的静态方法会在针对本地创建的ServerConnection调用ExecuteNonQuery()方法时产生阻塞。我是否错误地实施了这个?关于如何使这项工作的任何建议?

尝试解决方案

  • 使用Parallel.ForEach循环执行作业
  • 使用Task.Factory.StartNew()执行作业

在上述两种方法中,我都提到了两种静态方法:

  1. 使用SMO处理恢复的人。
  2. 使用SMO处理针对数据库执行SQL脚本的人。
  3. 致电代码

                Parallel.ForEach(restores.Restore,
                //new ParallelOptions {MaxDegreeOfParallelism = 4},
                restore => 
                {
                    //Grab copies of the backup information
                    string backup = restore.Backup;
                    string server = restore.Server;
                    string database = restore.Database;
                    string[] patchFiles = restore.PatchFiles;
    
                    //Begin by restoring the database from the backup file
                    RestoreDatabaseFromBackup(backup, server, database);
    
                    //Execute each patch file sequentially for the restore defintion
                    foreach (string patchFile in patchFiles)
                    {
                        ExecutePatchFile(patchFile, server, database);
                    }
                });
    

    ExecutePathFile方法

    private static void ExecutePatchFile(string patchFile, string server, string database)
        {
            //Create directory for the log file (if it doesn't already exist)
            DirectoryInfo di = CreateLogFileDirectory(server, database);
    
            //Create the log file
            StreamWriter sw = File.CreateText(Path.Combine(di.FullName, Path.GetFileNameWithoutExtension(patchFile) + ".log"));
    
            Console.WriteLine("{0} : Beginning execution of {1} for {2} on {3}.", DateTime.Now, patchFile, database, server);
    
            //Verify that 'patchFile' exists and is of the right file extension
            if (!File.Exists(patchFile) || Path.GetExtension(patchFile) != ".sql")
            {
                Console.WriteLine("{0} : Could not execute {1} for {2} on {3}. Invalid SQL file.", DateTime.Now, Path.GetFileName(patchFile), database, server);
                sw.WriteLine("{0} : Could not execute {1} for {2} on {3}. Invalid SQL file.", DateTime.Now, Path.GetFileName(patchFile), database, server);
                sw.Close();
                return;
            }
    
            string script = File.ReadAllText(patchFile);
    
            try
            {
                //Build out connection string
                SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder();
                sb.DataSource = server;
                sb.InitialCatalog = database;
                sb.IntegratedSecurity = true;
    
                //Create SqlConnection
                SqlConnection sqlCon = new SqlConnection(sb.ToString());
                //Set sqlCon to treat exceptions as infomessages
                sqlCon.FireInfoMessageEventOnUserErrors = true;
    
                //Create ServerConnection
                ServerConnection serverCon = new ServerConnection(sqlCon);
    
                serverCon.InfoMessage += new SqlInfoMessageEventHandler((sender, e) => ConnectionContext_InfoMessage(sender, e, sw));
                serverCon.ExecuteNonQuery(script, ExecutionTypes.ContinueOnError);
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                sw.Close();
            }
    
        }
    

    堆栈跟踪

    System.Reflection.TargetInvocationException was unhandled by user code
      HResult=-2146232828
      Message=Exception has been thrown by the target of an invocation.
      Source=BatchDatabaseRestorer
      StackTrace:
           at BatchDatabaseRestorer.Program.ExecutePatchFile(String patchFile, String server, String database) in c:\Users\zannett\Documents\Visual Studio 2013\Projects\BatchDatabaseRestorer\BatchDatabaseRestorer\Program.cs:line 224
           at BatchDatabaseRestorer.Program.<Main>b__0(RestoresRestore restore) in c:\Users\zannett\Documents\Visual Studio 2013\Projects\BatchDatabaseRestorer\BatchDatabaseRestorer\Program.cs:line 83
           at System.Threading.Tasks.Parallel.<>c__DisplayClass21`2.<ForEachWorker>b__17(Int32 i)
           at System.Threading.Tasks.Parallel.<>c__DisplayClassf`1.<ForWorker>b__c()
      InnerException: Microsoft.SqlServer.Management.Common.ExecutionFailureException
           HResult=-2146233087
           Message=Parse error occurred while looking for GO statement. Line 0.
           Source=Microsoft.SqlServer.BatchParserClient
           StackTrace:
                at Microsoft.SqlServer.Management.Common.ExecuteBatch.GetStatements(String sqlCommand)
           InnerException: ManagedBatchParser.ParserException
                HResult=-2146233088
                Message=""
                Source=Microsoft.SqlServer.BatchParser
                StackTrace:
                     at ManagedBatchParser.Parser.Parse()
                     at Microsoft.SqlServer.Management.Common.ExecuteBatch.GetStatements(String sqlCommand)
                InnerException: 
    

2 个答案:

答案 0 :(得分:0)

我相信当你让它在内部处理解析时,在多个ServerConnection实例上并行调用ExecuteNonQuery()是一个问题。如果我自己沿着GO关键字拆分脚本,然后将命令作为StringCollection(每个可用的覆盖)传递,那么无论并行作业的数量如何,命令都会正常执行。这使我相信Microsoft.SqlServer.BatchParserClient对象或Microsoft.SqlServer.BatchParser对象与ExecuteNonQuery()实现的并行执行不兼容。

答案 1 :(得分:0)

我偶然发现了这个问题,因为我对我提出了同样的异常。我正在通过反射从字符串中解析DateTime,我很确定TargetInvocationException正在替换FormatException,因为我正在使用MethodInfo.Invoke来调用DateTime的解析方法。用通常的DateTime.Parse替换后,我得到了FormatException。所以我的字符串格式不正确或者解析器的状态不正确(文化等)。

所以我猜CLR正在用​​TargetInvocationException替换调用的函数引发的任何异常。您可以检查字符串并查找要解析的复杂对象,因为它似乎是导致问题的解析器。