1

Our program uses the QueryQueuedBuilds from Microsoft.TeamFoundation.Build.Server to get all the queued builds from our build server.

So far we did it synchrone from a DispatcherTimer tick method, but the UI gets non-responsive sometimes for more than 1000 ms. This is the original call:

    public List<QueuedBuild> GetQueuedBuilds()
    {
        List<QueuedBuild> queuedBuilds = new List<QueuedBuild>();

        foreach (var project in _teamProjectList)
        {
            IQueuedBuildSpec queuedBuildSpec = _buildServer.CreateBuildQueueSpec(project.Name);
            IQueuedBuildQueryResult queuedBuildQueryResult = _buildServer.QueryQueuedBuilds(queuedBuildSpec);

            if (queuedBuildQueryResult.QueuedBuilds.Length > 0)
            {
                foreach (var result in queuedBuildQueryResult.QueuedBuilds)
                {
                    queuedBuilds.Add(new QueuedBuild()
                    {
                     // Set properties from result
                    });
                }
            }                
        }

        return queuedBuilds;
    }

But the line with the QueryQueuedBuilds takes a long time.

I discovered there are asynchronous methods BeginQueryQueuedBuilds and EndQueryQueuedBuilds, but I cannot find examples how to use it. This is what I got so far:

public List<QueuedBuild> GetQueuedBuilds()
{
    List<QueuedBuild> queuedBuilds = new List<QueuedBuild>();
    Collection<IQueuedBuildSpec> queuedBuildSpecs = new Collection<IQueuedBuildSpec>();

    foreach (var project in _teamProjectList)
    {
        queuedBuildSpecs.Add(_buildServer.CreateBuildQueueSpec(project.Name));
    }

    IAsyncResult asyncResult = _buildServer.BeginQueryQueuedBuilds(queuedBuildSpecs.ToArray(),
        OnBeginQueryQueuedBuilds, queuedBuilds);
    
    // What to do with asyncResult?

    return queuedBuilds; // How to return queuedBuilds?
}

private void OnBeginQueryQueuedBuilds(IAsyncResult ar)
{
    if (!(ar.AsyncState is List<QueuedBuild> queuedBuilds))
    {
        return;
    }

    IQueuedBuildQueryResult[] queryResults = _buildServer.EndQueryQueuedBuilds(ar);

    foreach (var queuedBuildQueryResult in queryResults)
    {
        foreach (var result in queuedBuildQueryResult.QueuedBuilds)
        {
            queuedBuilds.Add(new QueuedBuild()
            {
                // Set properties from result
            });
        }
    }

    // How to get the queuedBuilds back to the GUI?
}

The callback method works. But how do I get the queuedBuilds back to my GUI?

ffonz
  • 1,304
  • 1
  • 11
  • 29
  • What version of TFS are you using? There are modern asynchronous APIs available in versions of TFS released in the past 7 years. – Daniel Mann Jan 18 '22 at 22:26
  • We just upgraded to ADS2020. So I should use another API? – ffonz Jan 19 '22 at 11:04
  • Yes. You're using the old SOAP libraries that haven't been updated for years. There are REST libraries using modern APIs: https://learn.microsoft.com/en-us/azure/devops/integrate/concepts/dotnet-client-libraries?view=azure-devops#rest-packages – Daniel Mann Jan 19 '22 at 14:41
  • @DanielMann I found out that this piece of code is for getting the old XAML builds (we still have a lot of them). My collegue told me he used this old API especially for the XAML builds, coz the new WebApi didn't support XAML builds, only vNext. Is that true? – ffonz Jan 20 '22 at 11:12
  • No, there are REST APIs for XAML builds. I don't know if they're exposed in the REST API client versus having to construct REST API calls by hand, and I can't vouch for how well they work, but they do exist: https://learn.microsoft.com/en-us/rest/api/azure/devops/build/builds/queue?view=azure-devops-rest-7.1 That said, XAML builds have been deprecated for **seven years**. It's really in your best interest to migrate them to something modern. – Daniel Mann Jan 20 '22 at 15:19
  • Yes, I know. But we have almost 200 of them. There isn't a XAML to YAML converter. It all has to be done by hand. And that takes time. :-( – ffonz Jan 20 '22 at 15:30

1 Answers1

0

I finally solved the puzzle similar to this solution.

Most important changes I did: The callback method isn't used anymore. The IAsyncResult is now passed to a Task.Factory.FromAsync() method and the task is awaited.

This is what I ended up with:

    public async Task<List<QueuedBuild>> GetQueuedBuilds()
    {
        Collection<IQueuedBuildSpec> queuedBuildSpecs = new Collection<IQueuedBuildSpec>();

        foreach (var project in _teamProjectList)
        {
            queuedBuildSpecs.Add(_buildServer.CreateBuildQueueSpec(project.Name));
        }

        IAsyncResult asyncResult = _buildServer.BeginQueryQueuedBuilds(queuedBuildSpecs.ToArray(), null, null);

        var task = Task.Factory.FromAsync(asyncResult, _buildServer.EndQueryQueuedBuilds);
        IQueuedBuildQueryResult[] queuedBuildQueryResults = await task;
        return FinishQueryQueuedBuilds(queuedBuildQueryResults);
    }

    private List<QueuedBuild> FinishQueryQueuedBuilds(IQueuedBuildQueryResult[] queuedBuildQueryResults)
    {
        List<QueuedBuild> queuedBuilds = new List<QueuedBuild>();

        foreach (var queuedBuildQueryResult in queuedBuildQueryResults)
        {
            foreach (var result in queuedBuildQueryResult.QueuedBuilds)
            {
                queuedBuilds.Add(new QueuedBuild()
                {
                    // Set properties from result
                });
            }
        }

        return queuedBuilds;
    }

I hope this can help someone else with a similar problem.

ffonz
  • 1,304
  • 1
  • 11
  • 29