Заметки консультанта

Шамрай Александр Владимирович

Практическое руководство по отчетности TFS. Приложение.

Posted by Шамрай Александр на Январь 16, 2014

<<Содержание

Пример кода – настройка OData

WorkItemRevision

Code 1 – WorkItemRevision

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Entities

{

using System;

using System.Collections.Generic;

using System.Data.Services;

using System.Data.Services.Common;

using System.Reflection;

using Microsoft.Data.Services.Toolkit.QueryModel;

//Field references:

//http://msdn.microsoft.com/en-us/library/ms194971.aspx

[DataServiceKey(«Id»)]

[ETag(«ChangedDate»)]

[EntityPropertyMapping(«ChangedDate», SyndicationItemProperty.Updated, SyndicationTextContentKind.Plaintext, true)]

[EntityPropertyMapping(«Title», SyndicationItemProperty.Title, SyndicationTextContentKind.Plaintext, true)]

[EntityPropertyMapping(«Description», SyndicationItemProperty.Summary, SyndicationTextContentKind.Plaintext, true)]

public class WorkItemRevision

{

//Base information to provide in addition to WorkItem fields

public int Id { get; set; }

public string Project { get; set; }

public string Type { get; set; }

public string WebEditorUrl { get; set; }

//Base fields

public string AreaPath { get; set; }

public string IterationPath { get; set; }

public int Revision { get; set; } //aka Rev

public string Priority { get; set; }

public string Severity { get; set; }

public double StackRank { get; set; }

public string AssignedTo { get; set; }

public DateTime CreatedDate { get; set; }

public string CreatedBy { get; set; }

public DateTime ChangedDate { get; set; }

public string ChangedBy { get; set; }

public string ResolvedBy { get; set; }

public string Title { get; set; }

//valid states and transitions specific to template and work item

public string State { get; set; }

public string Reason { get; set; }

public double CompletedWork { get; set; }

public double RemainingWork { get; set; }

public string Description { get; set; }

public string ReproSteps { get; set; }

public string FoundInBuild { get; set; }

public string IntegratedInBuild { get; set; }

public int AttachedFileCount { get; set; }

public int HyperLinkCount { get; set; }

public int RelatedLinkCount { get; set; }

//Agile

public string Risk { get; set; }

public double StoryPoints { get; set; }

//Agile and CMMI

public double OriginalEstimate { get; set; }

//Scrum

public double BacklogPriority { get; set; }

public int BusinessValue { get; set; }

public double Effort { get; set; }

//Scrum and CMMI

public string Blocked { get; set; }

//CMMI

public double Size { get; set; }

[ForeignProperty]

public IEnumerable<Attachment> Attachments { get; set; }

[ForeignProperty]

public IEnumerable<Link> Links { get; set; }

public void Set(WorkItem w)

{

foreach (PropertyInfo p in w.GetType().GetProperties())

{

SetField(p.Name, p.GetValue(w, null));

}

}

public void SetField(string field, object value)

{

if (this.GetType().GetProperty(field) != null)

{

if (value != null)

{

this.GetType().GetProperty(field).SetValue(this, System.Convert.ChangeType(value, this.GetType()

.GetProperty(field) .PropertyType), null);

}

else

{

this.GetType().GetProperty(field).SetValue(this, value, null);

}

}

}

}

}

WorkItemHistoryProxy

Code 2 – WorkItemHistoryProxy

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Serialization

