Beginning Plugin Development - Part 1
Eventually we will need to code some plugins. If you want to do anything with this platform that is worth anything a plugin will be required more often than not.
I hope to cover off some basic concepts of plugin development and give some hints to how to complete different problems.
Pre-requisites for this will be:
- Visual Studio 2008
- CRM 4.0
- MSCRM SDK
- and an operational Plugin Registration (comes with the SDK) . Good guide here
I don't intend to go over creating a plugin from scratch, I use a VS template that builds the basic plugin library for me. Lazy I know but worth it. The templates are also in the SDK.
In this article I will cover off a simple requirement I have had. I need to relate a single entity to my contact entity and display it in an IFRAME. The IRFAME code is very simple and takes the custom entity object ID from a field in the contact record and uses that to build the frame URL.
So my steps are:
- Create a new contact record
- Create the related entity
- Write the related entity ID to the contact record
The first step is simple enough. When a new contact record (in my example representing a student) is created the plugin is fired. I want this to be on the post-create event so that I can be sure that the student has passed all other validation before I create the finance record for them. This is achieved by registering the plugin as post stage thus
Of course we need some code to write first. We are going to get the contactid of the record, create a new custom entity type and save that to the CRM system.
ICrmService crmService = context.CreateCrmService(true);
DynamicEntity StudentDetails =
(DynamicEntity)context.PostEntityImages["StudentDetails"];
DynamicEntity
workingStudent = GetBlankEntity("contact", "contactid",
((Key)StudentDetails.Properties["contactid"]).Value);
//Create a
finance account against the student
DynamicEntity finance =
provisionFinanceAccount(((Key)StudentDetails.Properties["contactid"]).Value);
finance.Properties.Add(new
StringProperty("xxx_name",StudentDetails.Properties["fullname"].ToString()));
Guid financeKey = crmService.Create(finance);
//Write finance
account ID back to student record
workingStudent.Properties.Add(new
StringProperty("xxx_financeaccount",financeKey.ToString()));
crmService.Update(workingStudent);
So the first thing to cover off is the PostEntityImages call on the second line. This allows us to see what data we want in the final record. The registration tool gives us a really neat little window on this via the GUI. We register an image to the step (in this case I have called it "StudentDetails") and you can add or remove whatever fields you get. By default when you register an image it gives you all columns. Best practice is to remove everything and then explicitly add in what you need. In my case I want the contactid and the fullname fields
Then from the code
(DynamicEntity)context.PostEntityImages["StudentDetails"];
We can access the image we have created in the registration tool by simply passing the name to the collection of PostEntityImages casting it to DynamicEntity on the way back.
Second "Best Practice" element is how you interact with the contact entity. By default mine passes 28 fields to the plugin. If I were to add a field to this collection and pass it back for update that is a huge network overhead that on production environments could start to grind things down. In this case I have a method that takes in the entity type, the primary key name and value and returns a blank DynamicEntity for you to work with. The source was taken from Andrew Zimmer's blog on best practice.
Now I can add the field to store the finance object's ID and send that back to the service, I have reduced 28 fields to 3.private static DynamicEntity GetBlankEntity(string entityName,
string primaryKeyAttribute, Guid id)
{
DynamicEntity entity = new DynamicEntity(entityName);
entity.Properties[primaryKeyAttribute] = new Key(id);
return entity;
}
Finally the whole point of the plugin. To create the finance object.
This returns a DynamicEntity with the basiscs of what I need, the object type and a lookup to the contact record set. Once this has been created I add the contact.fullname into the custom entity (for seperate views I will use this to indicate what student it belongs to but will expand to include student number as the system develops). Once this is assigned we can create the finance object.private DynamicEntity provisionFinanceAccount(Guid contactid)
{
DynamicEntity finance = new DynamicEntity();
finance.Properties =
new PropertyCollection();
finance.Name = "xxx_financeaccount";
Lookup student = new Lookup();
student.type =
EntityName.contact.ToString();
student.Value = contactid;
finance.Properties.Add(new LookupProperty("xxx_ownerid", student));
return finance;
}
Guid financeKey = crmService.Create(finance);
The crmService.Create method returns the GUID of the object once it is created. I need this to link the IFRAME on my form so I am using this in a second step.
workingStudent.Properties.Add(new StringProperty("xxx_financeaccount",financeKey.ToString()));
crmService.Update(workingStudent);
As this workingStudent object already has the primary key set calling the updated takes the new property and writes it back to the database without disturbing any other data.
And that is that. Following the creation of a student record my system automatically creates a finance account, storing the finance id back in the student record where I use it in an IFRAME to show the single entity with further sub-forms.
All the code discussed here:
public void Execute(IPluginExecutionContext context)
{
DynamicEntity entity = null;
// Check if the InputParameters property bag contains a target
// of the current operation and that target is of type DynamicEntity.
if (context.InputParameters.Properties.Contains(ParameterName.Target) &&
context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
{
// Obtain the target business entity from the input parmameters.
entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
if (entity.Name != EntityName.contact.ToString())
{
return;
}
}
else
{
return;
}
try
{
ICrmService crmService = context.CreateCrmService(true);
DynamicEntity StudentDetails = (DynamicEntity)context.PostEntityImages["StudentDetails"];
DynamicEntity workingStudent = GetBlankEntity("contact", "contactid", ((Key)StudentDetails.Properties["contactid"]).Value);
//Create a finance account against the student
DynamicEntity finance = provisionFinanceAccount(((Key)StudentDetails.Properties["contactid"]).Value);
finance.Properties.Add(new StringProperty("kcb_name",StudentDetails.Properties["fullname"].ToString()));
Guid financeKey = crmService.Create(finance);
//Write finance account ID back to student record
workingStudent.Properties.Add(new StringProperty("kcb_financeaccount",financeKey.ToString()));
crmService.Update(workingStudent);
}
catch (System.Web.Services.Protocols.SoapException ex)
{
throw new InvalidPluginExecutionException(
String.Format("An error occurred in the {0} plug-in.",
this.GetType().ToString()),
ex);
}
}
private DynamicEntity provisionFinanceAccount(Guid contactid)
{
DynamicEntity finance = new DynamicEntity();
finance.Properties = new PropertyCollection();
finance.Name = "kcb_financeaccount";
Lookup student = new Lookup();
student.type = EntityName.contact.ToString();
student.Value = contactid;
finance.Properties.Add(new LookupProperty("kcb_ownerid", student));
//Need to set the initial account balances to 0 here.
return finance;
}
///
/// Returns a clone of the required entity but with only the primary key. You can then add
/// the columns you are changing. Keeps network traffic down as only those columns being changed
/// are sent back to the service.
///
///
///
///
///
private static DynamicEntity GetBlankEntity(string entityName, string primaryKeyAttribute, Guid id)
{
DynamicEntity entity = new DynamicEntity(entityName);
entity.Properties[primaryKeyAttribute] = new Key(id);
return entity;
}
0 Responses to “Beginning Plugin Development - Part 1”:
Post a Comment