Below is a Plugin to create an Opportunity Clone [Cloning opportunity products as well].It uses early bound approach.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xrm.Sdk; using Microsoft.Crm.Sdk.Messages; using Entities = Pes.My.Configurations.Entities; using System.ServiceModel; using Pes.My.Configurations.Entities; using Microsoft.Xrm.Sdk.Query; using Microsoft.Xrm.Client; namespace My.Crm.Plugins.Opportunity { public class PostUpdate : IPlugin { public void Execute(IServiceProvider serviceProvider) { Entities.Opportunity oppRec; IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); // This plug-in was fired from the playback queue after the user selected to go online within Microsoft Dynamics CRM for Outlook. if (context.IsExecutingOffline || context.Depth > 1) return; if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { //Verify that the entity represents an Opportunity if (((Entity)context.InputParameters["Target"]).LogicalName != Pes.My.Configurations.Entities.Opportunity.EntityLogicalName || context.MessageName != "Update") return; else oppRec = ((Entity)context.InputParameters["Target"]).ToEntity<Pes.My.Configurations.Entities.Opportunity>(); } else { return; } try { IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); using (var MyContext = new MyContext(service)) { if (oppRec.My_IsCloned == true) { if (context.Depth == 1) { Entity opportunity = null; opportunity = service.Retrieve(Entities.Opportunity.EntityLogicalName, oppRec.Id, new ColumnSet(true)); Entity cloneopportunity = new Entity(); opportunity.Attributes.Remove("opportunityid"); opportunity.Id = Guid.NewGuid(); cloneopportunity = opportunity; Guid ClonedOppId = service.Create(cloneopportunity); List<Entities.OpportunityProduct> listoppproduct = MyContext.OpportunityProductSet .Where(acp => acp.OpportunityId.Id == oppRec.Id).ToList<Entities.OpportunityProduct>(); foreach (OpportunityProduct objoppproduct in listoppproduct) { cloneOppProduct((Guid)objoppproduct.Id, ClonedOppId, service); } } } } } catch (InvalidPluginExecutionException) { throw; } catch (FaultException<OrganizationServiceFault> ex) { throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex); } } private void cloneOppProduct(Guid guid, Guid Opp, IOrganizationService service) { Entity opportunityproduct = null; opportunityproduct = service.Retrieve(Entities.OpportunityProduct.EntityLogicalName, guid, new ColumnSet(true)); Entity cloneopportunityproduct = new Entity(); opportunityproduct.Attributes.Remove("opportunityproductid"); opportunityproduct.Attributes.Remove("opportunityid"); opportunityproduct.Id = Guid.NewGuid(); opportunityproduct.Attributes["opportunityid"] = new EntityReference(Entities.Opportunity.EntityLogicalName, Opp); cloneopportunityproduct = opportunityproduct; service.Create(cloneopportunityproduct); } } }
Note: I run this plugin from ribbon button "Clone Opportunity".Here I am setting Custom field "My_IsCloned " to true and saving the form using javaScript.And On post update it triggers this plugin.
Also you have to create an early bound class using CrmSvcUtil below is an example
CrmSvcUtil.exe /out:MyEntities.cs /url:https://Mydev1.api.crm.dynamics.com/XRMServices/2011/Organization.svc /username:name@My.com /password:pass123 /namespace:Pes.My.Configurations.Entities /serviceContextName:MyContext
Use the class generated i.e MyEntities [here] in your project and accomplish the same.
Hope this helps.
Yusuf
I tried what you said above but i am getting error while creating new entity.error is "Generic SQL error".this is my code
ReplyDeleteEntity newAccount = null;
Entity oldAccount = new Entity();
oldAccount = service.Retrieve("account", AccountID, new ColumnSet(true));
if (oldAccount != null)
{
oldAccount .Attributes.Remove("accountid");
oldAccount .Id = Guid.NewGuid();
newAccount = oldAccount ;
Guid accountID = service.Create(newAccount);
}
Could u please give any solution for this?
ReplyDeletecontext.Depth > 1 is very much a valid scenario and should not be used as a check against looping. If you had a plugin on save of one entity that creates or updates a second entity, any messages registered on that second entity would have a context of 2. Same can apply to workflow triggering during data import etc.
ReplyDeleteThe system will automatically error out if the depth > 8. There's no reason to check the depth for looping unless you have bad code / logic.