异步DB操作

本文关键字:操作 DB 异步 | 更新日期: 2023-09-27 18:20:02

我的工作流托管在IIS中。并且每个工作流都继承自asynccodeactivity。在BeginExecute中,我调用命令。Beginxxx,在执行结束时,我调用EndExecutexxx。我正在使用数据库访问块(DAAB)。

protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        DbCommand command = null;
        DbConnection dbConnection = null;
        entlib.Database database;
        try
        {
            database = EnterpriseLibraryContainer.Current.GetInstance<entlib.Database>(DatabaseName.Get(context));
            dbConnection = database.CreateConnection();
            command = dbConnection.CreateCommand();
            command.CommandText = CommandText.Get(context);
            command.CommandType = CommandType.Get(context);
            //have removed few assignments here
            context.UserState = new AsyncDbState(database, command);
        }
        catch (Exception e)
        {
            if (command != null)
                command.Dispose();
            if (dbConnection != null)
                dbConnection.Dispose();
            throw e;
        }
        return (database.Beginxxx(command, callback, state));
    }

    protected override TResult EndExecute(AsyncCodeActivityContext context, IAsyncResult iResult)
    {
        TResult result = default(TResult);
        var userState = context.UserState as AsyncDbState;
            try
            {
                result = (TResult)userState.Database.Endxxx(iResult);
            }
            finally
            {
                if (null != userState && null != userState.Command)
                    userState.Command.Dispose();
            }
        return result;
    }

偶尔它会在事件日志中抛出错误并终止整个应用程序池。在@Will发表评论后,我确实捕获了内部异常,并注意到发生了实际错误

在继承自asyncnativeactivity的另一个活动的BeginExecute中,我有

 var task = AsyncFactory<IDataReader>.Action(() => ExecuteMdxQuery(connectionStringSettings, mdxQuery, commandTimeout, cancellationToken), cancellationToken);                        
                    return AsyncFactory<IDataReader>.ToBegin(task, callback, state);
and AsyncFactory looks like this 
 public static Task<TResult> Action(Func<TResult> actionMethod,CancellationToken token)
    {
        TaskFactory factory = new TaskFactory();
        //TaskFactory factory = new TaskFactory(scheduler);
        return factory.StartNew<TResult>(() => actionMethod(), token);   
        }
  public static IAsyncResult ToBegin(Task<TResult> task, AsyncCallback callback, object state)
    {
        var tcs = new TaskCompletionSource<TResult>(state);
        var continuationTask = task.ContinueWith(t =>
        {
            if (task.IsFaulted)
            {
                tcs.TrySetException(task.Exception.InnerExceptions);
            }
            else if (task.IsCanceled)
            {
                tcs.TrySetCanceled();
            }
            else
            {
                tcs.TrySetResult(task.Result);
            }

发生未处理的异常,进程被终止。

发生未处理的异常,进程被终止。

应用程序ID:/LM/W3SVC/1/ROOT/workflowservice

进程ID:7140

异常:System.AggregateException

消息:通过等待任务或访问其exception属性,都没有观察到任务的异常。结果,终结器线程重新抛出了未观察到的异常。

StackTrace:位于System.Threading.Tasks.TaskExceptionHolder.Finalize()

InnerException:Microsoft.AnalysisServices.AomdClient.AdomdErrorResponseException

消息:服务器:当前操作已取消,因为事务中的另一个操作失败。

StackTrace:位于Microsoft.AnalysisServices.AdomdClient.AdomdConnection.XmlaClientProvider.Microsoft.AAnalysisServices.AdomdClient.IExecuteProvider.ExecuteTabular(CommandBehavior行为,ICommandContentProvider内容提供程序,AdomdPropertyCollection命令属性,IDataParameterCollection参数)位于Microsoft.AnalysisServices.AdomdClient.AdomdCommand.ExecuteReader(CommandBehavior行为)在WorkflowActivity.AsyncExecuteSafeReader.ExecuteMdxQuery(字符串connectionStringName,字符串mdxQuery,可为空的1 commandTimeout, CancellationToken cancellationToken) in d:'B'69'Sources'Infrastructure'WorkflowActivity'AsyncExecuteSafeReader.cs:line 222 at AsyncExecuteSafeReader.ExecuteMdxQuery(String connectionStringName, String mdxQuery, Nullable 1 commandTimeout,CancellationToken cancellionToken),位于d:''B''69''Sources''Infrastructure ''WorkflowActivity'' AsyncExecute SafeReader.cs:line 239位于WorkflowActivity.AsyncExecuteSafeReader。<>c:''b''69''Sources''Infrastructure ''WorkflowActivity''AsyncExecuteSafeReader.cs:line 180中的c__DisplayClassd.b_a()位于System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)在System.Threading.Tasks.Task.Execute()

异步DB操作

第一个提示是这在IIS中发生。虽然在应用程序中,当您可能遇到多线程导致的问题时,这通常是显而易见的,但在IIS中则不然

IIS中的每个请求都由不同的线程提供服务。任何共享实例都将被多个线程命中。如果你没有预料到的话,这通常是个坏消息

因此,我的第一个猜测(不得不猜测,因为您的异常的调用堆栈被切断了;稍后会详细介绍)是您在不同的线程中使用线程不安全的代码。我怀疑它是以EnterpriseLibraryContainer.Current.GetInstance为中心的,因为如果它不为每个线程存储实例,那么它将在线程之间共享相同的实例。你必须检查代码或文档。最简单的测试方法是在监视窗口中使用"生成对象ID",然后在两个不同的线程中比较EnterpriseLibraryContainer.Current.GetInstance的结果。

清楚的是,您的异常正在丢失,因为您正在重新抛出异常,而不是让它消失。有关这种情况下的最佳实践的更多信息,请参阅此答案。

重新检查调用堆栈,它似乎仍然是一个多线程错误,但问题似乎是多个线程试图完成两个不同Task s的执行。

消息:操作已完成
StackTrace:位于System.Activities.AsyncOperationContext.HShouldComplete()

某个地方正在尝试完成Task的执行,但它已经完成了。在中,一个线程击败另一个线程完成异步操作。

在这一点上,如果没有异常的完整堆栈跟踪,就无法判断实际问题是什么以及问题发生在哪里。获取此信息的最佳方法是在代码中第一时间捕获异常并对其调用ToString(),或者使用异常助手对话框上的"复制到剪贴板"链接(执行相同的操作,将其复制到剪贴板)。这很重要,因为你可以获得以下信息

  1. 异常类型
  2. 异常消息
  3. 堆栈跟踪

不仅针对您捕获的异常,而且针对由该异常包装的每个.InnerException`很多时候那才是你真正的问题所在

在这种情况下,当您这样做时,您能够识别代码在哪里遇到了可重入性问题。

@Will,连接字符串中的Mars=false似乎已经解决了它。无法复制它。理论上是-查询返回的结果。并调用了end execute。但它也返回了另一个结果集?这就是调用回调的原因。但到那时已经调用了end execute。但话说回来,它是零星的。如果这个理论是真的,我的理解是它可能一直失败。到目前为止还没有崩溃。也有少数程序需要rowcount。我很感激你花时间对此发表评论并分享你的理论。学到了很多。