Problems with adding attachments to a workItem (changing dates) - c#

I have a task to relocate work items from one TFS project to another within one TFS collection. All history and dates have to be identical to the original item. I read a lot information and have done a lot of work. I have almost completed the task but found one crucial issue. After saving of a new item which have been copied from original one I lost my attachments in revisions. Thus I can relocate item only once, after next attempt attachments will be lost.
There is my approach to copy items:
First of all I read that article about changing creation date and changed date: TFS API Change WorkItem CreatedDate And ChangedDate To Historic Dates.
Then I had next code:
var type = project.WorkItemTypes[itemToCopy.Type.Name];
var newWorkItem = new WorkItem(type);
List<Revision> revisions = item.Revisions.Cast<Revision>().OrderBy(r => r.Index).ToList();
var firstRevision = revisions.First();
revisions.Remove(firstRevision);
SetFields(newWorkItem, firstRevision.Fields, includeAreas);
result = Save(newWorkItem);
if (result != string.Empty)
throw new Exception(result);
newWorkItem = tfsManager.GetWorkItemStore().GetWorkItem(newWorkItem.Id);
var changed = firstRevision.Fields.Cast<Field>().First(f => f.ReferenceName == "System.ChangedDate").Value;
var created = firstRevision.Fields.Cast<Field>().First(f => f.ReferenceName == "System.CreatedDate").Value;
var changedDate = (DateTime)changed;
var createdDate = (DateTime)created;
newWorkItem.Fields["System.CreatedDate"].Value = createdDate;
newWorkItem.Fields["System.ChangedDate"].Value = changedDate.AddSeconds(1);
result = Save(newWorkItem);
if (result != string.Empty)
throw new Exception(result);
var attachments = firstRevision.Attachments.Cast<Attachment>().ToList();
var attachMap = new Dictionary<int, Attachment>();
if (attachments.Count > 0)
AddAttachments(newWorkItem, firstRevision.Attachments.Cast<Attachment>().ToList(), attachMap);
else
{
result = Save(newWorkItem);
if (result != string.Empty)
throw new Exception(result);
}
ApplyRevisions(newWorkItem, revisions, attachMap, includeAreas);
return newWorkItem;
The method "SetFields" copies all editable fields from the original work item to a new work item.
The method "Save" simply saves the work item and collect all info about mistakes during save process.
The method "ApplyRevisions" simple enumerate all revisions and copy fields and attachments. It looks like:
private void ApplyRevisions(WorkItem toItem,
List<Revision> revisions, Dictionary<int,
Attachment> attachMap,
bool includeAreas)
{
foreach (var revision in revisions.OrderBy(r => r.Index))
{
SetFields(toItem, revision.Fields, includeAreas);
AddAttachments(toItem, revision.Attachments.Cast<Attachment>().ToList(), attachMap);
AddChangesetLinks(toItem, revision.Links.Cast<Link>().ToList());
var result = Save(toItem);
if (result != string.Empty)
{
SetFields(toItem, revision.Fields, includeAreas);
result = Save(toItem);
if (result != string.Empty)
{
throw new Exception(result);
}
}
}
}
And the main part is how I copy attachments:
private void AddAttachments(WorkItem item, IList<Attachment> attaches, Dictionary<int, Attachment> attachMap)
{
var guid = Guid.NewGuid();
var currentAttaches = item.Attachments.Cast<Attachment>().ToList();
var files = new List<string>();
try
{
item.Open();
foreach (var attach in attaches)
{
if (attachMap.ContainsKey(attach.Id))
{
var id = attachMap[attach.Id].Id;
if (currentAttaches.Any(a => a.Id == id))
continue;
}
var bytes = tfsManager.TfsWebClient.DownloadData(attach.Uri);
var tempFile = CreateFileName(guid, attach.Name);
files.Add(tempFile);
if (bytes != null && bytes.Length > 0)
File.WriteAllBytes(tempFile, bytes);
else
{
File.Create(tempFile).Dispose();
}
var attachInfo = new AttachmentInfo(tempFile);
attachInfo.FieldId = 50;
attachInfo.CreationDate = attach.CreationTimeUtc;
//attachInfo.LastWriteDate = attach.LastWriteTimeUtc;
attachInfo.Comment = attach.Comment;
//attachInfo.AddedDate = attach.AttachedTimeUtc;
var newAttach = Attachment.MakeAttachment(item, attachInfo);
item.Attachments.Add(newAttach);
if (!attachMap.ContainsKey(attach.Id))
attachMap[attach.Id] = newAttach;
}
foreach (var attach in currentAttaches)
{
var id = attachMap.First(pair => pair.Value.Id == attach.Id).Key;
if (attaches.All(a => a.Id != id))
{
item.Attachments.Remove(attach);
}
}
var result = Save(item);
if (result != string.Empty)
throw new Exception(result);
}
finally
{
if (files.Count > 0)
{
foreach (var file in files)
{
File.Delete(file);
}
RemoveTempFolder(guid);
}
}
}
After copying item and retrieving it from TFS property "Attachments" in property "Revisions" is empty. I guess, that the problem is in different dates... But do not know how to solve it...
Sorry for enormous amount of code. It is my first question here.
P.S.
I have digged deeper in the question and found out that the problem in AuthorizedAddedDate of AttachmentInfo. There is a comparison of this date and ChangeDate of the particular revision during of process of filling Attachments. See code below:
DateTime asof = (DateTime) changedDate;
foreach (LinkInfo linkInfo in this.m_linksData)
{
if (linkInfo.AuthorizedAddedDate <= asof && asof < linkInfo.AuthorizedRemovedDate)
yield return linkInfo;
}
ChangedDate we can change and I change it during of processing of revisions. Unfortunately, I do not know how to change AuthorizedAddedDate of AttachmentInfo... Even if I change it, the value is the same after next load of the work item... It seems that this value is prohibited for changing(

Yep, it seems that I have found the answer. At least this solution helped me. Sorry, that I have not posted it immediately. The process of adding attachment looks like that:
var attachInfo = new AttachmentInfo(tempFile);
attachInfo.AddedDate = attach.AddedDate;
attachInfo.CreationDate = attach.CreationDate;
attachInfo.Comment = string.Format(#"{0} (from attachId={1})", attach.Comment, attach.Id);
attachInfo.RemovedDate = attach.RemovedDate;
attachInfo.FieldId = 50;
itemTo.LinkData.AddLinkInfo(attachInfo, itemTo);
Where "tempFile" is a file which we want to attach (in my case it has been copied from another WorkItem) and "attach" is a container for information about AttachmentInfo. "itemTo" is a target WorkItem where we want to add attachment.
If you need more detail or you want me to clarify any moments, please, let me know.
P.S. It is important to remember about time zones when setting AddedDate, CreationDate and RemovedDate.

Related

Performance issue when performing operations on entity objects

Im facing performance issue in below code in multiple foreach loops. First im getting a list of ReturnDetails and then based on detail id get the HandlingInfo object. Then based on value of action, update the ReturnsDetail Object again.
It take more than a minute for loading 3000 records of ReturnsDetail. While debugging locally, it runs for infinite amount of time.
Please let me know in anyway i can refactor this code .
Thanks for your help.
lstReturnsDetail = dcReturnsService.GetReturnDetailsInfo(header_id);
List<HandlingInfo> lstHandlingInfo = null;
foreach (ReturnsDetail oReturnsDetail in lstReturnsDetail)
{
using (DCReturns_Entities entities = new DCReturns_Entities())
{
lstHandlingInfo = entities.HandlingInfoes.Where(f => f.detail_id == oReturnsDetail.id).ToList();
if(lstHandlingInfo != null)
{
foreach (HandlingInfo oHandlingInfo in lstHandlingInfo)
{
if (oHandlingInfo.action == "DST")
{
oReturnsDetail.destroy += Convert.ToInt32(oHandlingInfo.qty);
}
else if (oHandlingInfo.action == "SHP")
{
oReturnsDetail.to_shop += Convert.ToInt32(oHandlingInfo.qty);
}
else if (oHandlingInfo.action == "RBX")
{
oReturnsDetail.in_stock += Convert.ToInt32(oHandlingInfo.qty);
}
}
}
}
oReturnsDetail.received_qty = oReturnsDetail.destroy + oReturnsDetail.to_shop + oReturnsDetail.in_stock;
}
dgReturnsDetail.DataSource = lstReturnsDetail.OrderByDescending(g => g.id).ToList();
Session[DCReturnsConstants.Returns_Detail_Entity] = lstReturnsDetail;
dgReturnsDetail.DataBind();
this is su-do code! but you should get the jist.
//modify this to return all of them into mem, and then filter on this...
//if it can not be done here then do below..
var lstReturnsDetail = dcReturnsService.GetReturnDetailsInfo(header_id);
//then create a list here which fetches all,
List<[type]> somelist
List<int> listId = lstReturnsDetail.select(x=>x.id).tolist();
using (var db = new DCReturns_Entities())
{
somelist = db.HandlingInfoes.Where(f => listId.Contains( f.detail_id)).ToList();
}
foreach (ReturnsDetail oReturnsDetail in lstReturnsDetail)
{
//performance issue is here
//using (DCReturns_Entities entities = new DCReturns_Entities())
//{
// lstHandlingInfo = entities.HandlingInfoes.Where(f => f.detail_id == oReturnsDetail.id).ToList();
//}
//insead fetach all before, into mem and filter from that list.
var lstHandlingInfo = somelist.Where(f => f.detail_id == oReturnsDetail.id).ToList();
//code ommited for reaablity
}
//code ommited for reaablity

How to incorporate criteria in Where method?

I want to ask about the WHERE condition in my search command. I'm calling web service (API) during searching and I want to put WHERE statement in my code but there's an error.
private async Task CallApi(string searchText = null)
{
long lastUpdatedTime = 0;
long.TryParse(AppSettings.ComplaintLastUpdatedTick, out lastUpdatedTime);
var currentTick = DateTime.UtcNow.Ticks;
var time = new TimeSpan(currentTick - lastUpdatedTime);
if (time.TotalSeconds > 1) {
int staffFk = Convert.ToInt32(StaffId);
var result = await mDataProvider.GetComplaintList(lastUpdatedTime, mCts.Token, staffFk);
if (result.IsSuccess)
{
// Save last updated time
AppSettings.ComplaintLastUpdatedTick = result.Data.Updated.ToString();
// Store data into database
if ((result.Data.Items != null) &&
(result.Data.Items.Count > 0))
{
var datas = new List<Complaint>(result.Data.Items);
**if (!string.IsNullOrEmpty(searchText))
{
datas = datas.Where(i => i.Description.Contains(searchText)
&& (i.SupervisorId.Equals(StaffId))
|| (i.ProblemTypeName.Contains(searchText)));
}
else
{
datas = datas.Where(i => i.SupervisorId.Equals(StaffId));
}**
Datas = new ObservableCollection<Complaint>(datas);
}
}
else if (result.HasError)
{
await mPageDialogService.DisplayAlertAsync("Error", result.ErrInfo.Message, "OK");
}
}
}
Both assignments of datas in the if ... else causes System.Collections.Generic.IEnumerable<ECS.Features.Complaints.Complaint>' to 'System.Collections.Generic.List<ECS.Features.Complaints.Complaint>'. An explicit conversions exists (are you missing a cast?) compilation errors:
I don't know how to use the WHERE condition there. Please help me. Thank you in advance for your concern.
datas is a List<Complaint> but you try to reassign it to IEnumerable<Complaint> with the Where statement. Add a ToList() after the Where to maintain type,
Or you could just declare datas as IEnumerable<Complaint>
IEnumerable<Complaint> datas = new List<Complaint>(result.Data.Items);
Issue is that datas is defined as being a List<Complaint>, and the return type of datas.Where(...) is an IEnumerable/IQueryable.
You could do:
datas = datas.Where(i => i.SupervisorId.Equals(StaffId)).ToList();
Complete code:
private async Task CallApi(string searchText = null)
{
long lastUpdatedTime = 0;
long.TryParse(AppSettings.ComplaintLastUpdatedTick, out lastUpdatedTime);
var currentTick = DateTime.UtcNow.Ticks;
var time = new TimeSpan(currentTick - lastUpdatedTime);
if (time.TotalSeconds > 1) {
int staffFk = Convert.ToInt32(StaffId);
var result = await mDataProvider.GetComplaintList(lastUpdatedTime, mCts.Token, staffFk);
if (result.IsSuccess)
{
// Save last updated time
AppSettings.ComplaintLastUpdatedTick = result.Data.Updated.ToString();
// Store data into database
if ((result.Data.Items != null) &&
(result.Data.Items.Count > 0))
{
var datas = new List<Complaint>(result.Data.Items);
if (!string.IsNullOrEmpty(searchText))
{
datas = datas.Where(i => i.Description.Contains(searchText)
&& (i.SupervisorId.Equals(StaffId))
|| (i.ProblemTypeName.Contains(searchText))).ToList();
}
else
{
datas = datas.Where(i => i.SupervisorId.Equals(StaffId)).ToList();
}
Datas = new ObservableCollection<Complaint>(datas);
}
}
else if (result.HasError)
{
await mPageDialogService.DisplayAlertAsync("Error", result.ErrInfo.Message, "OK");
}
}
}
You will then also have an error on the next line, Datas = new ObservableCollection becasue Datas is not defined, and if you meant datas, again, it will not be the List<> that you initially defined.

Executing multiple requests xrm sdk [duplicate]

I am using ExecuteMultipleResponse method to insert 10 account records at a time using SSIS.
List<Entity> _Accounts = new List<Entity>();
// Check the batch size and process
public override void InputAccount_ProcessInput(InputAccountBuffer Buffer)
{
//List<int> personIDs = new List<int>();
int index = 0;
while (Buffer.NextRow())
{
_Accounts.Add(InputAccountFromBuffer(Buffer));
//personIDs.Add(int.Parse(Buffer.sPersonID));
index++;
if (index == 10)
{
ImportBatch();
index = 0;
}
}
ImportBatch();
}
private void ImportBatch()
{
if (_Accounts.Count > 0)
{
var multipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = true
},
Requests = new OrganizationRequestCollection()
};
foreach (var profContact in _Accounts)
{
CreateRequest reqCreate = new CreateRequest();
reqCreate.Target = profContact;
reqCreate.Parameters.Add("SuppressDuplicateDetection", false);
multipleRequest.Requests.Add(reqCreate);
}
ExecuteMultipleResponse multipleResponses = (ExecuteMultipleResponse)organizationservice.Execute(multipleRequest);
var responses = (ExecuteMultipleResponseItemCollection)multipleResponses.Results["Responses"];
foreach (var response in responses)
{
if (response.Fault != null)
{
// A fault has occurred, handle it here
}
else
{
// THIS IS WHERE I KNOW THE GUID VALUE EXIST.
}
}
//IEnumerator f = multipleResponses.Responses.GetEnumerator();
_Accounts.Clear();
}
}
Above code is working fine, however, I now need to read and store Guids from response to a List. This information is essential for the next step in the package. I know, if I am creating single record I can simply say,
Guid newRecord = _service.Create(account);
I even managed to get down to check if the response have 'Fault' or not and if it doesn't have fault then Guid value should exist in the response.
Running response.Response.Results.Values in QuickWatch shows me the guid but I just can't find a way to read it directly and store it as a Guid.
The guid of a created record should be stored in the OrganizationResponse which can be found inside the ExecuteMultipleResponseItem
Try the following to get the guid as a string:
string id = response.Response.Results["id"].ToString()
If it works as expected you should also be able to instantiate a guid, if needed:
Guid guid = new Guid(id);