{

using System;

using System.Collections.Generic;

using System.Globalization;

using System.Linq;

using System.Net;

using System.Linq.Expressions;

using System.Text;

using System.Text.RegularExpressions;

using System.Web;

using Microsoft.Samples.DPE.ODataTFS.Model.Entities;

using Microsoft.Samples.DPE.ODataTFS.Model.ExpressionVisitors;

using System.Collections;

using Microsoft.Data.Services.Toolkit.QueryModel;

using Microsoft.TeamFoundation.WorkItemTracking;

public class TFSWorkItemHistoryProxy : TFSBaseProxy, ITfsWorkItemHistoryProxy

{

public TFSWorkItemHistoryProxy(Uri uri, ICredentials credentials)

: base(uri, credentials){}

public WorkItem GetWorkItem(int workItemId)

{

var wiql = string.Format(CultureInfo.InvariantCulture, «SELECT [System.Id] FROM WorkItems WHERE [System.Id] = {0}», workItemId);

return this.QueryWorkItems(wiql)

.Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>()

.Select(w => w.ToModel(this.GetTfsWebAccessArtifactUrl(w.Uri)))

.FirstOrDefault();

}

public IEnumerable<WorkItemRevision> GetWorkItemHistoryByProject(string projectName, FilterNode rootFilterNode,

ODataQueryOperation operation)

{

var key = string.Format(CultureInfo.InvariantCulture, «TFSWorkItemProxy.GetWorkItemsByProject_{0}_{1}», projectName, this.GetFilterNodeKey(rootFilterNode));

HttpContext.Current.Items[key] = this.RequestWorkItemsByProject(projectName, rootFilterNode, operation);

return (IEnumerable<WorkItemRevision>)HttpContext.Current.Items[key];

}

private static string BuildWiql(FilterNode rootFilterNode, ODataQueryOperation operation)

{

var constrains = string.Empty;

if (rootFilterNode != null)

{

foreach (var filterNode in rootFilterNode)

{

constrains += AddComparisonConstrainToWiql(filterNode, Constants.TFS.FieldLookup(filterNode.Key));

}

}

if (constrains.StartsWith(» OR «, StringComparison.OrdinalIgnoreCase))

{

constrains = constrains.Substring(» OR «.Length);

}

if (constrains.StartsWith(» AND «, StringComparison.OrdinalIgnoreCase))

{

constrains = constrains.Substring(» OR «.Length);

}

var wiql = string.Format(CultureInfo.InvariantCulture, «SELECT [System.Id] FROM WorkItems {0} {1}», string.IsNullOrEmpty(constrains) ? string.Empty : «WHERE», constrains).Trim();

string orderByWiql = string.Empty;

if (operation.OrderStack.Count > 0)

{

orderByWiql = TFSWorkItemHistoryProxy.GenerateOrderByWiql(operation.OrderStack);

}

wiql = wiql + » « + orderByWiql;

return wiql;

}

private static string GenerateOrderByWiql(Stack<ODataOrderExpression> orderExpressionStack)

{

if (orderExpressionStack == null)

{

throw new ArgumentNullException(«orderExpressionStack»);

}

StringBuilder finalOrderBy = new StringBuilder(«ORDER BY «);

//When the $top param is used with WCF Data Services Toolkit, this appears to

//also imply ordering by key ascending (in this case work item ID), so if the

//user also issues an explicit $orderby param for Id there will be a duplicate.

//It appears that the first $oderby value (top of stack) is the one provided

//explicitly by the user, so we take that one and track that it has been seen

//already. Subsequent order by fields are ignored.

ISet<string> seen = new HashSet<string>();

while (orderExpressionStack.Count > 0)

{

ODataOrderExpression expr = orderExpressionStack.Pop();

UnaryExpression unExpr = ((UnaryExpression)expr.Expression);

var opString = unExpr.Operand.ToString();

var name = opString.Substring(opString.IndexOf(‘.’) + 1);

name = Constants.TFS.FieldLookup(name);

if (!seen.Contains(name))

{

if (expr.OrderMethodName.StartsWith(«ThenBy»))

{

finalOrderBy.Append(» , «);

}

finalOrderBy.Append(name);

switch (expr.OrderMethodName)

{

case «OrderBy»:

case «ThenBy»:

finalOrderBy.Append(» asc»);

break;

case «OrderByDescending»:

case «ThenByDescending»:

finalOrderBy.Append(» desc»);

break;

}

seen.Add(name);

}

}

return finalOrderBy.ToString();

}

private static string AddComparisonConstrainToWiql(FilterNode filterNode, string tfsFieldName)

{

if (filterNode != null)

{

var sign = default(string);

switch (filterNode.Sign)

{

case FilterExpressionType.Equal:

sign = «=»;

break;

case FilterExpressionType.NotEqual:

sign = «<>»;

break;

case FilterExpressionType.GreaterThan:

sign = «>»;

break;

case FilterExpressionType.GreaterThanOrEqual:

sign = «>=»;

break;

case FilterExpressionType.LessThan:

sign = «<«;

break;

case FilterExpressionType.LessThanOrEqual:

sign = «<=»;

break;

case FilterExpressionType.Contains:

sign = filterNode.Key.Equals(«AreaPath», StringComparison.OrdinalIgnoreCase) || filterNode.Key.Equals(«IterationPath», StringComparison.OrdinalIgnoreCase) ? «UNDER» : «CONTAINS»;

break;

case FilterExpressionType.NotContains:

sign = filterNode.Key.Equals(«AreaPath», StringComparison.OrdinalIgnoreCase) || filterNode.Key.Equals(«IterationPath», StringComparison.OrdinalIgnoreCase) ? «NOT UNDER» : «NOT CONTAINS»;

break;

default:

throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, «WorkItem {0} can only be filtered with equal, not equal, greater than, lower than, greater than or equal, lower than or equal operators», filterNode.Key));

}

return string.Format(CultureInfo.InvariantCulture, » {0} {1} {2} ‘{3}’ «, filterNode.NodeRelationship.ToString(), tfsFieldName, sign, filterNode.Value);

}

return string.Empty;

}

