Calls to `Workspace.PendAdd` not adding items

2020-04-16 17:13发布

I have the following function that is being called in a loop on two new projects to add to source control. Each iteration of the loop gets the source code, copies it to the folder, creates a tfs team project, creates a workspace for that project, and then tries to add the code to source control.

static void Main(string[] args) {
    var tfsWorkItems = _<IWorkItems>();
    var workItems = tfsWorkItems.GetProjectsToMigrate();
    var tfs = _<ITfs>();
    var logFilePath = new DirectoryInfo("C:\\log");
    var workingDirectory = new DirectoryInfo("C:\\m");
    Cleanup(workItems, tfs, logFilePath, workingDirectory);
    var svn = _<ISvn>();
    var app = _<IApplication>();
    foreach (var workItem in workItems)
    {
        var root = Path.Combine(workingDirectory.FullName, workItem.Id.ToString());
        var svnBase = Path.Combine(root, "s");
        var localWorkspacePath = Path.Combine(root, "t");
        var tfsBase = Path.Combine(localWorkspacePath, workItem.TfsProjectName, "Main");
        var tfsProject = workItem.ProjectType.ToLower() == "php" ? Path.Combine(tfsBase, "src")
                                                                    : tfsBase;
        svn.CheckoutFromSvn(workItem.SvnLocation, svnBase);
        app.CopyToTfsFolderStructure(svnBase, tfsProject);
        tfs.CreateTeamProject(workItem.TfsProjectName, logFilePath);
        tfs.CreateWorkspace(workItem.WorkspaceName, localWorkspacePath);
        tfs.AddToSourceControl(workItem.WorkspaceName, localWorkspacePath, workItem.TfsProjectName);
    }
}

There are two projects. The first project works correctly, but the second does not. The second project creates the project and workspace, but in AddToSourceControl

public void AddToSourceControl(string workspaceName, string localPath, string projectName) {
    var tfs = new TfsTeamProjectCollection(_collection);
    var vcs = tfs.GetService<VersionControlServer>();
    var user = vcs.AuthorizedUser;
    var workspace = vcs.GetWorkspace(workspaceName, user);
    var serverPath = workspace.GetServerItemForLocalItem(Path.Combine(localPath, projectName, "Main"));
    var itemSpec = new ItemSpec[] {
        new ItemSpec(serverPath, RecursionType.Full)
    };
    workspace.PendAdd(serverPath, true);

    // doesn't return anything
    var pendingSets = vcs.QueryPendingSets(
        itemSpec, workspace.Name, user, true);
    var pendingChanges = pendingSets.Aggregate(new List<PendingChange>(), (acc, item) => {
        acc.AddRange(item.PendingChanges);
        return acc;
    });
    var parameters = new WorkspaceCheckInParameters(pendingChanges, "svn to tfs migration") {
        OverrideGatedCheckIn = ((CheckInOptions2)vcs.SupportedFeatures & CheckInOptions2.OverrideGatedCheckIn) == CheckInOptions2.OverrideGatedCheckIn,
        PolicyOverride = new PolicyOverrideInfo("migration triggered check-in", null),
        SuppressEvent = true,
    };
    workspace.CheckIn(parameters);
}

workspace.PendAdd(serverPath, true) always returns zero for the second project, regardless of which project is second. The first project always completes properly. It doesn't matter which project is second. The second project always returns zero items. I obviously want all projects to be added to source control correctly. What is going on here?

标签: c# tfs-sdk
5条回答
Explosion°爆炸
2楼-- · 2020-04-16 17:55

I have figured out a way around this problem. I have to create a separate console application that contained the code in the AddToSourceControl method and call the console application via the System.Diagnostics.Process class. Something internally in the TFS API must not be getting released properly until the whole entire program exits.

查看更多
孤傲高冷的网名
3楼-- · 2020-04-16 17:56

I was stucked with the command PenAdd and, like Wobuntu, the command :

Workstation.Current.EnsureUpdateWorkspaceInfoCache( _versionControl, _versionControl.AuthenticatedUser);

solved the problem for me.

You need to call this method before the command PenAdd.

查看更多
Root(大扎)
4楼-- · 2020-04-16 17:57

I had the same problem (even the MSDN sample did not work on my machine), but listening to non fatal errors gave me the hint to invoke Workstation.EnsureUpdateWorkspaceInfoCache before calling PendAdd. This solved it for me.

查看更多
在下西门庆
5楼-- · 2020-04-16 17:57
Workstation.Current.EnsureUpdateWorkspaceInfoCache(
                                                _versionControl, 
                                                _versionControl.AuthenticatedUser
                                               );