Getting The Wait Operation Timeout Exception for a query from Skip Value 100

I am writing a small data migration tools from one big database to another small database. All of the others data migration method worked satisfactorily, but the following method has given an exception from the SKIP VALUE IS 100. I run this console script remotely as well as inside of the source server also. I tried in many different was to find the actual problem what it is. After then I found that only from the SKIP VALUE IS 100 it is not working for any TAKE 1,2,3,4,5 or ....
Dear expertise, I don't have any prior knowledge on that type of problem. Any kind of suggestions or comments is appreciatable to resolve this problem. Thanks for you time.
I know this code is not clean and the method is too long. I just tried solve this by adding some line of extra code. Because the problem solving is my main concern. I just copy past the last edited method.
In shot the problem I can illustrate with this following two line
var temp = queryable.Skip(90).Take(10).ToList(); //no exception
var temp = queryable.Skip(100).Take(10).ToList(); getting exception
private static void ImporterDataMigrateToRmgDb(SourceDBEntities sourceDb, RmgDbContext rmgDb)
{
int skip = 0;
int take = 10;
int count = sourceDb.FormAs.Where(x=> x.FormAStateId == 8).GroupBy(x=> x.ImporterName).Count();
Console.WriteLine("Total Possible Importer: " + count);
for (int i = 0; i < count/take; i++)
{
IOrderedQueryable<FormA> queryable = sourceDb.FormAs.Where(x => x.FormAStateId == 8).OrderBy(x => x.ImporterName);
List<IGrouping<string, FormA>> list;
try
{
list = queryable.Skip(skip).Take(take).GroupBy(x => x.ImporterName).ToList();
//this line is getting timeout exception from the skip value of 100.
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
sourceDb.Dispose();
rmgDb.Dispose();
sourceDb = new SourceDBEntities();
rmgDb = new RmgDbContext();
skip += take;
continue;
}
if (list.Count > 0)
{
foreach (var l in list)
{
List<FormA> formAs = l.ToList();
FormA formA = formAs.FirstOrDefault();
if (formA == null) continue;
Importer importer = formA.ConvertToRmgImporterFromFormA();
Console.WriteLine(formA.FormANo + " " + importer.Name);
var importers = rmgDb.Importers.Where(x => x.Name.ToLower() == importer.Name.ToLower()).ToList();
//bool any = rmgDb.Importers.Any(x => x.Name.ToLower() == formA.ImporterName.ToLower());
if (importers.Count() == 1)
{
foreach (var imp in importers)
{
Importer entity = rmgDb.Importers.Find(imp.Id);
entity.Country = importer.Country;
entity.TotalImportedAmountInUsd = importer.TotalImportedAmountInUsd;
rmgDb.Entry(entity).State = EntityState.Modified;
}
}
else
{
rmgDb.Importers.Add(importer);
}
rmgDb.SaveChanges();
Console.WriteLine(importer.Name);
}
}
skip += take;
}
Console.WriteLine("Importer Data Migration Completed");
}
I have fixed my problem by modifying following code
var queryable =
sourceDb.FormAs.Where(x => x.FormAStateId == 8)
.Select(x => new Adapters.ImporterBindingModel()
{
Id = Guid.NewGuid().ToString(),
Active = true,
Created = DateTime.Now,
CreatedBy = "System",
Modified = DateTime.Now,
ModifiedBy = "System",
Name = x.ImporterName,
Address = x.ImporterAddress,
City = x.City,
ZipCode = x.ZipCode,
CountryId = x.CountryId
})
.OrderBy(x => x.Name);