private IEnumerable<WorkItemRevision> RequestWorkItemsByProject(string projectName, FilterNode rootFilterNode, ODataQueryOperation operation)

{

FilterNode newFilterNodeStartWithProject = new FilterNode() { Key = «Project, Sign = FilterExpressionType.Equal, Value = projectName };

if (rootFilterNode != null)

{

newFilterNodeStartWithProject.AddNode(rootFilterNode);

}

var wiql = BuildWiql(newFilterNodeStartWithProject, operation);

return this.ExecuteWiqlRequest(wiql, operation);

}

private IEnumerable<WorkItemRevision> ExecuteWiqlRequest(string wiql, ODataQueryOperation operation)

{

IEnumerable<WorkItemRevision> retWorkItems = null;

if (!operation.IsCountRequest)

{

if (operation.TopCount == 0)

{

//workaround for bug (I think) in WCF Data Services Toolkit.

//It appears that ODataQueryOperation.TopCount will be 0 when $select

// param is used and $top param is not explicitly sent by client

//(normally TopCount would have whatever was set for entity page size in //TFSService.InitializeService()

operation.TopCount = Constants.DefaultEntityPageSize;

}

IEnumerable<TeamFoundation.WorkItemTracking.Client.WorkItem> wilist = this.QueryWorkItems(wiql)

.Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>()

.Skip(operation.SkipCount).Take(operation.TopCount)

.Select(w => w).ToArray();

List<WorkItemRevision> lst = new List<WorkItemRevision>();

foreach (TeamFoundation.WorkItemTracking.Client.WorkItem w in wilist)

{

// Get All work item revisions

foreach (TeamFoundation.WorkItemTracking.Client.Revision revision in w.Revisions)

{

WorkItemRevision wrkRev = new WorkItemRevision();

wrkRev.Set(

w.ToModel(this.GetTfsWebAccessArtifactUrl(w.Uri))

);

wrkRev.Revision = revision.Index;

// Get value of fields in the work item revision

foreach (TeamFoundation.WorkItemTracking.Client.Field field in w.Fields)

{

wrkRev.SetField(field.Name, revision.Fields[field.Name].Value);

}

lst.Add(wrkRev);

}

}

}

else

{

var workItemServer = this.TfsConnection.GetService<TeamFoundation .WorkItemTracking.Client.WorkItemStore>();

try

{

Microsoft.TeamFoundation.WorkItemTracking.Client.Query q =

new TeamFoundation.WorkItemTracking.Client.Query(workItemServer, wiql, null, false);

int cnt = q.RunCountQuery();

List<WorkItemRevision> wiBlanks = new List<WorkItemRevision>(cnt);

WorkItemRevision blank = new WorkItemRevision();

for (int i = 0; i < cnt; i++)

{

wiBlanks.Add(blank);

}

retWorkItems = wiBlanks;

}

catch (Microsoft.TeamFoundation.WorkItemTracking.Client.ValidationException ex)

{

throw new System.Data.Services.DataServiceException(500, «Internal Server Error», ex.Message, «en-US», ex);

}

}

return retWorkItems;

}

}

}

