Dynamics CRM商机子网格同步问题:预计时间变更未同步至子网格
Fixing Dynamics CRM Plugin to Sync Opportunity's Estimated Close Date to Opportunity Products
Let's break down what's going wrong with your plugin and fix it step by step:
Key Issues in Your Original Code
- Incorrect trigger condition: You're checking if the
namefield exists in the target entity, but your requirement is to react whenestimatedclosedatechanges. This means your code only runs when the opportunity's name is updated, not when the close date is modified. - Wrong FetchXML query: You're querying the
opportunityentity instead ofopportunityproduct(the related line items). The code also references contacts unnecessarily—you need to target linked opportunity products, not contacts. - Invalid update logic: You're trying to update an
opportunityproductusing the ID of anopportunityrecord. ThecontactEntvariable holds an opportunity record, so you're attempting to modify a non-existent opportunity product with an invalid ID.
Corrected Plugin Code
public void Execute(IServiceProvider serviceProvider) { // Extract core services from the provider ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); try { // Verify we're handling an Opportunity Update message with a valid target if (context.MessageName == "Update" && context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity opportunityEntity) { // Get the new estimated close date (check Target first, then Post Image as fallback) DateTime? newCloseDate = null; if (opportunityEntity.Contains("estimatedclosedate")) { newCloseDate = (DateTime?)opportunityEntity["estimatedclosedate"]; } else if (context.PostEntityImages.Contains("PostImage") && context.PostEntityImages["PostImage"].Contains("estimatedclosedate")) { newCloseDate = (DateTime?)context.PostEntityImages["PostImage"]["estimatedclosedate"]; } if (newCloseDate.HasValue) { tracingService.Trace($"Syncing close date {newCloseDate.Value} to products for Opportunity ID: {opportunityEntity.Id}"); // Fetch all opportunity products linked to this opportunity string fetchXml = $@" <fetch no-lock='true'> <entity name='opportunityproduct'> <attribute name='opportunityproductid'/> <filter> <condition attribute='opportunityid' operator='eq' value='{opportunityEntity.Id}'/> </filter> </entity> </fetch>"; EntityCollection opportunityProducts = service.RetrieveMultiple(new FetchExpression(fetchXml)); tracingService.Trace($"Found {opportunityProducts.Entities.Count} products to update"); // Update each product's new_valid_to field foreach (Entity product in opportunityProducts.Entities) { Entity productToUpdate = new Entity("opportunityproduct", product.Id); productToUpdate["new_valid_to"] = newCloseDate.Value; service.Update(productToUpdate); tracingService.Trace($"Updated Product ID: {product.Id} with new_valid_to: {newCloseDate.Value}"); } } else { tracingService.Trace("No valid estimatedclosedate value found to sync"); } } } catch (Exception ex) { tracingService.Trace($"Plugin error: {ex.Message}"); throw new InvalidPluginExecutionException("Failed to sync opportunity close date to products: " + ex.Message, ex); } }
Critical Improvements Explained
- Targeted trigger: We first confirm this is an
Updatemessage for an Opportunity, then check ifestimatedclosedateis present (either in the Target or Post Image, to cover cases where only changed fields are sent). - Fixed product query: The FetchXML now correctly pulls all
opportunityproductrecords linked to the updated Opportunity, using their valid IDs. - Safe update logic: We use the actual opportunity product IDs to create valid update requests, setting
new_valid_toto the opportunity's new close date. - Error handling: Added try/catch with tracing to help debug issues, and throws a meaningful exception if syncing fails.
Plugin Registration Requirements
To ensure the plugin works correctly:
- Message: Register on the
Updatemessage of theopportunityentity. - Stage: Use the Post-operation stage (to work with the final updated Opportunity data).
- Filtering Attributes: Set
estimatedclosedateas the filtering attribute—this ensures the plugin only runs when this specific field changes, improving performance. - Post Image: Add a Post Image that includes the
estimatedclosedatefield (this acts as a fallback if the Target doesn't include the field, e.g., when other fields are updated alongside it).
内容的提问来源于stack exchange,提问作者Ahmed Mehany
相关产品推荐
相关产品推荐

