C# NancyFx+;信号机&x2B;使用进度的异步进度报告<;T>;

C# NancyFx+;信号机&x2B;使用进度的异步进度报告<;T>;,c#,asp.net,signalr,async-await,nancy,C#,Asp.net,Signalr,Async Await,Nancy,我半鄙视地向你求助。。。我一直在尝试使用SignalR、.NET4.5中的Async和progress将一些进度报告构建到NancyFx应用程序中。我的主要灵感来自这篇文章: 我对代码进行了一些定制,使其也支持进度步骤报告,而不仅仅是百分比 问题在于:我有一个名为ReviewSetup.SetupAsync(………,IProgress progress)的繁重任务,它是使用ProgressJobManager.Instance.DoJobAsync方法从Nancy模块调用的。我将IProgres

我半鄙视地向你求助。。。我一直在尝试使用SignalR、.NET4.5中的Async和progress将一些进度报告构建到NancyFx应用程序中。我的主要灵感来自这篇文章:

我对代码进行了一些定制,使其也支持进度步骤报告,而不仅仅是百分比

问题在于:我有一个名为ReviewSetup.SetupAsync(………,IProgress progress)的繁重任务,它是使用ProgressJobManager.Instance.DoJobAsync方法从Nancy模块调用的。我将IProgress作为SetupAsync方法的最终参数传入。我为Progress.ProgressChanged设置了一个EventHandler,每当我从SetupAsync()方法中报告一些进度时,都应该调用它

正如您在下面的代码中所看到的,我在ProgressJobManager.Instance.DoJobAsync()方法中连接了事件处理程序,这可能是在执行上下文/线程方面出现问题的地方之一

ProgressChanged进度事件的EventHandler通过调用ReportStep()方法更新ProgressJob。这些方法永远不会被调用,因为它们在我设置的任何断点上都不会中断,我认为这也是由于SetupAsync()方法在不同的线程上执行,因此无法使用提供的参数IProgress progress返回报告

未正确调用进度报告的问题会级联到ProgressJob.StepAdded事件,因此JobManger无法捕获该事件,因此无法通知ProgressHub向客户端发送消息,通知他们进度更新

上面提到的文章要求在ProgressJobManager.Instance.DoJobAsync()方法中完成所有繁重的工作(以及报告进度)。这很好,但我想调用ReviewSetup.SetupAsync()方法并让该方法报告进度。。。我希望你们还和我在一起:-)。我真的很想了解一下.NET4.5中的异步功能。我可能想把它构建成一个更复杂的应用程序,但这是我和我的自我之间的事:-)

无论如何,我的问题是:

  • 我是否以完全错误的方式处理了这个问题
  • 这是一个ExecutionContext/thread问题吗?因为我对async/await的所有方面都有丰富的经验,通过更好地理解.NET4.5中的async的工作原理,可以很容易地解决这个问题?如果是这样,请给我指出正确的方向!:-)
对不起,这不是你的花园品种“我如何转换为问题类型。因此,你的帮助比平常更感激!”:-)

这是我的密码:

ReviewSetup.cs(执行实际报告的类):

最后,ProgressStatus.cs:

public class ProgressStatus
{
    public bool IsComplete { get; set; }
    public int Percentage { get; set; }
    public string Step { get; set; }
}

快速浏览代码表明问题出在reviewsetupapimule.cs中,您可以调用
setup.setup
,但不要等待它返回的任务。第二个问题(可能是问题中的输入错误)是ReviewSetup.cs中的方法称为SetupAsync


当您不等待任务时,调用代码将在任务完成之前继续运行。在这种情况下,这将导致作业管理器认为作业已完成,并将其从列表中删除。

