How programmatically check is project mapped in TFS? - c#

I need to find out is project mapped locally or not from the code. I can get all TFS project using Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory.GetTeamProjectCollection(), than I can do foreach for workItemStore = new WorkItemStore(projects) and get a lot of project information but anything like IsMapped or MappingPath.
Information I'm needed is easily accessed from Source Control Explorer of Team Explorer in Visual Studio, but I need to do it from C# code.
That is what I tried:
var projects = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(_tfsServerUri));
projects.Authenticate();
var workItemStore = new WorkItemStore(projects);
foreach (Project pr in workItemStore.Projects)
{
pr.IsLocal;
}
UPD: ANSWER
MikeR's answer is good, but I want to add that it has one defect. If you have your root directory mapped, but you don't actually have all projects from this root directory on your local computer, Miker's solution will still return all projects. If you don't want your code to act this way, here is my solution:
TfsTeamProjectCollection teamProjectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(_tfsServerUri));
teamProjectCollection.Authenticate();
VersionControlServer versionControl = teamProjectCollection.GetService<VersionControlServer>();
string computerName = Environment.MachineName;
WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent();
// get yours local workspaces
Workspace[] workspaces = versionControl.QueryWorkspaces(null, windowsIdentity.Name, computerName);
foreach (Project pr in workItemStore.Projects)
{
var mapped = false;
foreach (Workspace workspace in workspaces)
{
var path = workspace.TryGetLocalItemForServerItem("$/" + pr.Name);
if (!String.IsNullOrEmpty(path) && Directory.Exists(path))
{
mapped = true;
}
}
// do what you want with mapped project
}

This is more a generic approach, but I think you will manage to customize it for your needs (not compiled, just pointing to the direction):
string project = "TeamProject1";
string serverPath = "$/"+project;
string computername = "myComputer"; // possibly Environment.Computer or something like that
var tpc= TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(_tfsServerUri));
tpc.Authenticate();
// connect to VersionControl
VersionControlServer sourceControl = (VersionControlServer)tpc.GetService(typeof(VersionControlServer));
// iterate the local workspaces
foreach (Workspace workspace in sourceControl.QueryWorkspaces(null, null, computername))
{
// check mapped folders
foreach (WorkingFolder folder in workspace.Folders)
{
// e.g. $/TeamProject1 contains $/ if the root is mapped to local
if (serverPath.Contains(folder.ServerItem) && !folder.IsCloaked)
{
Console.WriteLine(serverPath + " is mapped under "+ folder.LocalItem);
Console.WriteLine("Workspacename: "+workspace.Name);
}
}
}

Related

How to call GetWorkspace in TFS properly?

Currently when I call GetWorkspace I get ItemNotMappedException exception, but when I iterate over workspaces manually I can get my code working. This is so bizarre that I am wondering if I should call some refresh or something before I call GetWorkspace?
Here is my code:
Workspace active = null;
using (var tfs = new TfsTeamProjectCollection(uri))
{
tfs.EnsureAuthenticated();
VersionControlServer vcs = tfs.GetService<VersionControlServer>();
// displaying info and manually finding desired workspace
foreach (var ws in vcs.QueryWorkspaces(null, vcs.AuthorizedUser, Environment.MachineName))
{
Console.WriteLine(ws.Name);
foreach (var f in ws.Folders)
{
Console.WriteLine($" {f.LocalItem}");
if (f.LocalItem == map_path)
active = ws;
}
}
// at this point workspace is found and I can work with it
// but this will crash
Workspace workspace = vcs.GetWorkspace(map_path);
}
I use VS2015 and the library for TFS is fetched from NuGet repo. It is "Microsoft.TeamFoundationServer.ExtendedClient" version "15.112.1".
The VersionControlServer.GetWorkspace method searches all known workspaces on the current computer to identify a workspace that has explicitly or implicitly mapped the provided local path. If no workspace is found, this method throws an ItemNotMappedException.
Try to call to Workstation.EnsureUpdateWorkspaceInfoCache in your code to force update the workspace information cache:
TfsTeamProjectCollection tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("<your-tfs-uri-here>"));
VersionControlServer tfServer = tpc.GetService<VersionControlServer>();
Workstation.Current.EnsureUpdateWorkspaceInfoCache(tfServer, tfServer.AuthorizedUser);
Have a look at this similar question: Microsoft.TeamFoundation.VersionControl.Client.ItemNotMappedException even when the workspace exists and has a mapping

