I am new to CRM development.
I have a Custom Entity "customer". This Entity has a Field called "defaultcustomer", which can be TRUE or FALSE. I am working on a Plug-In where i need to set the "defaultcustomer" to FALSE for all the "Customers". I am doing it as below:
FACTS:
I have registered the plugin for the entity "customer" itself. So when the Entity "customer" is updated, the plugin fires.
private void MakeAllNonDefault()
{
try
{
QueryExpression query = new QueryExpression("customer");
query.ColumnSet = new ColumnSet("defaultcustomer");
EntityCollection retrieved = service.RetrieveMultiple(query);
foreach (Entity myCustomer in retrieved.Entities)
{
myCustomer["defaultcustomer"] = false;
service.Update(myCustomer);
}
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException("An error occurred in MakeAllNonDefault(): " + ex.ToString());
}
}
ERROR:
It throws error on this line:
myCustomer["defaultcustomer"] = false;
System.Collections.Generic.KeyNotFoundException:
The given key was not present in the dictionary.
The error means that particular field is not present in the collection of properties. In CRM, only properties that have been set or updated are included.
Try something like:
foreach (Entity myCustomer in retrieved.Entities)
{
if (myCustomer.Attributes.ContainsKey("defaultcustomer"))
{
myCustomer["defaultcustomer"] = false;
}
else
{
myCustomer.Attributes.Add("defaultcustomer", false);
}
service.Update(myCustomer);
}
Have you double checked that the field really is called defaultcustomer?
If it's a custom entity then it's likely the field begins with a prefix, for instance new_defaultcustomer. Make sure you are using the name of the field, not the display name.
The solution posted by #glosrob seems fine. Are you still getting "The given key was not present in the dictionary"?
Try to use ITracingService to get more information about the plugin execution flow.
While Update all Crm fields are False accept that what you Update the field. For that you can use Pre/Post Images in Plugin. you will found that crm field key and update what you need.
Related
Developer's version of Acumatica 2020R1 is installed locally. Data for sample tenant MyTenant from training for I-300 were loaded, and WSDL connection established.
DefaultSoapClient is created fine.
However, attempts to export any data by using Getlist cause errors:
using (Default.DefaultSoapClient soapClient =
new Default.DefaultSoapClient())
{
//Sign in to Acumatica ERP
soapClient.Login
(
"Admin",
"*",
"MyTenant",
"Yogifon",
null
);
try
{
//Retrieving the list of customers with contacts
//InitialDataRetrieval.RetrieveListOfCustomers(soapClient);
//Retrieving the list of stock items modified within the past day
// RetrievalOfDelta.ExportStockItems(soapClient);
RetrievalOfDelta.ExportItemClass(soapClient);
}
public static void ExportItemClass(DefaultSoapClient soapClient)
{
Console.WriteLine("Retrieving the list of item classes...");
ItemClass ItemClassToBeFound = new ItemClass
{
ReturnBehavior = ReturnBehavior.All,
};
Entity[] ItemClasses = soapClient.GetList(ItemClassToBeFound);
string lcItemType = "", lcValuationMethod = "";
int lnCustomFieldsCount;
using (StreamWriter file = new StreamWriter("ItemClass.csv"))
{
//Write the values for each item
foreach (ItemClass loItemClass in ItemClasses)
{
file.WriteLine(loItemClass.Note);
}
}
The Acumatica instance was modified by adding a custom field to Stock Items using DAC, and by adding several Attributes to Customer and Stock Items.
Interesting enough, this code used to work until something broke it.
What is wrong here?
Thank you.
Alexander
In the request you have the following line: ReturnBehavior = ReturnBehavior.All
That means that you try to retrieve all linked/detail entities of the object. Unfortunately, some object are not optimized enough to not affect query performance in GetList scenarios.
So, you have to options:
Replace ReturnBehavior=All by explicitly specifying linked/detail entities that you want to retrieve and not include Attributes into the list.
Retrieve StockItem with attributes one by one using Get operation instead of GetList.
P.S. The problem with attributes will most likely be fixed in the next version of API endpoint.
Edit:
Code sample for Get:
public static void ExportItemClass(DefaultSoapClient soapClient)
{
Console.WriteLine("Retrieving the list of item classes...");
ItemClass ItemClassToBeFound = new ItemClass
{
ReturnBehavior = ReturnBehavior.Default //retrieve only default fields (without attributes and other linked/detailed entities)
};
Entity[] ItemClasses = soapClient.GetList(ItemClassToBeFound);
foreach(var entity in ItemClasses)
{
ItemClass itemClass= entity as ItemClass;
ItemClass.ReturnBehavior=ReturnBehavior.All;
// retrieve each ItemClass with all the details/linked entities individually
ItemClass retrievedItemCLass = soapClient.Get(itemClass);
}
I got this piece of code in my account plugin, which was working before, but keep having this wired issue now.
What it tries to do is, when the account is updated, also update the primary contact.
ColumnSet contactCols = new ColumnSet(new string[] { "firstname"});
Entity contact = orgService.Retrieve("contact", contactId, contactCols);
tracer.Trace("firstname is " + contact["firstname"]);
contact["firstname"] = DateTime.Now.ToString();
orgService.Update(contact);
The Retrieve() works, but the Update() will throw the following exception:
Unhandled Exception:
Microsoft.Xrm.Sdk.InvalidPluginExecutionException:
System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]:
System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]:
System.NullReferenceException:
Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #BF42D86C (Fault Detail is equal to Microsoft.Xrm.Sdk.OrganizationServiceFault). (Fault Detail is equal to Microsoft.Xrm.Sdk.OrganizationServiceFault).
at CRMSyncPlugin.SyncEntityClass.Execute(IServiceProvider serviceProvider)
at Microsoft.Crm.Asynchronous.V5ProxyPlugin.Execute(IServiceProvider serviceProvider)
at Microsoft.Crm.Asynchronous.EventOperation.InvokePlugin(AsyncExecutionContext context, IPlugin pluginInstance)
It said NullReferenceException, I just couldn't figure out what is null.
==========================================================
After trying what #Nicknow suggested, still the same error.
Here is what I got from the trace:
Retrieving Contact: 048f9564-81b4-e311-a27c-0026553e0f7c
Retrieved Contact
firstname is John
The retrieve worked, just the update failed. Thx
I've added some additional tracing and null checking, this should work:
tracer.trace("Retrieving Contact: {0}", contactId.ToString());
ColumnSet contactCols = new ColumnSet(new string[] { "firstname"});
Entity contact = orgService.Retrieve("contact", contactId, contactCols);
tracer.trace("Retrieved Contact");
tracer.Trace("firstname is " + contact.Attributes.Contains("firstname") ? contact["firstname"] : "(No Value for firstname)");
if (contact.Attributes.Contains("firstname")) contact["firstname"] = DateTime.Now.ToString();
else contact.Attributes.Add("firstname", DateTime.Now.ToString());
orgService.Update(contact);
You might check to see if there is another plugin that runs on the Update of Contact - the error could be coming from that other plugin instead of this one.
I generally don't try to update the object that I got from the Retrieve call. Just new up a Contact, set the ID, and include only the fields you're looking to change.
ColumnSet contactCols = new ColumnSet(new string[] { "firstname"});
Entity contact = orgService.Retrieve("contact", contactId, contactCols);
tracer.Trace("firstname is " + contact["firstname"]);
var updateContact = new Contact();
updateContact["contactId"] = contactId;
updateContact["firstname"] = DateTime.Now.ToString();
orgService.Update(updateContact);
I've found this tends to avoid these sorts of issues.
I made a workflow for "Corrective Action". Here the issuer will issue the CA to a user. If he replies to that then the approver should review it. Here for approver i created a content type named "CA Review" for the Task where I added a field "Outcome" which is a drop down.
In the workflow, for the review task, in the method invoking i am attaching the content type to the task. This is working fine and i am able to see the "outcome" in the task edit form.
In the event of task changed, i need the value of "outcome" to set the other filed. Here i am using after properties of the task changed event. But this returns "null". Here is my code for getting the outcome value.
private void checkTheRespondeApproval(object sender, ConditionalEventArgs e)
{
var props = this.onReviewTaskChanged_AfterProperties1.ExtendedProperties[GetWorkflowFieldId("Outcome")];
e.Result = (props.ToString() == "Approved");
}
Here "props" is getting "Null". Please help me to get the outcome value. The code for "GetWorkflowFieldID" is as follows;
private Guid GetWorkflowFieldId(string pFieldName)
{
foreach (SPField item in workflowProperties.Item.Fields)
{
if (item.Title == pFieldName)
return item.Id;
}
return Guid.Empty;
}
Ok Guys,
I found the answer. The problem is for the field ID I am looking into wrong collection. I should get the field id of the content type. The following is the function I wrote to get the ID. It is working now.
private Guid GetContentTypeFileID(string pFieldName)
{
foreach (SPField item in workflowProperties.Web.ContentTypes["CAReview"].Fields)
{
if (item.Title == pFieldName)
return item.Id;
}
return Guid.Empty;
}
I hope this will help you too.
I am getting the following error when i try to update the item's name in the sharepoint
document library. The item is of type document set and its default values is loaded using javascript. In the Item added event we are updating with the new changed item's name value. But in the item.update() code statement i am getting following error.
The File CZY14389 has been modified by domain\username on current date.
Please provide your commens on resolving this.
You cannot change the name of a sharepoint document like that. You need to "move it".
Item.Update();
Item.File.MoveTo(Item.ParentList.RootFolder.Url + "/" + newFileName, false);
Item.File.Item["FileRef"] = newFileName;
Item.File.Update();
before you update item name and call item.update(), can you try to refresh your item like this:
item = item.ParentList.GetItemById(item.ID);
item.name = "xyz";
item.update();
this can sometimes happen in event handler. the problem is the updation process in the event handler is not as the same of the workflow. In event handler for updating you have to use the followitn steps. Dont use Item.Update() as in workflow.
Follow the steps:
• call and disable event firing before your code with : base.EventFiringEnabled = false;
•update your item by calling item.systemUpdate(false);
•enable event firing with : base.EventFiringEnabled = true;
Disable event firing and call your update code, dont forget to enable event firing.
HandleEventFiring handleEventFiring = new HandleEventFiring();
handleEventFiring.DisableHandleEventFiring();
try
{
item.Update();
//if item.Update doesnt work then use(For me item.update worked only on my local not on prod then i used the below)
//item.SystemUpdate(false)
}
finally
{
handleEventFiring.EnableHandleEventFiring();
}
public class HandleEventFiring : SPItemEventReceiver
{
public void DisableHandleEventFiring()
{
//obsolete
//this.DisableEventFiring();
this.EventFiringEnabled = false;
}
public void EnableHandleEventFiring()
{
//obsotete
//this.EnableEventFiring();
this.EventFiringEnabled = true;
}
}
I have a lookup field in the form, before select the related entity for the lookup, I check some conditions, if not pass, I overwrite the lookup onclick event to alert user; else, I need to overwrite the onclick event to show the lookup window to allow user to select entity.
So I need the object type code of this lookup, but before select there is no value then I can't get the object type code by use this code: var objecttypecode = Xrm.Page.getAttribute("field id").getValue()[0].type;
How to get object type code by entity name?
I find out the way:
function GetObjectTypeCode(entityName) {
try {
var lookupService = new RemoteCommand("LookupService", "RetrieveTypeCode");
lookupService.SetParameter("entityName", entityName);
var result = lookupService.Execute();
if (result.Success && typeof result.ReturnValue == "number") {
return result.ReturnValue;
}
else {
return null;
}
}
catch (ex) {
throw ex;
}
}
One of the supported way of doing this is using the Metadata service and then retrieving the Object Type Code or etc (entity Type Code).