I am what you call a "n00b" in CRM plugin development. I am trying to write a plugin for Microsoft's Dynamics CRM 2011 that will create a new activity entity when you create a new contact. I want this activity entity to be associated with the contact entity.
This is my current code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
namespace ITPH_CRM_Deactivate_Account_SSP_Disable
{
public class SSPDisable_Plugin: IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
if (context.InputParameters.Contains("Target") && context.InputParameters["target"] is Entity)
{
Entity entity = context.InputParameters["Target"] as Entity;
if (entity.LogicalName != "account")
{
return;
}
Entity followup = new Entity();
followup.LogicalName = "activitypointer";
followup.Attributes = new AttributeCollection();
followup.Attributes.Add("subject", "Created via Plugin.");
followup.Attributes.Add("description", "This is generated by the magic of C# ...");
followup.Attributes.Add("scheduledstart", DateTime.Now.AddDays(3));
followup.Attributes.Add("actualend", DateTime.Now.AddDays(5));
if (context.OutputParameters.Contains("id"))
{
Guid regardingobjectid = new Guid(context.OutputParameters["id"].ToString());
string regardingobjectidType = "account";
followup["regardingobjectid"] = new EntityReference(regardingobjectidType, regardingobjectid);
}
service.Create(followup);
}
}
}
But when i try to run this code: I get an error when i try to create a new contact in the CRM environment. The error is: "The given key was not present in the dictionary" (Link *1). The error pops up right as i try to save the new contact.
Link *1: http://puu.sh/4SXrW.png
(Translated bold text: "Error on business process")
Microsoft Dynamics CRM uses the term activity to describe several types of interactions. The types of activities are:
Phone Call, Task, E-mail, Letter, Fax and Appointment.
ActivityPointer (Activity) Entity
To make your code working Replace the following line:
Entity followup = new Entity();
with
Entity followup = new Entity("task");
And remove the following line:
followup.LogicalName = "activitypointer";
Also please read my comment and Guido Preite's commend above. You need to modify your code to make it working with contact.
Edited
Make sure ContactId does exist in CRM before referencing it to Activity.
This can often happen if you are explicity adding an attribute value to the target entity in you plugin where it already has been added.
Rather than entity.Attributes.Add(...)
use entity["attributename"] = ...
Related
Hi I'm new to Dynamics and plugins in dynamics. I have created a simple Entity called Library that holds books.
After a new book is created I want the price of the book to increment by a GST of 10% on the server side via a plugin.
I know this would normally occur on the page before saving by I'm trying to work out how server side logic works.
I have created a postOperation (synchronous) step for the "Create" message to call the Plugin Execute() method. From my reading this should occur AFTER the record is saved in the database.
I also have a post image entity that I access.
In the Execute method I try to access the saved record via the PostMessageEntity to update the price, but I get an exception saying the record does not exist based on the record identifier that i have obtained. I can confirm the record was never created in the system, yet the postOperation has been called.
How do I access the just saved record in the plugin so that I can update the Price?
My code:
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider.
Microsoft.Xrm.Sdk.IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)
serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
// create a trace log so you can see where in the code it breaks
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
// create access to service
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
tracingService.Trace("have reached execute event in plugin.");
// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
tracingService.Trace("We have a target and it is an entity.");
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName == "new_books")
{
tracingService.Trace("the entity id of the record that was created is .." + entity.Attributes["new_booksid"].ToString());
// do we have a post update image of the new_books entity
if (context.PostEntityImages.Contains("newbookpostImage") && context.PostEntityImages["newbookpostImage"] is Entity)
{
tracingService.Trace("we have a postEntityImage.");
// // yep lets grab it.
Entity postMessageEntity = (Entity)context.PostEntityImages["newbookpostImage"];
// get book price as just saved to db
decimal bookPrice = ((Money)postMessageEntity.Attributes["new_price"]).Value;
// get id of the the record we have
Guid RecordID = ((Guid)postMessageEntity.Attributes["new_booksid"]);
tracingService.Trace("we have a post update bookprice.");
tracingService.Trace("the entity id of the post image entity is ..." + postMessageEntity.Attributes["new_booksid"].ToString());
Entity created_book = new Entity("new_books");
// use service to access a field of the current record as it is in the database and column we want to update.
created_book = service.Retrieve(created_book.LogicalName, RecordID, new ColumnSet(true));
//And the last line is where it dies and tells me new_books with id with d7bfc9e2 - 2257 - ec11 - 8f8f - 00224814e6e0 does not exist.
}
}
}
}
Entity postMessageEntity = (Entity)context.PostEntityImages["newbookpostImage"];
Is your PostEntityImage new_books entity?
Also if you have an entity postMessageEntity you can directly get Entity Record ID by
postMessageEntity.ID
rather than Guid RecordID = ((Guid)postMessageEntity.Attributes["new_booksid"]);
Here your code does nothing more than create empty object of type Entity new_books.
You have not set Priamry name field of entiy or any other. Also you have not created a record, you should use
Entity created_book = new Entity("new_books")
service.Create(created_book);
Below you are trying to fecth Record from Entity new_books based on postMessageEntity.Id
You should check postMessageEntity logical name is same as created_book.LogicalName and then use postMessageEntity.ID rather than RecordID
created_book = service.Retrieve(created_book.LogicalName, RecordID, new ColumnSet(true));
In the plugin pipeline you can actually add, modify and even remove attributes in the entity object on the fly. This must be done before the main operations take place: in the prevalidation or in the preoperation stage.
So, your code can be simplified like this:
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
Debug.Assert(context.Stage <= 20); // This only works in prevalidation and preoperation stages.
var book = (Entity)context.InputParameters["Target"]; // For message Create a Target parameter is always available.
// Using GetAttributeValue is more safe, because when price is not set, the attribute will not be available in the collection.
decimal? price = book.GetAttributeValue<Money>("new_price")?.Value;
if (price.HasValue)
book["new_price"] = new Money(price.Value * 1.1M);
}
In the synchronous post create stage you are still in a database transaction. At that point the record is created, but not yet committed.
The ID of the record created can be found in the OutputParameters collection. You can pick it up like this:
var recordId = (Guid)context.OutputParameters["id"];
There is no need to do checks on the context object. When your plugin is registered properly, all items you would expect to be available will be there. If not, a proper exception log will be your best friend. Just add a generic exception handler responsible for writing the error context to the plugin trace log.
i have a lookup "new_lookuptransactionheader" in my entity "new_trialxrmservicetoolkit". This lookup linked from "new_transactionheader" entity. How can i insert data using c# crm plugin?
this mycode:
public void InsertDataUsingLookup(XrmServiceContext xrm, Entity entitiy, ITracingService tracer)
{
new_trialxrmservicetoolkit trial = new new_trialxrmservicetoolkit();
trial.new_name = "testplugin";
trial.new_LookupTransactionHeader = null; //this is i don't know how to get value from new_LookupTransactionHeader
trial.new_Notes = "this is test plugin using c#";
xrm.AddObject(trial);
xrm.SaveChanges();
}
I updated mycode and this solve:
public void InsertDataUsingLookup(XrmServiceContext xrm, Entity entitiy, ITracingService tracer)
{
new_trialxrmservicetoolkit trial = new new_trialxrmservicetoolkit();
trial.new_name = "testplugin";
trial.new_LookupTransactionHeader = new EntityReference("new_transactionheader", Guid.Parse("5564B5F0-0292-E711-8122-E3FE48DB937B"));
trial.new_Notes = "this is test plugin using c#";
xrm.AddObject(trial);
xrm.SaveChanges();
}
trial.Attributes["new_LookupTransactionHeader"] = new EntityReference("new_transactionheader", new_transactionheaderId-GUID);
You have to use EntityReference like above to setup lookup attribute.
I'm fairly new to developing but I've encountered a problem when creating a plugin for Dynamics CRM. The plugin is supposed to render the parentcustomerid field null on contact entity when the account entity it is linked to is updated when the field primarycontactid is updated to something else, either that be null on linked to another contact.
With the code I have written currently no errors are thrown and the code is executed successful but parentcustomerid field still contains the account it is linked too when it should remove the link.
Here is the code i have currently:
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
//Obtain the execution context
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
//obtain organizational services
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
EntityReference prePCID;
Entity PreImage;
// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the image entity from the Pre Entity Images.
tracingService.Trace
("trace1: Getting the target entity from Input Parameters.");
PreImage = (Entity)context.InputParameters["Target"];
// Verify that the target entity represents an account.
// If not, this plug-in was not registered correctly.
tracingService.Trace
("trace2: Verifying that the target entity represents a account.");
if (PreImage.LogicalName == "account")
{
if (PreImage.Attributes.Contains("primarycontactid"))
{
tracingService.Trace
("trace3: Setting the primary contact id in the prePCID.");
//prePCID = (EntityReference)PreImage.Attributes["primarycontactid"];
prePCID = (EntityReference)PreImage["primarycontactid"];
tracingService.Trace
("trace4: Primary Contact Name: " + prePCID.Name + " Creating a variable that stores the contact using the prePCID.");
Entity contactToRemoveReference = service.Retrieve("contact", prePCID.Id, new ColumnSet("parentcustomerid")) as Entity;
tracingService.Trace
("trace5: Removes the id: " + prePCID.Id + " of the parentcustomerid.");
contactToRemoveReference.Attributes["parentcustomerid"] = null;
service.Update(contactToRemoveReference);
tracingService.Trace
("trace6: Execution Successful.");
}
}
}
}
If anyone could solve this issue for me, that would be great thanks.
You are populating Entity variable PreImage from primary entity in the input parameters.
I think you should get it from Pre image like:
Entity PreImage;
if (context.PreEntityImages.Contains("yourPreImageName") && context.PreEntityImages["yourPreImageName"] != null)
{
PreImage = context.PreEntityImages["yourPreImageName"];
}
I am trying to make a plugin for CRM 2011 that will update some values in a contact entity when it has been disabled.
When a contact gets disabled: I want it to change 3 radio buttons to "Nei" (No in Norwegian). This will disable the access to my self-service portal i have for my customers. You can see a picture of my contact entity with the radio buttons in it here. I want to force all those radio buttons to "Nei" when contact is disabled.
I am a complete beginner with CRM plugin development, and a fairly new user of C#. So please keep it as simple as possible.
I have been reading through the manuals for weeks and i cant seem to get anywhere. (Well, Microsoft is not known for their well written manuals).
You need to register your plugin to both SetState and SetStateDynamicEntity messages, with Pre-Operation as execution stage. Take this code as an example:
using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
namespace TestPlugin
{
public class UpdateBoolFields : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
try
{
if (context.InputParameters.Contains("EntityMoniker") &&
context.InputParameters["EntityMoniker"] is EntityReference)
{
EntityReference targetEntity = (EntityReference)context.InputParameters["EntityMoniker"];
OptionSetValue state = (OptionSetValue)context.InputParameters["State"];
if (state.Value == 1)// I'm not sure is 1 for deactivate
{
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
Entity contact = service.Retrieve(targetEntity.LogicalName, targetEntity.Id, new ColumnSet(true));
contact["field1"] = false;
contact["field2"] = false;
contact["field3"] = false;
service.Update(contact);
}
}
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message);
}
}
}
}
In CRM when emails arrive and have the tracking token in them they automatically set the regarding field to be the incident (or whatever they relate to)
Unfortunately the Record wall isn't updated with this info so even if you are following the case nothing alerts you to the new activity.
I want to write a plugin on email or incident (or both) that updates the record wall and creates a task to follow up on that email with in 3 days.
I'm looking at the SDK and I can't see what the appropriate event in the pipe line would be to work out when an email is/has its regarding field set on arrival in the CRM.
The CRM email creation life-cycle is not well described in the documentation. [shakes fist]
Extra things that are bothering me
I can't seem to include a reference to get a strongly typed Email, Post or Case (driving me crazy)
Testing this is really hard (harder than it should be)
EDIT Here is my current code
namespace Assembly.Plugins
{
using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// PostEmailDeliverIncoming Plugin.
/// </summary>
public class PostEmailDeliverIncoming : Plugin
{
/// <summary>
/// Initializes a new instance of the <see cref="PostEmailDeliverIncoming"/> class.
/// </summary>
public PostEmailDeliverIncoming()
: base(typeof(PostEmailDeliverIncoming))
{
RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "DeliverIncoming", "email", ExecutePostEmailDeliverIncoming));
// Note : you can register for more events here if this plugin is not specific to an individual entity and message combination.
// You may also need to update your RegisterFile.crmregister plug-in registration file to reflect any change.
}
protected void ExecutePostEmailDeliverIncoming(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
//Extract the tracing service for use in debugging sandboxed plug-ins.
ITracingService tracingService = localContext.TracingService;
// Obtain the execution context from the service provider.
IPluginExecutionContext context = localContext.PluginExecutionContext;
// Obtain the organization service reference.
var service = localContext.OrganizationService;
// The InputParameters collection contains all the data passed in the message request.
if (!context.InputParameters.Contains("Target") || !(context.InputParameters["Target"] is Entity))
return;
// Obtain the target entity from the input parmameters.
var target = (Entity)context.InputParameters["Target"];
// Verify that the target entity represents an account.
// If not, this plug-in was not registered correctly.
if (target.LogicalName != "email")
return;
if((string)target["direction"] != "Incoming")
return;
if (target["regardingobjectid"] == null)
return;
try
{
// if its not a case I don't care
var incident = service.Retrieve("incident", (Guid)target["regardingobjectid"], new ColumnSet(true));
if (incident == null)
return;
var post = new Entity("post");
post["regardingobjectid"] = target["regardingobjectid"];
post["source"]=new OptionSetValue(0);
post["text"] = String.Format("a new email has arrived.");
// Create the task in Microsoft Dynamics CRM.
tracingService.Trace("FollowupPlugin: Creating the post.");
service.Create(post);
// Create a task activity to follow up with the account customer in 7 days.
var followup = new Entity("task");
followup["subject"] = "Follow up incoming email.";
followup["description"] = "An email arrived that was assigned to a case please follow it up.";
followup["scheduledstart"] = DateTime.Now.AddDays(3);
followup["scheduledend"] = DateTime.Now.AddDays(3);
followup["category"] = context.PrimaryEntityName;
// Refer to the email in the task activity.
if (context.OutputParameters.Contains("id"))
{
var regardingobjectid = new Guid(context.OutputParameters["id"].ToString());
followup["regardingobjectid"] = new EntityReference("email", regardingobjectid);
}
// Create the task in Microsoft Dynamics CRM.
tracingService.Trace("FollowupPlugin: Creating the task activity.");
service.Create(followup);
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occurred in the FollupupPlugin plug-in.", ex);
}
catch (Exception ex)
{
tracingService.Trace("FollowupPlugin: {0}", ex.ToString());
throw;
}
}
}
}
I've just been fighting with this exact same issue and came across this post. I thought I'd post the solution for you (if you still need it) and anyone else who comes across the issue in the future.
Here's the solution I arrived at:
- Using the Plugin Registration Tool register a New Image on the appropriate step( Stage = "40", MessageName = "DeliverIncoming")
- Set the New Image to be a Post Image
- In your plugin fetch the Post Image's entity ID:
Guid emailID = context.PostEntityImages["PostImage"].Id;
Entity emailFromRetrieve = localContext.OrganizationService.Retrieve(
"email",
emailID,
new Microsoft.Xrm.Sdk.Query.ColumnSet(true));
Email email = emailFromRetrieve.ToEntity<Email>();
if (email.RegardingObjectId == null)
{
return;
}
var regardingObject = email.RegardingObjectId;
Hope this helps!
I'm actually working on a very similar plugin at the moment. Mine creates a custom entity upon arrival of an email addressed to a certain email address. It also associates the incoming email with that new record via the Regarding field. I've added a Pre-Operation step on Create of Email and it works great, including incoming email from the router.
What I'm not sure of is when CRM fills in the Regarding field. You might look at Post-Operation and see if it is set there?
One interesting caveat regarding the Regarding field (haha!): Unlike single lookup fields, the Regarding object's name is actually stored in the ActivityPointer table, so when you update the Regarding field, be sure to set the Name on the EntityReference. If you don't, the Regarding lookup will still have a clickable icon but there won't be any text. I do it like this:
email.RegardingObjectId = [yourentity].ToEntityReference();
email.RegardingObjectId.Name = email.Subject;
Hope that helps!
I ended up doing this in a workflow on the email entity
Steps
Create new workflow, I called it 'incoming email workflow'
Scope is Organisation
Choose Email as the entity and check 'Record field changes'
Add a step that checks Regarding (Case):Case Contains Data
if true:
Add a step that creates a Post
Edit the properties in the Post
Text : This case has had {Direction(E-mail)} email activity from {From(E-mail)}
Source : Auto Post
Regarding : {Regarding(E-mail)}
Add a step that creates a Task
Edit the properties in the Task
Subject : Follow up {Subject(E-mail)}
Regarding : {Regarding(E-mail)}
Try to use the following code:
if ((bool)entity["directioncode"] == false)
Instead of your code:
if((string)target["direction"] != "Incoming")