WorkItemRevisionRepositories

Code 3 – WorkItemRevisionRepositories

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Repositories

{

using System;

using System.Collections;

using System.Collections.Generic;

using System.Data.Services;

using System.Globalization;

using System.Linq;

using Microsoft.Data.Services.Toolkit.QueryModel;

using Microsoft.Samples.DPE.ODataTFS.Model.Entities;

using Microsoft.Samples.DPE.ODataTFS.Model.ExpressionVisitors;

using Microsoft.Samples.DPE.ODataTFS.Model.Serialization;

public class WorkItemHistoryRepository : IRepository<WorkItemRevision>

{

private readonly ITfsWorkItemHistoryProxy proxy;

public WorkItemHistoryRepository(ITfsWorkItemHistoryProxy proxy)

{

this.proxy = proxy;

}

public IEnumerable<WorkItemRevision> GetAll()

{

List<WorkItemRevision> lst = new List<WorkItemRevision>();

return lst;

}

public WorkItemRevision GetOne(string id)

{

return null;

}

[RepositoryBehavior(HandlesFilter = true, HandlesSkip = true, HandlesTop = true, HandlesOrderBy = true)]

public IEnumerable<WorkItemRevision> GetWorkItemHistoryByProject(ODataSelectManyQueryOperation operation)

{

if (operation == null)

{

throw new ArgumentNullException(«operation»);

}

var parameters = new WorkItemFilterExpressionVisitor(operation.FilterExpression).Eval();

return this.proxy.GetWorkItemHistoryByProject(operation.Key, parameters, operation);

}

}

}

TFSWorkItemFactProxy

Code 4 – WorkItemFactProxy

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Serialization

