I am doing some update in crm using ssis. I tried to close some cases in crm based on certain conditions. This is my sample code in public override void Input0_ProcessInputRow(Input0Buffer Row) method.
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
// Create a Entity object of type 'case'
Entity caseEnt = new Entity("incident");
Entity incidentResolution= new Entity("incidentresolution");
incidentResolution.Attributes.Add("incidentid", new
EntityReference("incident", Row.DEVCaseGUID));
caseEnt["incidentid"] = Row.DEVCaseGUID;
//organizationservice.Update(caseEnt);
//Changes added here by //
EntityCollection collection= GetAssociatedActivities(new EntityReference("incident", Row.DEVCaseGUID))
foreach (Entity activity in collection.Entities)
{
CancelActivity(activity, organizationservice);
}
// Changes added here //
// Close the incident with the resolution.
var closeIncidentRequest = new CloseIncidentRequest
{
IncidentResolution = incidentResolution,
Status = new OptionSetValue(5)
};
organizationservice.Execute(closeIncidentRequest);
}
private EntityCollection GetAssociatedActivities(EntityReference regarding)
{
QueryExpression query = new QueryExpression { EntityName = "activitypointer", ColumnSet = new ColumnSet(new string[] { "activitytypecode" }) };
query.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, regarding.Id);
query.Criteria.AddCondition("statecode", ConditionOperator.NotEqual, 1); //ignore completed
EntityCollection collection = organizationservice.RetrieveMultiple(query);
return collection
}
// Cancel an Activity
private static void CancelActivity(Entity entity, IOrganizationService service)
{
EntityReference moniker = new EntityReference();
if (entity.LogicalName == "activitypointer")
{
if (entity.Attributes.Contains("activityid") & entity.Attributes.Contains("activitytypecode"))
{
moniker.LogicalName = entity.Attributes["activitytypecode"].ToString();
moniker.Id = (Guid)entity.Attributes["activityid"];
SetStateRequest request = new SetStateRequest();
request.EntityMoniker = moniker;
request.State = new OptionSetValue(2);
request.Status = new OptionSetValue(-1);
SetStateResponse response = (SetStateResponse)service.Execute(request);
}
}
}
Row.DEVCaseGUID is the GUID of the Case.
statuscode is 5 for closed.
statecode is 2 for Resolved.
I tried follow this example but no success. Or is there any simple way to achieve this ?
Closing a Case in CRM is different than setting state/statuscode.
An Intermediate entity named IncidentResoultion is created when a case is closed.
You can try the following code to close the case programmatically.
Entity incidentResolution= new Entity("incidentresolution");
incidentResolution.Attributes.Add("incidentid", new
EntityReference("incident", Row.DEVCaseGUID));
// Close the incident with the resolution.
var closeIncidentRequest = new CloseIncidentRequest
{
IncidentResolution = incidentResolution,
Status = new OptionSetValue(5)
};
organizationservice.Execute(closeIncidentRequest);
Please note that a case can only be marked as Closed/Completed only if all the activititesregarding that Case are completed.
Update 09-Nov-2017: Adding code for closing related activities regarding CASE
private List<Entity> GetAssociatedActivities(EntityReference regarding)
{
QueryExpression query = new QueryExpression { EntityName = "activitypointer", ColumnSet = new ColumnSet(new string[] { "activitytypecode" }) };
query.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, regarding.Id);
query.Criteria.AddCondition("statecode", ConditionOperator.NotEqual, 1); //ignore completed
EntityCollection activities = organizationservice.RetrieveMultiple(query);//change collection to activities
foreach (Entity activity in activities.Entities)
{
CancelActivity(activity, organizationservice);
}
}
// Cancel an Activity
private static void CancelActivity(Entity entity, IOrganizationService service)
{
EntityReference moniker = new EntityReference();
if (entity.LogicalName == "activitypointer")
{
if (entity.Attributes.Contains("activityid") & entity.Attributes.Contains("activitytypecode"))
{
moniker.LogicalName = entity.Attributes["activitytypecode"].ToString();
moniker.Id = (Guid)entity.Attributes["activityid"];
SetStateRequest request = new SetStateRequest();
request.EntityMoniker = moniker;
request.State = new OptionSetValue(2);
request.Status = new OptionSetValue(-1);
SetStateResponse response = (SetStateResponse)service.Execute(request);
}
}
}
https://www.magnetismsolutions.com/blog/roshanmehta/2012/2/16/Dynamics_CRM_2011_Closing_all_Related_Activities_for_a_Record.aspx
https://msdynamicscrmblog.wordpress.com/2013/06/18/there-are-still-open-activities-associated-with-this-case-when-resolving-a-case-in-dynamics-crm-2011/
Related
Requirement
I have the requirement where I want to update few fields on account
and create contact for it reading data from an API.
The number of records to be updated is around 100,000 so I want to
use either ExecuteTransactionRequest or ExecuteMultipleRequest so that I can execute all in batches.
Since I want the contact record to be created for the account updated I used the ExecuteTransactionRequest.
Problem -
The problem is batch size.
I have added the condition if request batch size count equals 500 then execute all the requests. However my batch can include
Update request for account and
Create request for contact
So it may happen that the batch may not be exact 500 and it would skip the execute request. How can I do this and make sure that contact record is created for each Updated Account correctly.
Any help would be appreciated. Thanks in Advance
Below is my code --
var requests = new ExecuteTransactionRequest
{
Requests = new OrganizationRequestCollection(),
ReturnResponses = returnResponses
};
foreach (var customer in customerList)
{
string custNo = customer.GetAttributeValue<string>("customernumber");
// Gets customer details from another api
var custInfo = await CustomerService.Get(custNo);
// Update the values on customer table
Entity cust = new Entity("account");
cust.Id = customer.Id;
cust["companytypecode"] = custInfo.EntityTypeCode;
cust["companytypedescription"] = custInfo .EntityTypeDescription;
var roles = custInfo.Roles.Where(c => c.RoleStatus == "ACTIVE").ToArray();
//create contact for each account
foreach(var role in roles)
{
Entity contact = new Entity("contact");
contact["FirstName"] = role.RolePerson?.FirstName;
contact["MiddleName"] = role.RolePerson?.MiddleNames;
contact["LastName"] = role.RolePerson?.LastName;
contact["AccountId"] = new EntityReference("account", customer.Id);
CreateRequest createRequest = new CreateRequest { Target = contact };
requests.Requests.Add(createRequest);
}
UpdateRequest updateRequest = new UpdateRequest { Target = cust };
requests.Requests.Add(updateRequest);
if (requests.Requests.Count == 500) // Problem is the batch size will not execute per account since it also has create request of contact. How can i make sure that each request is executed correctly
{
service.Execute(requests);
requests.Requests.Clear();
}
}
// For the last remaining accounts
if (requests.Requests.Count > 0)
{
service.Execute(requests);
}
Thank you for helping out. I resolved this with below solution. Happy to be corrected.
EntityCollection requestsCollection = new EntityCollection();
foreach (var customer in customerList)
{
string custNo= customer.GetAttributeValue<string>("customernumber");
var custInfo = await businessService.Get(custNo);
Entity cust = new Entity("account");
cust.Id = customer.Id;
cust["companytypecode"] = custInfo.EntityTypeCode;
cust["companytypedescription"] = custInfo .EntityTypeDescription;
requestsCollection.Entities.Add(cust);
var roles = custInfo.Roles.Where(c => c.RoleStatus == "ACTIVE").ToArray();
foreach(var role in roles)
{
Entity contact = new Entity("contact");
contact["FirstName"] = role.RolePerson?.FirstName;
contact["MiddleName"] = role.RolePerson?.MiddleNames;
contact["LastName"] = role.RolePerson?.LastName;
contact["AccountId"] = new EntityReference("account", customer.Id);
requests.Entities.Add(contact);
}
if (requestsCollection.Entities.Count > 500)
{
ExecuteBulkUpdate(requestsCollection);
requestsCollection = new EntityCollection();
}
}
private void ExecuteBulkUpdate(EntityCollection requestsCollection)
{
var requests = new ExecuteTransactionRequest
{
Requests = new OrganizationRequestCollection(),
ReturnResponses = returnResponses
};
foreach (var request in requestsCollection.Entities)
{
if (request.Id != Guid.Empty)
{
UpdateRequest updateRequest = new UpdateRequest { Target = request };
requests.Requests.Add(updateRequest);
}
else
{
CreateRequest createRequest = new CreateRequest { Target = request };
requests.Requests.Add(createRequest);
}
}
try
{
var responseForCreateRecords = (ExecuteTransactionResponse)service.Execute(requests);
int i = 0;
// Display the results returned in the responses.
foreach (var responseItem in responseForCreateRecords.Responses)
{
if (responseItem != null)
log.LogInformation(responseItem.Results["id"].ToString());
i++;
}
requests.Requests.Clear();
}
catch (FaultException<OrganizationServiceFault> ex)
{
log.LogInformation("Request failed for the {0} and the reason being: {1}",
((ExecuteTransactionFault)(ex.Detail)).FaultedRequestIndex + 1, ex.Detail.Message);
throw;
}
}
I am currently working on a project where I need to be able to rent a truck to a customer but also need to add the customer details if not existing. My problem is even though through the WPF form I input the exact same details of a customer, there would be a new set of data added thus creating a new Customer ID for one person. How would I be able to get the database disregard the existing customer details?
My data service code:
public class DataService
{
public static void rentTruck(TruckRental toRent, bool isNewCustomer)
{
using (var ctx = new DAD_TruckRental_RGMContext())
{
if (!isNewCustomer)
{
ctx.Entry(toRent.Customer).State = EntityState.Unchanged;//doesnt leave existing customer unchanged
}
ctx.Entry(toRent.Truck).State = EntityState.Modified;
ctx.TruckRental.Add(toRent);
ctx.SaveChanges();
}
}
My cs code:
private void Button_Click(object sender, RoutedEventArgs e)
{
TruckCustomer cust = new TruckCustomer();
cust.Age = int.Parse(ageTextBox.Text);
cust.LicenseNumber = licenseNumberTextBox.Text;
cust.LicenseExpiryDate = licenseExpiryDateDatePicker.SelectedDate.Value.Date;
TruckPerson per = new TruckPerson();
per.Address = addressTextBox.Text;
per.Telephone = telephoneTextBox.Text;
per.Name = nameTextBox.Text;
cust.Customer = per;
int truckId = int.Parse(truckIdTextBox.Text);
IndividualTruck truck = DataService.searchTruckByID(truckId);
decimal priceTotal = decimal.Parse(totalPriceTextBox.Text);
TruckRental toRent = new TruckRental();
toRent.TotalPrice = priceTotal;
toRent.RentDate = rentDateDatePicker.SelectedDate.Value.Date;
toRent.ReturnDueDate = returnDueDateDatePicker.SelectedDate.Value.Date;
toRent.Customer = cust;
toRent.Truck = truck;
truck.Status = "Rented";
DataService.rentTruck(toRent, true);
MessageBox.Show("Truck rented succesfully");
}
Here is my suggestion
1- First check if customer details already exist in db using LicenseNumber
2- The first step will be either null or have details, so if null then add received customer details otherwise update
here is the code
public class DataService
{
public static void rentTruck(TruckRental toRent, bool isNewCustomer, TruckCustomer tcustomer)
{
using (var ctx = new DAD_TruckRental_RGMContext())
{
var ob = ctx.TruckCustomer.Where(c => c.LicenseNumber == customer.LicenseNumber);
if ( ob != null) //not exist
{
//create new here
ctx.TruckCustomer.Add(tcustomer);
}
//exist then just update State
ctx.ob.State = EntityState.Modified;
ctx.AddOrUpdate(ob);
ctx.TruckRental.Add(toRent);
ctx.SaveChanges();
}
}
I hope this can help you
I am trying to update loan balance based on the payment made by a member.
Payment goes well and it is inserted as expected but during the update of the table LOANBAl, nothing is modified, bellow is my code:
public void UpdateLoanBal(MPAreceipting mpa, string id)
{
using (BOSAEntities db = new BOSAEntities())
{
General gn = new General();
gn.GetUser();
gn.GetServerDate();
LoanRepayment lr = new LoanRepayment();
lr.GetMemberDeduction(loanno);
var lOANBAL = db.LOANBALs.Find(id);
var lb = new LOANBAL();
lb.AuditID = gn.sysUser;
lb.AuditTime = gn.serverDate;
lb.Balance = Convert.ToDecimal(lr.loanBalance);
lb.IntrOwed = Convert.ToDecimal(lr.intOwed);
lb.LastDate = mpa.dateDeposited;
db.Entry(lOANBAL).State = EntityState.Modified;
db.SaveChanges();
}
}
I used the lOANBAL which is an entity and update its properties.
public void UpdateLoanBal(MPAreceipting mpa, string id)
{
var db = new BOSAEntities();
using (var dbContextTransaction = db.Database.BeginTransaction())
{
try
{
var lOANBAL = db.LOANBALs.Find(loanno);
General gn = new General();
gn.GetUser();
gn.GetServerDate();
LoanRepayment lr = new LoanRepayment();
lr.GetMemberDeduction(loanno);
lOANBAL.LoanNo = loanno;
lOANBAL.AuditID = gn.sysUser;
lOANBAL.AuditTime = gn.serverDate;
lOANBAL.Balance = Convert.ToDecimal(lr.loanBalance);
lOANBAL.IntrOwed = Convert.ToDecimal(lr.intOwed);
lOANBAL.LastDate = mpa.dateDeposited;
lOANBAL.TransactionNo=lr.
db.Entry(lOANBAL).State = EntityState.Modified;
db.SaveChanges();
dbContextTransaction.Commit();
}
catch (DbEntityValidationException Exc)
{
dbContextTransaction.Rollback();
string errormessage = string.Join(";",
Exc.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage));
throw new DbEntityValidationException(errormessage);
}
}
}
In C#, I'm writing a program that transfers data from an old Microsoft Dynamics CRM system to a new one.
For most of the entities, I can use the UpsertRequest. Though, for contacts and accounts, there are already records in the new environment. As I don't want to have doubles, I would like to have the UpsertRequest check on the "Name" field in case of accounts and on "Fullname" in case of contacts.
Would that be possible? I (searched a lot and) cannot find examples on this. If not, what would be the best way to proceed?
Thanks for any feedback.
for such case I would make a plugin on message create of contact and account such like this one :
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService organizationService = serviceFactory.CreateOrganizationService(context.UserId);
Entity TargetEntity = new Entity();
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
TargetEntity = (Entity)context.InputParameters["Target"];
QueryExpression queryDuplicateDetect = new QueryExpression(TargetEntity.LogicalName);
if (TargetEntity.LogicalName == "account" && TargetEntity.Attributes.Contains("name"))
{
queryDuplicateDetect.ColumnSet = new ColumnSet(new string[] { "name" });
queryDuplicateDetect.Criteria.AddCondition(new ConditionExpression("name", ConditionOperator.Equal, TargetEntity["name"].ToString()));
}
else if (TargetEntity.LogicalName == "contact" && TargetEntity.Attributes.Contains("fullname"))
{
queryDuplicateDetect.ColumnSet = new ColumnSet(new string[] { "fullname" });
queryDuplicateDetect.Criteria.AddCondition(new ConditionExpression("fullname", ConditionOperator.Equal, TargetEntity["fullname"].ToString()));
}
try
{
EntityCollection resultsColl = organizationService.RetrieveMultiple(queryDuplicateDetect);
if (resultsColl.Entities.Count > 0)
{
foreach (Entity e in resultsColl.Entities)
{
tracingService.Trace("Record Found with ID {0}", e.Id);
}
//log results in some entity for more info
throw new InvalidPluginExecutionException("Duplicate detected.");
}
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message);
}
}
}
and on the create side i'll use simple try catch to skip existing records
Entity x = new Entity("account");
x["name"] = "mohamed0";
Entity y = new Entity("contact");
y["fullname"] = "mohamed";
Entity z = new Entity("contact");
z["fullname"] = "mohamed";
try
{
var r = _orgService.Create(x);
r = _orgService.Create(y);
r = _orgService.Create(z);
}
catch (Exception e)
{
throw;
}
hope this will help.
In following code snippet I am retrieving notes related to an order. It works fine only if notetext does contain data. Now, while debugging I found that, in other case it throws the exception that Object reference not set to an instance of an object.
I think following snippet looks good, but not sure what is missing, any idea to sort out the problem?
private void fetchDocument(IOrganizationService service, Guid vOrderId)
{
EntityCollection results = null;
string tempNote = string.Empty;
string tempFileName = string.Empty;
ColumnSet cols = new ColumnSet("subject", "filename", "documentbody", "mimetype","notetext");
QueryExpression query = new QueryExpression {
EntityName = "annotation" ,
ColumnSet = cols,
Criteria = new FilterExpression
{
Conditions = {
new ConditionExpression("objectid",ConditionOperator.Equal,vOrderId)
}
}
};
results = service.RetrieveMultiple(query);
Entity defaultRecord = results.Entities.ElementAtOrDefault(0);
if(defaultRecord.Contains("notetext"))
{
tempNote = defaultRecord.GetAttributeValue<string>("notetext");
}
if (defaultRecord.Contains("filename"))
{
tempFileName = defaultRecord.GetAttributeValue<string>("filename");
}
}
You haven't guarded defaultrecord against null.
results = service.RetrieveMultiple(query);
if (results.Entities == null || !results.Entities.Any()) return;
Entity defaultRecord = results.Entities.ElementAt(0);
Extending the answer to backup result.Entities == null check.
Retrieve multiple EntityCollection is not foolproof.
EntityCollection property:
Decomplied SDK retrieve multiple core:
protected internal virtual EntityCollection RetrieveMultipleCore(QueryBase query)
{
bool? retry = new bool?();
do
{
bool forceClose = false;
try
{
using (new OrganizationServiceContextInitializer(this))
return this.ServiceChannel.Channel.RetrieveMultiple(query);
}
catch (MessageSecurityException ex)
{
..
}
finally
{
this.CloseChannel(forceClose);
}
}
while (retry.HasValue && retry.Value);
return (EntityCollection) null;
}
Decomplied SDK Cached Organization Serivce Context Retrieve multiple:
public override EntityCollection RetrieveMultiple(QueryBase query)
{
RetrieveMultipleRequest retrieveMultipleRequest = new RetrieveMultipleRequest();
retrieveMultipleRequest.Query = query;
RetrieveMultipleResponse multipleResponse = this.Execute<RetrieveMultipleResponse>((OrganizationRequest) retrieveMultipleRequest);
if (multipleResponse == null)
return (EntityCollection) null;
else
return multipleResponse.EntityCollection;
}
public EntityCollection EntityCollection
{
get
{
if (this.Results.Contains("EntityCollection"))
return (EntityCollection) this.Results["EntityCollection"];
else
return (EntityCollection) null;
}
}
Your issue is actually at this line:
Entity defaultRecord = results.Entities.ElementAtOrDefault(0);
There are no results found, meaning
there is no Annotation that exists with an objectid of "vOrderId", or the user that is performing the query, doesn't have rights to read that record.
Regardless, you should just check for defaultRecord being null or not, and exiting if it is.
This check of null is a common occurrence, which is why I've written this ExtensionMethod:
public Entity GetFirstOrDefault(this IOrganizationService service, QueryBase qb) {
return service.RetrieveMultiple(qb)?.Entities.FirstOrDefault();
}
This would simplify your code to this:
private void fetchDocument(IOrganizationService service, Guid vOrderId)
{
EntityCollection results = null;
string tempNote = string.Empty;
string tempFileName = string.Empty;
ColumnSet cols = new ColumnSet("subject", "filename", "documentbody", "mimetype","notetext");
QueryExpression query = new QueryExpression {
EntityName = "annotation" ,
ColumnSet = cols,
Criteria = new FilterExpression
{
Conditions = {
new ConditionExpression("objectid",ConditionOperator.Equal,vOrderId)
}
}
};
var defaultRecord = service.GetFirstOrDefault(query);
if(defaultRecord != null)
{
if(defaultRecord.Contains("notetext"))
{
tempNote = defaultRecord.GetAttributeValue<string>("notetext");
}
if (defaultRecord.Contains("filename"))
{
tempFileName = defaultRecord.GetAttributeValue<string>("filename");
}
}
}