using C# to get an ec2-instance tag

I'm not a developer so maybe the answer is out there for a different solution but I can't really translate it from python or something else.
I'm trying to use the AWS .NET SDK to find an instance and then get the instance's tags. I've gotten as far as being able to determine if an instance is up and running or not. I also see how I can create and delete tags (not in code example below). But I don't see an easy way to actually check if a tag exists and get the value of the tag if it does exist.
Sorry if I'm missing the obvious but this is all new to me. Here's an example of the code I'm using to check if an instance is running.
instanceID = "i-myInstanceID";
do {
var myrequest = new DescribeInstanceStatusRequest();
DescribeInstanceStatusResponse myresponse = ec2.DescribeInstanceStatus(myrequest);
int isCount = myresponse.DescribeInstanceStatusResult.InstanceStatuses.Count;
for (int isc=0; isc < isCount; isc++) {
InstanceStatus instanceStatus = myresponse.DescribeInstanceStatusResult.InstanceStatuses[isc];
if (instanceStatus.InstanceId.Contains(instanceID)) {
Console.WriteLine("It looks like instance "+instanceID+" is running.");
idIdx = isc;
foundID = true;
break;
}
}
if ((foundID==false) && (secondCounter==1)) {
Console.Write("Looking for instance "+instanceID);
} else {
Console.Write(".");
}
Thread.Sleep(1000);
secondCounter++;
if (secondCounter > 5) {
break;
}
} while (foundID == false) ;
First send a DescribeInstancesRequest to get the list of Instances:
public DescribeInstancesResult GetInstances(Ec2Key ec2Key)
{
_logger.Debug("GetInstances Start.");
AmazonEC2 ec2 = CreateAmazonEc2Client(ec2Key);
var ec2Request = new DescribeInstancesRequest();
DescribeInstancesResponse describeInstancesResponse = ec2.DescribeInstances(ec2Request);
DescribeInstancesResult result = describeInstancesResponse.DescribeInstancesResult;
_logger.Debug("GetInstances End.");
return result;
}
Then loop through the instances until you find the one you want, and then use the Tag.GetTagValueByKey method:
// This just calls the above code
DescribeInstancesResult ec2Instances = _ec2ResourceAccess.GetInstances(ec2Key);
var returnInstances = new List<Ec2UtilityInstance>();
foreach (var reservation in ec2Instances.Reservation)
{
foreach (var runningInstance in reservation.RunningInstance)
{
var returnInstance = new Ec2UtilityInstance();
returnInstance.InstanceId = runningInstance.InstanceId;
returnInstance.InstanceName = runningInstance.Tag.GetTagValueByKey("Name");
returnInstance.Status = (Ec2UtilityInstanceStatus)Enum.Parse(typeof(Ec2UtilityInstanceStatus), runningInstance.InstanceState.Name, true);
returnInstance.DefaultIp = runningInstance.Tag.GetTagValueByKey("DefaultIp");
returnInstance.InstanceType = runningInstance.InstanceType;
returnInstance.ImageId = runningInstance.ImageId;
returnInstances.Add(returnInstance);
}
}
Here is the link for full source that this was taken from:
https://github.com/escherrer/EC2Utilities
Common\Manager
and
Common\ResourceAccess

Categories

Resources