I am creating a plugin on Disassociate message of Project (custom entity); having N:N relationship with User entity. It's not working. Here is the code.
IOrganizationService service = localContext.OrganizationService;
IPluginExecutionContext context = localContext.PluginExecutionContext;
// Get Primary Entity
EntityReference target = (EntityReference)context.InputParameters["Target"];
if (target.LogicalName == "new_project" || target.LogicalName == "systemuser")
{
Relationship relationShip = (Relationship)context.InputParameters["Relationship"];
// Get Related Entities
EntityReferenceCollection relatedentities = (EntityReferenceCollection)context.InputParameters["RelatedEntities"];
foreach (EntityReference rel in relatedentities)
{
// Check Related Entity Logical & Schema name
if (rel.LogicalName == "new_project" && relationShip.SchemaName == "new_systemuser_new_project")
{
Entity user = service.Retrieve("systemuser", target.Id, new ColumnSet(true));
Entity project = service.Retrieve("new_project", rel.Id, new ColumnSet(true));
// Grant access
RevokeAccessRequest revokeAccessRequest = new RevokeAccessRequest
{
Revokee=target,
Target = rel
};
service.Execute(revokeAccessRequest);
}
}
}
But I am not able to debug this plugin using plugin registration tool.It returns the message "App Domain Unloaded".
I don't know what is wrong with this plugin. Please Help.
I have solved this problem.
I had registered this plugin in update message, then changed RegisterFile.crmregister
MessageName="Disassociate" and PrimaryEntityName=""
also changed the default constructor of the my plugin class.
base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(10, "Disassociate", "", new Action<LocalPluginContext>(ExecutePreValidateProjectUpdate)));
Hope this will help someone facing same problem.
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'm trying to create a plugin that changes all related contacts' address fields if the parent account's address field is changed in account form. I created a plugin to run in pre operation stage (update message against account entity) synchronously.
I used LINQ query to retrieve all related contacts and it works. Then I'm using foreach loop to loop trough all contacts and change them address fields. I'm using OrganizationServiceContext.AddObject(); function to add every contact to the tracking pipeline (or whatever it's called) and finally I'm using OrganizationServiceContext.SaveChanges(); to trying to save all contacts. But that's when I'm getting this error:
System.InvalidOperationException: The context is already tracking the 'contact' entity.
Here's my code
// Updating child contacts' fields
if (context.PreEntityImages.Contains("preAccount") && context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) {
if (((Entity)context.InputParameters["Target"]).Contains("address2_name")) {
Entity account = (Entity)context.InputParameters["Target"];
Entity preAccount = (Entity)context.PreEntityImages["preAccount"];
if (account["address2_name"] != preAccount["address2_name"]) {
EntityReference parentCustomer = new EntityReference(account.LogicalName, account.Id);
Contact[] childContacts = orgService.ContactSet.Where(id => id.ParentCustomerId == parentCustomer).ToArray();
foreach (Contact contact in childContacts) {
contact.Address2_Name = (string)account["address2_name"];
orgService.AddObject(contact);
}
orgService.SaveChanges();
}
}
}
What I'm doing wrong?
You already attached the entities to the context when you retrieved the contacts with the query
Contact[] childContacts = orgService.ContactSet.Where(id => id.ParentCustomerId == parentCustomer).ToArray();
so you don't need to add again the entities to the context, instead you need to update them, by:
orgService.UpdateObject(contact); // this row instead of orgService.AddObject(contact);
I am creating a pre event plugin for CRM 2011 that sets the account owner and updates all the child contacts with the same owner. The plugin is installed correctly and updates the main account record correctly but the child contact owner does not change . I have pushed the owner name into another field of the contact to check I have the correct details and that field is updating.
I'm sure its something to do with attaching the child contacts to the correct context but so far I have drawn a blank.
//Set new account owner - Works fine
account.OwnerId = new EntityReference(SystemUser.EntityLogicalName, ownerId);
//Pass the same owner into the contacts - Does not get updated
UpdateContacts(account.Id, ownerId, service, tracingService);
The system is successfully updating the account owner and the description label of the child record.
public static void UpdateContacts(Guid parentCustomerId, Guid ownerId, IOrganizationService service, ITracingService tracingService)
{
// Create the FilterExpression.
FilterExpression filter = new FilterExpression();
// Set the properties of the filter.
filter.FilterOperator = LogicalOperator.And;
filter.AddCondition(new ConditionExpression("parentcustomerid", ConditionOperator.Equal, parentCustomerId));
// Create the QueryExpression object.
QueryExpression query = new QueryExpression();
// Set the properties of the QueryExpression object.
query.EntityName = Contact.EntityLogicalName;
query.ColumnSet = new ColumnSet(true);
query.Criteria = filter;
// Retrieve the contacts.
EntityCollection results = service.RetrieveMultiple(query);
tracingService.Trace("Results : " + results.Entities.Count);
SystemUser systemUser = (SystemUser)service.Retrieve(SystemUser.EntityLogicalName, ownerId, new ColumnSet(true));
tracingService.Trace("System User : " + systemUser.FullName);
XrmServiceContext xrmServiceContext = new XrmServiceContext(service);
for (int i = 0; i < results.Entities.Count; i++)
{
Contact contact = (Contact)results.Entities[i];
contact.OwnerId = new EntityReference(SystemUser.EntityLogicalName, systemUser.Id);
contact.Description = systemUser.FullName;
xrmServiceContext.Attach(contact);
xrmServiceContext.UpdateObject(contact);
xrmServiceContext.SaveChanges();
tracingService.Trace("Updating : " + contact.FullName);
}
}
The tracing service prints out everything I would expect. Do I need to also attach the system user and somehow attach the entity reference to the context?
Any help appreciated.
You have to make a separate web service call using AssignRequest to change the ownership of a record. Unfortunately you cannot just change the Owner attribute.
I think I was getting myself into all sorts of mess with this plugin as by default changing the account owner automatically changes the associated contacts owner. I was therefore trying to overwrite something that it was already doing.
By using the AssignRequest to set the account owner rather than the child records it worked fine. Credit given to Chris as he pointed me in the right direction.
All that was needed was to change the first line of my code to use AssignRequest and the entire UpdateContacts method became obselete.
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.