Moving a SharePoint folder and contents to different location in same Document Library

I'm looking for a way to move a folder and all it's contents to a different location in the same library using the Client Object Model for SharePoint 2010 (C#).
For example we have a folder for a project (say 12345) and it's URL is
http://sharepoint/site/library/2012/12345
where 2012 represents a year. I'd like to programmatically move the 12345 folder to a different year, say 2014 which probably exists already but may not.
I've searched around but the solutions I'm getting seem extremely complicated and relevant to moving folders to different site collections, I'm hoping because it's in the same library there might be a simpler solution? One idea I have is to rely on Explorer View instead of CSOM?
Thanks a lot!
There is no built-in method in SharePoint CSOM API for moving Folder with Files from one location into another.
The following class represents how to move files from source folder into destination folder:
public static class FolderExtensions
{
public static void MoveFilesTo(this Folder folder, string folderUrl)
{
var ctx = (ClientContext)folder.Context;
if (!ctx.Web.IsPropertyAvailable("ServerRelativeUrl"))
{
ctx.Load(ctx.Web, w => w.ServerRelativeUrl);
}
ctx.Load(folder, f => f.Files, f => f.ServerRelativeUrl, f => f.Folders);
ctx.ExecuteQuery();
//Ensure target folder exists
EnsureFolder(ctx.Web.RootFolder, folderUrl.Replace(ctx.Web.ServerRelativeUrl, string.Empty));
foreach (var file in folder.Files)
{
var targetFileUrl = file.ServerRelativeUrl.Replace(folder.ServerRelativeUrl, folderUrl);
file.MoveTo(targetFileUrl, MoveOperations.Overwrite);
}
ctx.ExecuteQuery();
foreach (var subFolder in folder.Folders)
{
var targetFolderUrl = subFolder.ServerRelativeUrl.Replace(folder.ServerRelativeUrl,folderUrl);
subFolder.MoveFilesTo(targetFolderUrl);
}
}
public static Folder EnsureFolder(Folder parentFolder, string folderUrl)
{
var ctx = parentFolder.Context;
var folderNames = folderUrl.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
var folderName = folderNames[0];
var folder = parentFolder.Folders.Add(folderName);
ctx.Load(folder);
ctx.ExecuteQuery();
if (folderNames.Length > 1)
{
var subFolderUrl = string.Join("/", folderNames, 1, folderNames.Length - 1);
return EnsureFolder(folder, subFolderUrl);
}
return folder;
}
}
Key points:
allows to ensure whether destination folder(s) exists
In case of nested folders, its structure is preserved while moving files
Usage
var srcFolderUrl = "/news/pages";
var destFolderUrl = "/news/archive/pages";
using (var ctx = new ClientContext(url))
{
var sourceFolder = ctx.Web.GetFolderByServerRelativeUrl(srcFolderUrl);
sourceFolder.MoveFilesTo(destFolderUrl);
sourceFolder.DeleteObject(); // delete source folder if nessesary
ctx.ExecuteQuery();
}
Just in case someone needs this translated to PnP PowerShell. It's not battle tested but works for me. Versions and metadata moved as well within the same library.
$list = Get-PnPList -Identity Documents
$web = $list.ParentWeb
$folder = Ensure-PnPFolder -Web $list.ParentWeb -SiteRelativePath "Shared Documents/MoveTo"
$tofolder = Ensure-PnPFolder -Web $list.ParentWeb -SiteRelativePath "Shared Documents/MoveTwo"
function MoveFolder
{
[cmdletbinding()]
Param (
$web,
$fromFolder,
$toFolder
)
$fromFolder.Context.Load($fromFolder.Files)
$fromFolder.Context.Load($fromFolder.Folders)
$fromFolder.Context.ExecuteQuery()
foreach ($file in $fromFolder.Files)
{
$targetFileUrl = $file.ServerRelativeUrl.Replace($fromFolder.ServerRelativeUrl, $toFolder.ServerRelativeUrl);
$file.MoveTo($targetFileUrl, [Microsoft.SharePoint.Client.MoveOperations]::Overwrite);
}
$fromFolder.Context.ExecuteQuery();
foreach ($subFolder in $fromFolder.Folders)
{
$targetFolderUrl = $subFolder.ServerRelativeUrl.Replace($fromFolder.ServerRelativeUrl, $toFolder.ServerRelativeUrl);
$targetFolderRelativePath = $targetFolderUrl.SubString($web.RootFolder.ServerRelativeUrl.Length)
$tofolder = Ensure-PnPFolder -Web $list.ParentWeb -SiteRelativePath $targetFolderRelativePath
MoveFolder -Web $web -fromFolder $subFolder -toFolder $tofolder
}
}
$web.Context.Load($web.RootFolder)
$web.Context.ExecuteQuery()
MoveFolder -Web $web -fromFolder $folder -toFolder $tofolder
$folder.DeleteObject()
$web.Context.ExecuteQuery()

Copying/over-writing one source to another in TFS?

Question Background:
Within my TF Server I have two folders, one is a simple 'HelloWorld.sln' in a folder called 'HelloWorldDev' and the other is a 'HelloWorld.sln' in a folder called 'HelloWorldQA'. Each folder contains its respective .cs files etc.
I want to checkout a file from the HelloWorld QA folder, replace - or update it - with a version from the HelloWorldDev folder with the same file name, then check this file back into the HelloWorldQA folder with the relevant changes.
Question:
I am very new to the TFS API so I'm not 100% if what I'm trying to ask is the correct way to proceed, or if its even possible. Can someone give me an example of achieving this?
Code so far:
string fileName = #"C:\Users\Me\Documents\TfsServer\HelloWorldQA\IHelloWorld.cs";
string fileNameQA = #"C:\Users\Me\Documents\TfsServer\HelloWorld\IHelloWorld.cs";
string uri = #"https://tfsServer.visualstudio.com/";
var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(fileName);
var server = new TfsTeamProjectCollection(workspaceInfo.ServerUri);
var workspace = workspaceInfo.GetWorkspace(server);
workspace.PendEdit(fileName);
FileInfo fi = new FileInfo(fileName);
var workspaceInfoQA = Workstation.Current.GetLocalWorkspaceInfo(fileNameQA);
var serverQA = new TfsTeamProjectCollection(workspaceInfo.ServerUri);
var workspaceQA = workspaceInfo.GetWorkspace(serverQA);
workspace.PendEdit(fileNameQA);
FileInfo fiQA = new FileInfo(fileNameQA);
First, instead of using 2 workspaces, you can simply map both folders in the same workspace.
Then you're looking for a merge operation:
var sourcePath = workspace.GetServerItemForLocalItem(fileName);
var targetPath = workspace.GetServerItemForLocalItem(fileNameQA);
var getStatus = workspace.Merge(sourcePath, targetPath, null, null);
if (getStatus.NumUpdated > 0)
{
//OK
}

TFS PendEdit results in TF14098 : Access Denied

I like to checkout a file trough C# code, but I always get a TF14098 Acess Denied Exception. The TFS Administrator reassures, that I have checkout/edit permissions and in VisualStudio and with tf.exe I do not have any problems to checkout the file.
This is my current C# code:
const string tfsServer = #"http://MyServer:8080/tfs";
const string fileName = #"$/MyFile.xaml";
const string workspaceName = "MyWorkspace";
using (TfsTeamProjectCollection tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsServer)))
{
if (tfs != null)
{
WorkspaceInfo workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(fileName);
if (null != workspaceInfo)
{
var vcs = tfs.GetService<VersionControlServer>();
Workspace workspace = workspaceInfo.GetWorkspace(tfs);
if(workspace == null){
workspace = vcs.GetWorkspace(workspaceName, vcs.AuthorizedUser);
}
vcs.NonFatalError += VersionControlOnNonFatalError;
vcs.Getting += OnGetting;
vcs.BeforeCheckinPendingChange += OnBeforeCheckinPendingChange;
vcs.NewPendingChange += OnNewPendingChange;
if (workspace != null)
{
workspace.PendEdit(fileName);
}
}
}
}
It results always with:
Non-fatal failure: TF14098: Access Denied: User Me needs PendChange permission(s) for $/MyFile.xaml.
After a lot of research for this error I tryed to check the permissions trough Dos-Box and I did the following commandline:
tf checkout $/MyFile.xaml
it results in a Checkout of the file without any problem! (If I am in the directory where the file resists)
Has anyone a Idea what the problem may be? What's the difference between my code (my Application written and executed with VisualStudio2012) and the commandline tf checkout?
Thanks for tips and hints!
Patrik
The problem in your code is, that the TfsTeamProjectCollection does not fit to your path, because you never set the Collection name (should be something like #"http://MyServer:8080/tfs/DefaultCollection").
I never did a checkout by using the API, but I did a checkin starting like this:
WorkspaceInfo wi = Workstation.Current.GetLocalWorkspaceInfo(Environment.CurrentDirectory);
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(wi.ServerUri);
VersionControlServer versionControlServer = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
Workspace workSpace = versionControlServer.GetWorkspace(wi);
As you can see, I search the TeamCollection by using my workspace and not setting it independent. Doing it this way, you will get the right VersionControlServer to do your checkout.
The difference by using the tf.exe tool is, that you run it in your local workspace, so the tool knows to which item it is linked in TFS and to which Collection.

TFS / File Checkout from C#

I don't have a great deal of experience with TFS, other than using it for source control. I am working on a C# application that will need to modify files that are being controlled by TFS. From within my C# application, how can I check out a file that is controlled via TFS?
Thanks - Randy
You can use PendEdit to make your files writables, make your changes to it, then you add it to the pending changes, and finally check it in.
Here is some code where a folder structure is created and then checked in (Very similar to what you will need).
private static void CreateNodes(ItemCollection nodes)
{
using (var tfs = TeamFoundationServerFactory.GetServer("http://tfsserver:8080"))
{
var versionControlServer = tfs.GetService(typeof (VersionControlServer)) as VersionControlServer;
versionControlServer.NonFatalError += OnNonFatalError;
// Create a new workspace for the currently authenticated user.
var workspace = versionControlServer.CreateWorkspace("Temporary Workspace", versionControlServer.AuthenticatedUser);
try
{
// Check if a mapping already exists.
var workingFolder = new WorkingFolder("$/testagile", #"c:\tempFolder");
// Create the mapping (if it exists already, it just overides it, that is fine).
workspace.CreateMapping(workingFolder);
// Go through the folder structure defined and create it locally, then check in the changes.
CreateFolderStructure(workspace, nodes, workingFolder.LocalItem);
// Check in the changes made.
workspace.CheckIn(workspace.GetPendingChanges(), "This is my comment");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// Cleanup the workspace.
workspace.Delete();
// Remove the temp folder used.
Directory.Delete("tempFolder", true);
}
}
}
private static void CreateFolderStructure(Workspace workspace, ItemCollection nodes, string initialPath)
{
foreach (RadTreeViewItem node in nodes)
{
var newFolderPath = initialPath + #"\" + node.Header;
Directory.CreateDirectory(newFolderPath);
workspace.PendAdd(newFolderPath);
if (node.HasItems)
{
CreateFolderStructure(workspace, node.Items, newFolderPath);
}
}
}
Using the other solution gave me permission problems.
Here's an alternative way to checkout your files using tf.exe:
//Checkout file
Process proc = new Process();
proc.StartInfo =
new ProcessStartInfo(
#"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe",
string.Format("checkout \"{0}\"", fileLocation)
);
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
For those looking to use the first solution and resolve the permission issue you can use the following code to use the current credentials, this replaces the "TeamFoundationServerFactory.GetServer" call then use the TfsTeamProjectCollection (tmPrjColl) to get the VersionControlServer:
using Microsoft.TeamFoundation.Client;
using MTVC = Microsoft.TeamFoundation.VersionControl.Client;
using MVSC = Microsoft.VisualStudio.Services.Common;
MVSC.VssCredentials creds = new MVSC.VssCredentials(new MVSC.WindowsCredential(true));
using (TfsTeamProjectCollection tmPrjColl = new TfsTeamProjectCollection(new Uri("<source control URL>"), creds))
{
MTVC.VersionControlServer verCtrlSvr = tmPrjColl.GetService<MTVC.VersionControlServer>();
...
}

Categories