{

using System;

using System.Collections.Generic;

using System.Globalization;

using System.Linq;

using System.Net;

using System.Linq.Expressions;

using System.Text;

using System.Text.RegularExpressions;

using System.Web;

using Microsoft.Samples.DPE.ODataTFS.Model.Entities;

using Microsoft.Samples.DPE.ODataTFS.Model.ExpressionVisitors;

using System.Collections;

using Microsoft.Data.Services.Toolkit.QueryModel;

using Microsoft.TeamFoundation.WorkItemTracking;

public class TFSWorkItemFactProxy : TFSBaseProxy, ITfsWorkItemFactProxy

{

public TFSWorkItemFactProxy(Uri uri, ICredentials credentials)

: base(uri, credentials)

{

}

public WorkItem GetWorkItem(int workItemId)

{

var wiql = string.Format(CultureInfo.InvariantCulture, «SELECT [System.Id] FROM WorkItems WHERE [System.Id] = {0}», workItemId);

return this.QueryWorkItems(wiql)

.Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>()

.Select(w => w.ToModel(this.GetTfsWebAccessArtifactUrl(w.Uri)))

.FirstOrDefault();

}

public IEnumerable<WorkItemFact> GetWorkItemFactByProject(string projectName, FilterNode rootFilterNode, ODataQueryOperation operation)

{     

var key = string.Format(CultureInfo.InvariantCulture, «TFSWorkItemFactProxy.GetWorkItemsFactByProject_{0}_{1}», projectName, this.GetFilterNodeKey(rootFilterNode));

HttpContext.Current.Items[key] = this.RequestWorkItemFactsByProject(projectName, rootFilterNode, operation);

return (IEnumerable<WorkItemFact>)HttpContext.Current.Items[key];

}

private static string BuildWiql(FilterNode rootFilterNode, ODataQueryOperation operation)

{

var constrains = string.Empty;

if (rootFilterNode != null)

{

foreach (var filterNode in rootFilterNode)

{

constrains += AddComparisonConstrainToWiql(filterNode, Constants.TFS.FieldLookup(filterNode.Key));

}

}

if (constrains.StartsWith(» OR «, StringComparison.OrdinalIgnoreCase))

{

constrains = constrains.Substring(» OR «.Length);

}

if (constrains.StartsWith(» AND «, StringComparison.OrdinalIgnoreCase))

{

constrains = constrains.Substring(» OR «.Length);

}

var wiql = string.Format(CultureInfo.InvariantCulture, «SELECT [System.Id] FROM WorkItems {0} {1}», string.IsNullOrEmpty(constrains) ? string.Empty : «WHERE», constrains).Trim();

string orderByWiql = string.Empty;

if (operation.OrderStack.Count > 0)

{

orderByWiql = TFSWorkItemFactProxy.GenerateOrderByWiql(operation.OrderStack);

}

wiql = wiql + » « + orderByWiql;

return wiql;

}

private static string GenerateOrderByWiql(Stack<ODataOrderExpression> orderExpressionStack)

{

if (orderExpressionStack == null)

{

throw new ArgumentNullException(«orderExpressionStack»);

}

StringBuilder finalOrderBy = new StringBuilder(«ORDER BY «);

//When the $top param is used with WCF Data Services Toolkit, this appears to also imply

//ordering by key ascending (in this case work item ID), so if the user also issues an

//explicit $orderby param for Id there will be a duplicate. It appears that the first

//$oderby value (top of stack) is the one provided explicitly by the user, so we take that

//one and track that it has been seen already. Subsequent order by fields are ignored.

ISet<string> seen = new HashSet<string>();

while (orderExpressionStack.Count > 0)

{

ODataOrderExpression expr = orderExpressionStack.Pop();

UnaryExpression unExpr = ((UnaryExpression)expr.Expression);

var opString = unExpr.Operand.ToString();

var name = opString.Substring(opString.IndexOf(‘.’) + 1);

name = Constants.TFS.FieldLookup(name);

if (!seen.Contains(name))

{

if (expr.OrderMethodName.StartsWith(«ThenBy»))

{

finalOrderBy.Append(» , «);

}

finalOrderBy.Append(name);

switch (expr.OrderMethodName)

{

case «OrderBy»:

case «ThenBy»:

finalOrderBy.Append(» asc»);

break;

case «OrderByDescending»:

case «ThenByDescending»:

finalOrderBy.Append(» desc»);

break;

}

seen.Add(name);

}

}

return finalOrderBy.ToString();

}

private static string AddComparisonConstrainToWiql(FilterNode filterNode, string tfsFieldName)

{

if (filterNode != null)

{

var sign = default(string);

switch (filterNode.Sign)

{

case FilterExpressionType.Equal:

sign = «=»;

break;

case FilterExpressionType.NotEqual:

sign = «<>»;

break;

case FilterExpressionType.GreaterThan:

sign = «>»;

break;

case FilterExpressionType.GreaterThanOrEqual:

sign = «>=»;

break;

case FilterExpressionType.LessThan:

sign = «<«;

break;

case FilterExpressionType.LessThanOrEqual:

sign = «<=»;

break;

case FilterExpressionType.Contains:

sign = filterNode.Key.Equals(«AreaPath», StringComparison.OrdinalIgnoreCase) || filterNode.Key.Equals(«IterationPath», StringComparison.OrdinalIgnoreCase) ? «UNDER» : «CONTAINS»;

break;

case FilterExpressionType.NotContains:

sign = filterNode.Key.Equals(«AreaPath», StringComparison.OrdinalIgnoreCase) || filterNode.Key.Equals(«IterationPath», StringComparison.OrdinalIgnoreCase) ? «NOT UNDER» : «NOT CONTAINS»;

break;

default:

throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, «WorkItem {0} can only be filtered with equal, not equal, greater than, lower than, greater than or equal, lower than or equal operators», filterNode.Key));

}

return string.Format(CultureInfo.InvariantCulture, » {0} {1} {2} ‘{3}’ «, filterNode.NodeRelationship.ToString(), tfsFieldName, sign, filterNode.Value);

}

return string.Empty;

}

