I need all Fields for a TFS Project Collection without specifiying a workitem. I'm already using the TFS API for a few other things, but I haven't found anything on this.
What I found was using the witadmin.exe listfields command, which gives me exactly what I want, but how do I get the results as a collection in my code? I'm not very experienced with commands, I just need the Field IDs and Names in my code so that I can display them in a WPF Listview. Do I need to call cmd.exe in my code or does the TFS API have an extension method for this? My "teacher" said it should have one since it can be done with the witadmin.exe.
witadmin.exe is a .NET Executable, so you can use Reflector, dotPeek or ilSpy on it to see how Microsoft has implemented it. This seems to be the snippet you're after:
protected void InitFields()
{
if (this.fields == null)
{
FieldDefinitionCollection definitions = new FieldDefinitionCollection(this.Store, false);
List<FieldDefinition> list = new List<FieldDefinition>(definitions.Count);
Dictionary<string, FieldDefinition> dictionary = new Dictionary<string, FieldDefinition>(definitions.Count, this.Store.ServerStringComparer);
for (int i = 0; i < definitions.Count; i++)
{
FieldDefinition item = definitions[i];
if (!item.IsInternal)
{
list.Add(item);
dictionary[item.ReferenceName] = item;
dictionary[item.Name] = item;
}
}
list.Sort(new FieldComparer(this.Store.ServerStringComparer));
this.fields = list;
this.fieldsMap = dictionary;
}
}
It will generate a list of all available fields in the Collection.
this.Store is an instance of WorkItemStore.
Related
I would like to create tags in the Visual Studio editor to insert all sorts of glyphs, adornments, text hightlightings, etc., based on line/column/length locations in the code.
I have been carefully reading the documentation walkthrough pages (https://learn.microsoft.com/en-us/visualstudio/extensibility/walkthrough-creating-a-margin-glyph?view=vs-2017 and related pages). Although a bit complex and hard to understand, it seems like the API is very much oriented on giving the means to analyse the code: it is able to give your code split into spans, with classifications, etc.
However, I have the "opposite" need: I already have the analysis done by my external analysis engine. And I already have a set of results to be displayed in the editor with line/column/length for each one. Like:
function "foo", located at line 345, column 1, length 3, and other fields containing information to be displayed,
variable "my_var", located at line 349, column 13, length 6, and other fields containing information to be displayed,
Is it possible to create tags in the Visual Studio editor directly based on their line/column/length location? Any hint, any pointer to more detailed documentation or tutorial would be greatly appreciated.
Lance's link was quite helpful to understand another way to create tags different from the MS documentation example.
Indeed, I don't analyse the text contained into the spans, the analysis is already done outside. I get some list of "defects" locations.
I get them into a defectsLocation dictionary (defectsLocation[filename][line] = location data (...)
Here is was I did:
internal class MyDefectTagger : ITagger<MyDefectTag>
{
private IClassifier m_classifier;
private ITextBuffer m_buffer;
internal MyDefectTagger(IClassifier classifier, ITextBuffer buffer)
{
m_classifier = classifier;
m_buffer = buffer;
}
IEnumerable<ITagSpan<MyDefectTag>>
ITagger<MyDefectTag>.GetTags(NormalizedSnapshotSpanCollection spans)
{
if (MyModel.Instance == null || MyModel.Instance.defectsLocation == null)
{
yield return null;
}
var filename = GetFileName(m_buffer);
if (!MyModel.Instance.defectsLocation.ContainsKey(filename))
{
yield return null;
}
foreach (SnapshotSpan span in spans)
{
ITextSnapshot textSnapshot = span.Snapshot;
foreach (ITextSnapshotLine textSnapshotLine in textSnapshot.Lines)
{
var line = textSnapshotLine.LineNumber + 1; // Lines start at 1 in VS Editor
if (MyModel.Instance.defectsLocation[filename].ContainsKey(line) &&
!MyModel.Instance.defectsLocation[filename][line].rendered)
{
var rendered = MyModel.Instance.defectsLocation[filename][line].rendered;
yield return new TagSpan<MyDefectTag>(
new SnapshotSpan(textSnapshotLine.Start, 0),
new MyDefectTag()
);
}
}
}
}
}
I am using eBay API to list new / update existing product using C#. I need to know a way to get Item Specifics for an Item so that when I update the product, the specifics are not lost.
I am using DetailLevelCodeType.ReturnAll with GetSellerListCall() to bring down all details. What I understand from the documentation, if I use DetailLevelCodeType.ItemReturnAttributes using the GetItem() call, I can get the same.
My question is if we have any way to get all data including Item Specifics using the GetSellerList() call?
Below is the sample code I am using -
GetSellerListCall call = new GetSellerListCall(_context);
call.DetailLevelList.Add(DetailLevelCodeType.ReturnAll);
call.Pagination = new PaginationType() { EntriesPerPage = 20 };
call.EndTimeFrom = DateTime.UtcNow;
call.EndTimeTo = DateTime.UtcNow.AddDays(1);
do
{
call.Pagination.PageNumber++;
ItemTypeCollection items = call.GetSellerList();
foreach (ItemType item in items)
{
//Perform My Action
}
}
while (call.HasMoreItems);
Thanks in advance.
I did not find any solution with the GetSellerListCall() and hence moved on by making an extra call to GetItem() using the sample below. Hope this helps.
GetItemCall call = new GetItemCall(_context);
call.IncludeItemSpecifics = true;
call.DetailLevelList.Add(DetailLevelCodeType.ItemReturnAttributes);
ItemType _itemSpecific = call.GetItem('Item ID from eBay');
We recently upgraded from Ektron 8.6 to 9.0 (Ektron CMS400.NET, Version: 9.00 SP2(Build 9.0.0.249)).
I have some code (below) which we use to display links to items in a taxonomy. Under 8.6, this would show library items if they had been added to the taxonomy. As of 9.0, it no longer displays library items. It still works for DMS items and normal pages (all first class content in Ektron).
private List<ContentData> getTaxonomyItems(long TaxonomyId)
{
listContentManager = new ContentManager();
criteria = new ContentTaxonomyCriteria(ContentProperty.Id, EkEnumeration.OrderByDirection.Ascending);
criteria.PagingInfo = new Ektron.Cms.PagingInfo(400); // there's a lot of items and I don't want to page them.
criteria.AddFilter(TaxonomyId, true); // this gets sub taxonomies too :)
List<ContentData> contentList = listContentManager.GetList(criteria);
return contentList;
}
(I would love to simply say to users to use the DMS instead of the library, but we have a security requirement and I'm not aware of a way I can enforce security on DMS items like we can with library items by dropping a webconfig file in the library folder.)
Is this a bug that anyone else has experienced?
Or is there a problem with my code (did an API change in the upgrade to 9.0)?
Thanks.
I ended up emailing Ektron support in Sydney (I'm in Australia), and they said:
I would expect ContentManager to only return content, not library
items – must have been a loophole which is now closed. Taxonomy is the
way to go.
So I used some of the code they provided and came up with the following, which appears to work...
private List<TaxonomyItemData> getTaxonomyItems(long TaxonomyId)
{
List<TaxonomyItemData> list = new List<TaxonomyItemData>();
TaxonomyManager taxManager = new TaxonomyManager(Ektron.Cms.Framework.ApiAccessMode.Admin);
TaxonomyCriteria taxonomyCriteria = new Ektron.Cms.Organization.TaxonomyCriteria();
taxonomyCriteria.AddFilter(Ektron.Cms.Organization.TaxonomyProperty.Path,
Ektron.Cms.Common.CriteriaFilterOperator.StartsWith, GetTaxonomyPathById(TaxonomyId));
List<TaxonomyData> TaxonomyDataList = taxManager.GetList(taxonomyCriteria);
foreach (TaxonomyData taxd in TaxonomyDataList)
{
TaxonomyData taxTree = taxManager.GetTree(taxd.Path,
1, // depth. doesn't seem to work. have to manually tranverse lower taxonomies.
true, // include items
null,
Ektron.Cms.Common.EkEnumeration.TaxonomyType.Content,
Ektron.Cms.Common.EkEnumeration.TaxonomyItemsSortOrder.taxonomy_item_display_order);
foreach (TaxonomyItemData taxItem in taxTree.TaxonomyItems)
{
list.Add(taxItem);
}
}
return list;
}
private static String GetTaxonomyPathById(long taxonomyId)
{
TaxonomyManager tMgr = new TaxonomyManager();
TaxonomyData tData = tMgr.GetItem(taxonomyId);
if (tData != null)
{
return tData.Path;
}
return "";
}
This code fetches items for all the child taxonomies as well as returning library items.
The one problem is that it fetches duplicates for some items, but those are easy to clean out.
I was also told by Ektron that...
TaxonomyManager.GetItem(“{path}”) is a more efficient way to get the
categories
That's why I've included the GetTaxonomyPathById() method (inspired by this blog post: http://www.nimbleuser.com/blog/posts/2009/iterating-through-ektron-content-in-multiple-taxonomies-via-directly-interfacing-with-search-indexing-services/ )
I'm looking for a way to automatically produce a changelog (actually a list of workitems) between two releases of my application. I have two versions of my application, v1 and v2, each is identified by a label in TFS 2010 (LABEL1 and LABEL2) that I manually created before building the setups of my app.
I have a branching system, which means I have a trunk were most of bugs are fixed, and a branch where patches are applied mostly using merges from the trunk (but there are also some fixes on the branch only that do not concern the trunk). The two versions of my application (v1 and v2) are versions from the branch.
I would like TFS 2010 to be able to return the list of bugs that were fixed (ie. the list of work items with type = Bug that are closed and verified) between these two labels.
I tried to achieve this using the web UI of TFS 2010, or using Visual Studio, but I didn't find any way.
Then I tried to ask tf.exe for a history using the following command line:
tf history /server:http://server_url/collection_name "$/project_path" /version:LLABEL1~LLABEL2 /recursive /noprompt /format:brief
where LABEL1 is the label that has been associated with the source code of the v1 of the application, and LABEL2 the label that has been associated with the source code of the v2 of the application.
It actually fails in two ways:
- the command line only returns a list of changesets, not a list of associated closed work items
- the list of changesets only contains the changesets that I applied on the branch itself, not the changesets that I also applied and the trunk and then merged to the branch. Setting or not the "/slotmode" parameter doesn't change anything.
There I tried to write a piece of C# code to retrieve the list of workitems (not the list of changesets):
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://server_url/collection_name"));
VersionControlServer controlServer = tfs.GetService<VersionControlServer>();
VersionControlServer vcs = tfs.GetService<VersionControlServer>();
VersionSpec sFrom = VersionSpec.ParseSingleSpec("LLABEL1", null);
VersionSpec sTo = VersionSpec.ParseSingleSpec("LLABEL2", null);
var changesets = vcs.QueryHistory(
"$/project_path",
sTo,
0,
RecursionType.Full,
null,
sFrom,
sTo,
int.MaxValue,
true,
false); // Slotmode to false
Dictionary<int, WorkItem> dico = new Dictionary<int, WorkItem>();
foreach (Changeset set in changesets)
{
foreach (WorkItem zz in set.WorkItems)
{
if (!dico.ContainsKey(zz.Id))
{
dico.Add(zz.Id, zz);
}
}
}
foreach (KeyValuePair<int, WorkItem> pair in dico.OrderBy(z => z.Key))
{
Console.WriteLine(string.Format("ID: {0}, Title: {1}", pair.Key, pair.Value.Title));
}
This actually works, I get the list of workitems between my two labels which is actually what I wanted. But only workitems associated to changesets that were committed on the branch itself are taken into account: the workitems of type "Bug" that were solved on the trunk then merged to the branch don't appear. Slotmode doesn't change anything.
Then I finally tried to replace VersionSpecs that were defined by a label with VersionSpecs that are defined by changesets:
VersionSpec sFrom = VersionSpec.ParseSingleSpec("C5083", null);
VersionSpec sTo = VersionSpec.ParseSingleSpec("C5276", null);
And my code finally works.
So my question is: how could I get the same result with labels, which are the TFS objects I use to identify a version? If it's not possible, how should I identify a version in TFS 2010?
Thx.
Btw I found some questions on stackoverflow, but none of them gave me answers with labels. For instance:
Question example
I think http://tfschangelog.codeplex.com/ can possibly help you here.
TFS ChangeLog applicatoin allows users to automatically generate release notes from TFS. Users will have to provide information on thier project, branch and changeset range and then TFS ChangeLog application will extract information from each changeset in a given range and all the associated workitems to such changesets. i.e. it will travel from starting changeset upto ending changeset and will extract data about each changeset along with associated workitems in an XML file.
Users can then use their own transformation logic including filter, sorting, styling, output formatting, etc. to generate Release Notes Report.
Another thing I would like to add here will be related to Labels in TFS. Labels are basically assigned / associated with changesets. Currently, TFS ChangeLog application does not support Labels to define starting and ending point but it does support changeset which can be used as a workaround solution.
Hope this is useful.
In general, the absolute method of defining points in time in any SCM is clearly the checkin-id. Using labels to abstract this, is in TFS not the optimum as discussed here & here. A better approach is to use builds instead, especially in a modern CI environment.
In order to retrieve the max changeset that was contained in a given build you 'd have to do something like this:
using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
namespace GetChangesetsFromBuild
{
class Program
{
static void Main()
{
TfsTeamProjectCollection tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://TFSServer:8080/Name"));
IBuildServer bs = (IBuildServer)tpc.GetService(typeof(IBuildServer));
IBuildDetail build = bs.GetAllBuildDetails(new Uri("vstfs:///..."));
List<IChangesetSummary> associatedChangesets = InformationNodeConverters.GetAssociatedChangesets(build);
int idMax = associatedChangesets[0].ChangesetId;
}
}
}
A difficulty with the above is to retrieve the BuildUri of the builds you are interested in. In order to get this information you could do something like this:
IBuildDetail[] builds = bs.QueryBuilds("TeamPorjectName", "yourBuildDefinitionName")
and then retrieve the Uri's that are important to you.
This is also a good vehicle if you eventually insist on using labels: Besides Uri, each build[] has also a LabelName.
I have been in the same situation as you. I also want Work Items from merged changesets included. I only include Work Items that are Done. Also if the same Work Item is linked to multiple changesets, only the last changeset is reported. I use this in a CI setup; and create a changelog for each build. The List<ChangeInfo> can then be exported to a XML/HTML/TXT-file. Here is my solution:
namespace TFSChangelog
{
public class TFSChangelogGenerator
{
private const string workItemDoneText = "Done";
/// <summary>
/// This class describes a change by:
/// Changeset details
/// and
/// WorkItem details
/// </summary>
public class ChangeInfo
{
#region Changeset details
public DateTime ChangesetCreationDate { get; set; }
public int ChangesetId { get; set; }
#endregion
#region WorkItem details
public string WorkItemTitle { get; set; }
public int WorkItemId { get; set; }
#endregion
}
public static List<ChangeInfo> GetChangeinfo(string tfsServer, string serverPath, string from, string to)
{
// Connect to server
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsServer));
tfs.Connect(ConnectOptions.None);
var vcs = tfs.GetService<VersionControlServer>();
// Create versionspec's
VersionSpec versionFrom = null;
if (!string.IsNullOrEmpty(from))
versionFrom = VersionSpec.ParseSingleSpec(from, null);
VersionSpec versionTo = VersionSpec.Latest;
if (!string.IsNullOrEmpty(to))
versionTo = VersionSpec.ParseSingleSpec(to, null);
// Internally used dictionary
var changes = new Dictionary<int, ChangeInfo>();
// Find Changesets that are checked into the branch
var directChangesets = vcs.QueryHistory(
serverPath,
VersionSpec.Latest,
0,
RecursionType.Full,
null,
versionFrom,
versionTo,
Int32.MaxValue,
true,
false
).Cast<Changeset>();
foreach (var changeset in directChangesets)
{
foreach (var workItem in changeset.WorkItems.Where(workItem => workItem.State == workItemDoneText))
{
if (changes.ContainsKey(workItem.Id))
{
if (changeset.ChangesetId < changes[workItem.Id].ChangesetId) continue;
}
changes[workItem.Id] = new ChangeInfo { ChangesetId = changeset.ChangesetId, ChangesetCreationDate = changeset.CreationDate, WorkItemId = workItem.Id, WorkItemTitle = workItem.Title };
}
}
// Find Changesets that are merged into the branch
var items = vcs.GetItems(serverPath, RecursionType.Full);
foreach (var item in items.Items)
{
var changesetMergeDetails = vcs.QueryMergesWithDetails(
null,
null,
0,
item.ServerItem,
VersionSpec.Latest,
0,
versionFrom,
versionTo,
RecursionType.Full
);
foreach (var merge in changesetMergeDetails.Changesets)
{
foreach (var workItem in merge.WorkItems.Where(workItem => workItem.State == workItemDoneText))
{
if (changes.ContainsKey(workItem.Id))
{
if (merge.ChangesetId < changes[workItem.Id].ChangesetId) continue;
}
changes[workItem.Id] = new ChangeInfo { ChangesetId = merge.ChangesetId, ChangesetCreationDate = merge.CreationDate, WorkItemId = workItem.Id, WorkItemTitle = workItem.Title };
}
}
}
// Return a list sorted by ChangesetId
return (from entry in changes orderby entry.Value.ChangesetId descending select entry.Value).ToList();
}
}
}
This question got me closer to solving a similar problem I was having.
Use the type LabelVersionSpec instead of VersionSpec for label versions.
Replace:
VersionSpec sFrom = VersionSpec.ParseSingleSpec("LLABEL1", null);
VersionSpec sTo = VersionSpec.ParseSingleSpec("LLABEL2", null);
with:
LabelVersionSpec sFrom = new LabelVersionSpec("LLABEL1");
LabelVersionSpec sTo = new LabelVersionSpec("LLABEL2");
I'm encountering a problem with one of my IEnumerable's that I haven't seen before.
I have a collection:
IEnumerable<IDependency> dependencies;
that's being used in a foreach loop.
foreach (var dependency in dependencies)
For some reason this foreach doesn't iterate over my IEnumerable and simply skips to the end.
If I change my foreach to loop through a a list however it seems to work fine:
foreach (var dependency in dependencies.ToList())
What could I be doing that's causing this behaviour? I haven't experienced this with IEnumerable before.
Update:
Here's the entire code of my foreach that's running in my method GenerateDotString:
foreach (var dependency in dependencies)
{
var dependentResource = dependency.Resource;
var lineColor = (dependency.Type == DependencyTypeEnum.DependencyType.Hard) ? "blue" : "red";
output += labelFormat.FormatWith(dependentResource.Name.MakeDotsafeString(), dependentResource.Name, dependentResource.ResourceType);
output += relationshipFormat.FormatWith(dependentResource.Name.MakeDotsafeString(), currentName, lineColor);
if (dependentResource.DependentResources != null)
{
output += GenerateDotString(dependentResource, dependentResource.DependentResources, searchDirection);
}
}
return output;
Update 2:
Here's the signature of the method containing this foreach (incase it helps).
private static string GenerateDotString(IResource resource, IEnumerable<IDependency> dependencies, SearchEnums.SearchDirection searchDirection)
Update 3:
Here's the method GetAllRelatedResourcesByParentGuidWithoutCacheCheck:
private IEnumerable<IDependency> GetAllRelatedResourcesByParentGuidWithoutCacheCheck(Guid parentCiGuid, Func<Guid, IEnumerable<IDependency>> getResources)
{
if (!_itemsCheckedForRelations.Contains(parentCiGuid)) // Have we already got related resources for this CI?;
{
var relatedResources = getResources(parentCiGuid);
_itemsCheckedForRelations.Add(parentCiGuid);
if (relatedResources.Count() > 0)
{
foreach (var relatedResource in relatedResources)
{
relatedResource.Resource.DependentResources = GetAllRelatedResourcesByParentGuidWithoutCacheCheck(relatedResource.Resource.Id, getResources);
yield return relatedResource;
}
}
}
}
Update 4:
I'm adding the methods in the chain here to be clear on how we're getting the collection of dependencies.
The above method GetAllRelatedResourcesByParentGuidWithoutCacheCheck accepts a delegate which in this case is:
private IEnumerable<IDependency> GetAllSupportsResources(Guid resourceId)
{
var hardDependents = GetSupportsHardByParentGuid(resourceId);
var softDependents = GetSupportsSoftByParentGuid(resourceId);
var allresources = hardDependents.Union(softDependents);
return allresources;
}
which is calling:
private IEnumerable<IDependency> GetSupportsHardByParentGuid(Guid parentCiGuid)
{
XmlNode ciXmlNode = _reportManagementService.RunReportWithParameters(Res.SupportsHardReportGuid, Res.DependentCiReportCiParamName + "=" + parentCiGuid);
return GetResourcesFromXmlNode(ciXmlNode, DependencyTypeEnum.DependencyType.Hard);
}
and returns:
private IEnumerable<IDependency> GetResourcesFromXmlNode(XmlNode ciXmlNode, DependencyTypeEnum.DependencyType dependencyType)
{
var allResources = GetAllResources();
foreach (var nodeItem in ciXmlNode.SelectNodes(Res.WebServiceXmlRootNode).Cast<XmlNode>())
{
Guid resourceGuid;
var isValidGuid = Guid.TryParse(nodeItem.SelectSingleNode("ResourceGuid").InnerText, out resourceGuid);
var copyOfResource = allResources.Where(m => m.Id == resourceGuid).SingleOrDefault();
if (isValidGuid && copyOfResource != null)
{
yield return new Dependency
{
Resource = copyOfResource,
Type = dependencyType
};
}
}
}
which is where the concrete type is returned.
So it looks like the problem was to do with the dependencies collection infinately depending on itself.
It seems from my debugging that iterating the IEnumerable causes a timeout and so the foreach simply skips execution of its contents where as ToList() returns as much as it can before timing out.
I may not be correct about that but it's what seems to be the case as far as I can tell.
To give a bit of background as to how this all came about I'll explain the code changes I made yesterday.
The first thing the application does is build up a collection of all resources which are filtered by resource type. These are being brought in from our CMDB via a web service call.
What I was then doing is for each resource that was selected (via autocomplete in this case) I'd make a web service call and get the dependents for the resource based on its Guid. (recursively)
I changed this yesterday so that we didn't need to obtain the full resource information in this second web service call, rather, simply obtain a list of Guids in the web service call and grab the resources from our resources collection.
What I forgot was that the web service call for dependents wasn't filtered by type and so it was returning results that didn't exist in the original resources collection.
I need to look a bit further but it seems that at this point, the new collection of dependent resources was becoming dependent on itself and thus, causing the IEnumerable<IDependents> collection later on to timeout.
This is where I've got to today, if I find anything else I'll be sure to note it here.
To summarise this:
If infinite recursion occurs in an IEnumerable it'll simply timeout
when attempting to enumerate in a foreach.
Using ToList() on the IEnumerable seems to return as much data as it
can before timing out.