-是的,第一个确实是一个输入错误,调用安装程序的先前非异步版本()方法。-在调用代码中不等待SetupAsync方法是有意义的,但调用代码如何继续,并将能够返回作业(带有id),以便客户端挂接作业并订阅进度更新?如果我等待SetupAsync()方法,作业刚刚完成后是否不会返回?\n此处的魔法酱位于ProgressJobManager.Instance.DoJobAsync中。它使用Task.Run在后台线程上运行lambda。这就是DoJobAsync能够立即返回作业对象的原因。
        Post["/newasync"] = parameters =>
        {
            this.RequiresAuthentication();
            try
            {

                //removed some stuff

                var progresss = new Progress<ProgressStatus>();
                var job = ProgressJobManager.Instance.DoJobAsync(j =>
                {                        
                    //EventHandler bound at the wrong moment/on the wrong thread?
                    progresss.ProgressChanged += delegate(object sender, ProgressStatus status)
                    {
                        j.ReportStep(status.Step);
                        if (status.IsComplete == true)
                        {
                            j.ReportComplete();
                        }
                    };   
                    setup.Setup(config, setupModel.NotebookGuid, setupModel.TagGuids, user, setupModel.ReviewName, progresss);
                });
                //this method needs to keep executing so it can return the job.Id, needed by the client to track its progress.
                return Response.AsJson(job).WithStatusCode(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                return HttpStatusCode.BadRequest;
            }
        };
public class ProgressJob
{
    public event EventHandler<EventArgs> ProgressChanged;
    public event EventHandler<EventArgs> StepAdded;
    public event EventHandler<EventArgs> Completed;

    private volatile int _progress;
    private volatile bool _completed;
    private volatile List<string> _steps;
    private CancellationTokenSource _cancellationTokenSource;

    public List<string> Steps
    {
        get
        {
            return _steps;
        }
    }

    public ProgressJob(string id)
    {
        Id = id;
        _cancellationTokenSource = new CancellationTokenSource();
        _steps = new List<string>();
        _completed = false;
    }

    public string Id { get; private set; }

    public int Progress
    {
        get { return _progress; }
    }

    public bool IsComplete
    {
        get { return _completed; }
    }

    public CancellationToken CancellationToken
    {
        get { return _cancellationTokenSource.Token; }
    }

    public Progress<ProgressStatus> ProgressStatus { get; set; } 

    public void ReportProgress(int progress)
    {
        if (_progress != progress)
        {
            _progress = progress;
            OnProgressChanged();
        }
    }

    public void ReportStep(string step)
    {
        _steps.Add(step);
        OnStepAdded();
    }

    public void ReportComplete()
    {
        if (!IsComplete)
        {
            _completed = true;
            OnCompleted();
        }
    }

    protected virtual void OnCompleted()
    {
        var handler = Completed;
        if (handler != null) handler(this, EventArgs.Empty);
    }

    protected virtual void OnProgressChanged()
    {
        var handler = ProgressChanged;
        if (handler != null) handler(this, EventArgs.Empty);
    }

    protected virtual void OnStepAdded()
    {
        var handler = StepAdded;
        if (handler != null) handler(this, EventArgs.Empty);
    }

    public void Cancel()
    {
        _cancellationTokenSource.Cancel();
    }
}
public class ProgressJobManager
{
    public static ProgressJobManager Instance;
    private IHubContext _hubContext;
    private IProgressJobDataProvider _jobDataProvider;

    public ProgressJobManager(IProgressJobDataProvider jobDataProvider)
    {
        _jobDataProvider = jobDataProvider;
        _hubContext = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
    }

    public ProgressJob DoJobAsync(Action<ProgressJob> action)
    {
        var job = new ProgressJob(Guid.NewGuid().ToString());
        _jobDataProvider.AddJob(job);
        job.ProgressStatus = new Progress<ProgressStatus>();
        Task.Factory.StartNew(() =>
        {
            action(job);
            job.ReportComplete();
            job = _jobDataProvider.DeleteJob(job);
        },
        TaskCreationOptions.LongRunning);
        BroadcastJobStatus(job);
        return job;
    }

    private void BroadcastJobStatus(ProgressJob job)
    {
        job.ProgressChanged += HandleProgressChanged;
        job.Completed += HandleJobCompleted;
        job.StepAdded += HandleStepAdded;
    }

    private void HandleJobCompleted(object sender, EventArgs e)
    {
        var job = (ProgressJob)sender;

        _hubContext.Clients.Group(job.Id).JobCompleted(job.Id);

        job.ProgressChanged -= HandleProgressChanged;
        job.Completed -= HandleJobCompleted;
        job.StepAdded -= HandleStepAdded;
    }

    private void HandleProgressChanged(object sender, EventArgs e)
    {
        var job = (ProgressJob)sender;
        _hubContext.Clients.Group(job.Id).ProgressChanged(job.Id, job.Progress);
    }

    private void HandleStepAdded(object sender, EventArgs e)
    {
        var job = (ProgressJob)sender;
        var step = job.Steps[job.Steps.Count - 1];
        _hubContext.Clients.Group(job.Id).StepAdded(job.Id, step);
    }

    public ProgressJob GetJob(string id)
    {
        return _jobDataProvider.GetJob(id);
    }
}
public class ProgressHub : Hub
{
    public void TrackJob(string jobId)
    {
        Groups.Add(Context.ConnectionId, jobId);       
    }

    public void CancelJob(string jobId)
    {
        var job = ProgressJobManager.Instance.GetJob(jobId);
        if (job != null)
        {
            job.Cancel();
        }
    }

    public void ProgressChanged(string jobId, int progress)
    {
        Clients.Group(jobId).ProgressChanged(jobId, progress);
    }

    public void JobCompleted(string jobId)
    {
        Clients.Group(jobId).JobCompleted(jobId);
    }

    public void StepAdded(string jobId, string step)
    {
        Clients.Group(jobId).StepAdded(jobId, step);
    }
}
public class ProgressStatus
{
    public bool IsComplete { get; set; }
    public int Percentage { get; set; }
    public string Step { get; set; }
}