private IEnumerable<WorkItemFact> RequestWorkItemFactsByProject(string projectName, FilterNode rootFilterNode, ODataQueryOperation operation)

{

FilterNode newFilterNodeStartWithProject = new FilterNode() {

Key = «Project», Sign = FilterExpressionType.Equal, Value = projectName

};

if (rootFilterNode != null)

{

newFilterNodeStartWithProject.AddNode(rootFilterNode);

}

var wiql = BuildWiql(newFilterNodeStartWithProject, operation);

return this.ExecuteWiqlRequest(wiql, operation);

}

private IEnumerable<WorkItemFact> ExecuteWiqlRequest(string wiql, ODataQueryOperation operation)

{

IEnumerable<WorkItemFact> retWorkItems = null;

if (!operation.IsCountRequest)

{

if (operation.TopCount == 0)

{

//workaround for bug (I think) in WCF Data Services Toolkit.

//It appears that ODataQueryOperation.TopCount will be 0 when $select param is used

//and $top param is not explicitly sent by client (normally TopCount would have

//whatever was set for entity page size in TFSService.InitializeService()

operation.TopCount = Constants.DefaultEntityPageSize;

}

IEnumerable<TeamFoundation.WorkItemTracking.Client.WorkItem> wilist = this.QueryWorkItems(wiql).Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>() .Skip(operation.SkipCount).Take(operation.TopCount)

.Select(w => w).ToArray();

List<WorkItemFact> lst = new List<WorkItemFact>();

foreach (TeamFoundation.WorkItemTracking.Client.WorkItem w in wilist)

{

TeamFoundation.WorkItemTracking.Client.Revision prevRev=null;

// Get All work item revisions

foreach (TeamFoundation.WorkItemTracking.Client.Revision revision in w.Revisions)

{

if(prevRev!=null)

{

WorkItemFact wiFactPrev = new WorkItemFact();

wiFactPrev.Project = w.Project.Name;

wiFactPrev.Id = w.Id;

wiFactPrev.Revision = (int)prevRev.Fields[«System.Rev»].Value;

wiFactPrev.RevisionDate = System.Convert.ToDateTime(revision.Fields[«Changed Date»].Value).Date; // Take the date for the next revision

wiFactPrev.RevisionCount=null;

wiFactPrev.RecordCount=-1;

wiFactPrev.SurrogateKey = wiFactPrev.Id + «:» + wiFactPrev.Revision;

// add numeric values to the fact

foreach (TeamFoundation.WorkItemTracking.Client.Field field in prevRev.Fields)

{

switch (prevRev.Fields[field.Name].FieldDefinition.FieldType)

{

case TeamFoundation.WorkItemTracking.Client.FieldType.Integer:

wiFactPrev.SetField(field.Name, System.Convert.ToInt64(field.Value) * -1);

break;

case TeamFoundation.WorkItemTracking.Client.FieldType.Double:

wiFactPrev.SetField(field.Name, System.Convert.ToDouble(field.Value) * -1);

break;

}

}

lst.Add(wiFactPrev);

}

prevRev=revision;

WorkItemFact wiFact = new WorkItemFact();

wiFact.Project = w.Project.Name;

wiFact.Id = w.Id;

wiFact.Revision = (int)revision.Fields[«System.Rev»].Value;

wiFact.RevisionCount = 1;

wiFact.RecordCount = 1;

wiFact.RevisionDate = System.Convert.ToDateTime( revision.Fields[«Changed Date»].Value).Date;

wiFact.SurrogateKey = wiFact.Id + «:» + wiFact.Revision;

// add numeric values to the fact

foreach (TeamFoundation.WorkItemTracking.Client.Field field in revision.Fields)

{

switch (revision.Fields[field.Name].FieldDefinition.FieldType)

{

case TeamFoundation.WorkItemTracking.Client.FieldType.Integer:

wiFact.SetField(field.Name, System.Convert.ToInt64(field.Value) );

break;

case TeamFoundation.WorkItemTracking.Client.FieldType.Double:

wiFact.SetField(field.Name, System.Convert.ToDouble(field.Value) );

break;

}

}

lst.Add(wiFact);

}

}

retWorkItems = lst;

}

else

{

var workItemServer = this.TfsConnection .GetService<TeamFoundation.WorkItemTracking .Client.WorkItemStore>();

try

{

Microsoft.TeamFoundation.WorkItemTracking.Client.Query q =

new TeamFoundation.WorkItemTracking.Client.Query(workItemServer, wiql, null, false);

int cnt = q.RunCountQuery();

List<WorkItemFact> wiBlanks = new List<WorkItemFact>(cnt);

WorkItemFact blank = new WorkItemFact();

for (int i = 0; i < cnt; i++)

{

wiBlanks.Add(blank);

}

retWorkItems = wiBlanks;

}

catch (Microsoft.TeamFoundation.WorkItemTracking.Client.ValidationException ex)

{

throw new System.Data.Services.DataServiceException(500, «Internal Server Error», ex.Message, «en-US», ex);

}

}

return retWorkItems;

}

}

}