This is the correct solution and it worked for me.

查看更多
【Aperson】
6楼-- · 2020-04-16 18:03

I don't know why your application is not working, but the TFS API is capable of creating multiple workspaces and checking in from them without any need to dispose anything or call the GC or anything strange like that.

Here's an example program, modified from the one you posted to the connect site, that exhibits correct behavior:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.IO;
using Microsoft.TeamFoundation.VersionControl.Common;

namespace AddToSourceControl
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Uri serverUri = new Uri(args[0]);
                string serverPath = args[1];
                string localPath = args[2];

                for (int i = 0; i < 5; i++)
                {
                    string uniqueId = Guid.NewGuid().ToString();

                    if (i > 0)
                    {
                        Console.WriteLine();
                    }

                    Console.WriteLine("Creating a workspace and checking in with id " + uniqueId);

                    TfsTeamProjectCollection connection = new TfsTeamProjectCollection(serverUri);
                    VersionControlServer vcs = connection.GetService<VersionControlServer>();

                    string uniqueServerPath = VersionControlPath.Combine(serverPath, uniqueId);
                    string uniqueFolder = Path.Combine(localPath, uniqueId);

                    Workspace workspace = vcs.CreateWorkspace(uniqueId, vcs.AuthorizedUser, "", new WorkingFolder[] {
                        new WorkingFolder(uniqueServerPath, uniqueFolder)
                    });

                    Console.WriteLine("Created TFS workspace " + uniqueId);

                    CheckinLocalFolder(serverUri, localPath, uniqueId);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Failed: " + e);
            }
        }

        private static void CheckinLocalFolder(Uri serverUri, string localPath, string uniqueId)
        {
            TfsTeamProjectCollection connection = new TfsTeamProjectCollection(serverUri);
            VersionControlServer vcs = connection.GetService<VersionControlServer>();

            string uniqueFolder = Path.Combine(localPath, uniqueId);
            string uniqueFile = Path.Combine(uniqueFolder, uniqueId + ".txt");

            Workspace workspace = vcs.GetWorkspace(uniqueFolder);

            Console.Out.WriteLine("Found workspace " + workspace.Name);

            // Create a local folder with a file in it
            Directory.CreateDirectory(uniqueFolder);
            using (TextWriter output = new StreamWriter(uniqueFile))
            {
                output.WriteLine("This is " + uniqueId);
                output.Close();
            }

            Console.WriteLine("Created file " + uniqueFile);

            workspace.PendAdd(uniqueFolder, true);

            PendingChange[] pendingChanges = workspace.GetPendingChanges();

            Console.WriteLine("Pended changes:");
            foreach (PendingChange pendingChange in pendingChanges)
            {
                Console.WriteLine(" " + pendingChange.LocalItem + " (" + pendingChange.ChangeType + ")");
            }

            int changeset = workspace.CheckIn(pendingChanges, "Test from id " + uniqueId);

            Console.WriteLine("Checked in " + pendingChanges.Length + " as changeset " + changeset);
        }
    }
}

So I would suggest that the problem lies elsewhere in your code. However, if you can produce a short, self-contained example that exhibits the problem without many layers of abstraction, that would be helpful.

Some helpful tips:

Make sure your workspace mappings are setup correctly: otherwise, calling PendAdd recursively will not, in fact, add anything.

Make sure the files exist locally: for the same reason.

Listen for errors: The TFS API has several events that it can notify consumers of - one that is particularly useful is the "non fatal" error notifications. In many operations, one part of an operation can fail, and instead of exiting or throwing an exception, a "non fatal" will be raised and the operation will continue.

An example of this is when you're adding multiple paths to PendAdd and one of them fails (for example, because a path is locked.) Without listening to non fatal errors, you will not know that this path is excluded from the pending changes. (Though you will know that a path is excluded, due to looking at the return code.)

If you have a VersionControlServer vcs:

public class Example
{
    static void Main(string[] args)
    {
        VersionControlServer vcs = ConnectToServer(); // ... etc ...
        vcs.NonFatalError += Example.OnNonFatalError;
    }

    internal static void OnNonFatalError(Object sender, ExceptionEventArgs e)
    {
        if (e.Exception != null)
        {
            Console.Error.WriteLine("  Non-fatal exception: " + e.Exception.Message);
        }
        else
        {
            Console.Error.WriteLine("  Non-fatal failure: " + e.Failure.Message);
        }
    }
}

(Note that this example was taken from Buck Hodges' helpful client API example blog post.)

查看更多
登录 后发表回答