WorkItemFactRepository

Code 5 – WorkItemFactRepositories

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Repositories

{

using System;

using System.Collections;

using System.Collections.Generic;

using System.Data.Services;

using System.Globalization;

using System.Linq;

using Microsoft.Data.Services.Toolkit.QueryModel;

using Microsoft.Samples.DPE.ODataTFS.Model.Entities;

using Microsoft.Samples.DPE.ODataTFS.Model.ExpressionVisitors;

using Microsoft.Samples.DPE.ODataTFS.Model.Serialization;

public class WorkItemFactRepository : IRepository<WorkItemFact>

{

private readonly ITfsWorkItemFactProxy proxy;

public WorkItemFactRepository(ITfsWorkItemFactProxy proxy)

{

this.proxy = proxy;

}

public IEnumerable<WorkItemFact> GetAll()

{

List<WorkItemFact> lst = new List<WorkItemFact>();

return lst;

}

public WorkItemFact GetOne(string id)

{

return null;

}

[RepositoryBehavior(HandlesFilter = true, HandlesSkip = true, HandlesTop = true, HandlesOrderBy = true)]

public IEnumerable<WorkItemFact> GetWorkItemFactsByProject(ODataSelectManyQueryOperation operation)

{

if (operation == null)

{

throw new ArgumentNullException(«operation»);

}

var parameters = new WorkItemFilterExpressionVisitor(operation.FilterExpression).Eval();

return this.proxy.GetWorkItemFactByProject(operation.Key, parameters, operation);

}

}

}

Настройка альтернативных учетных данных для VS Online

Процедура настройки альтернативных учетных данных описана на странице http://tfodata.visualstudio.com и является необходимым условием для данного руководства.

Шаг Инструкции
1. Опционально – Включить и настроить базовые учетные данные для tfs.visualstudio.com
[ ] — Сделано
  • Перейдите к учетной записи, которую вы хотите использовать на https://tfs.visualstudio.com. Например, вы можете иметь https://account.visualstudio.com.
  • В правом верхнем углу нажмите на имени вашей учетной записи и затем выберите My Profile.
  • Выберите вкладку Credentials.
  • Нажмите ссылку Enable alternate credentials and set password,
  • Введите пароль. Предполагается, что вы выберите уникальный пароль здесь (не связанный с другими учетными записями),
  • Нажмите кнопку Save Changes.
2. Настроить аутентификацию через сервисы OData
[ ] — Сделано
  • Для проверки подлинности через службы OData, вам нужно отправить ваши учетные данные для базовой аутентификации в формате домен\имя_пользователя и пароль:
    • домен\имя_пользователя
    • пароль
  • учетная запись из account.visualstudio.com, имя пользователя на вкладке Credentials в разделе My Profile, и пароль – это пароль, созданный на шаге 1.

<<Содержание

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

 
%d такие блоггеры, как: