OnSave Mode List

In the past I have had to explain to people that because you are filling in a seperate (non-customisable) form to mark an oppertunity as won then there is no way to tell what is happening to the opp as it is saved.

However while searching for something else I came across this article that details a very neat work around and one that I will be using in the future.

Adding a Value to a Picklist

I have had cause to add values programmatically to a picklist in the contact entity. It is a custom picklist whose values change depending on actions on another entity. I struggled to pull together all the elements that I needed for this solution from areas on the net so I thought I would post this in case anyone faces a similar problem.

For a new picklist item you need a string to appear in the dropdown box and an integer that represents the choice in the entity.

This is the code I used:


public void addCourse(string course)
{
if (!inPicklist(course))
{
InsertOptionValueRequest request;
int lastValue = 0;
Option newCourse = new Option();
LocLabel label = new LocLabel();

newCourse.Label = new CrmLabel();
newCourse.Value = new CrmNumber();
foreach (Option o in _picklist.Options)
{
int thisValue = o.Value.Value;
if (thisValue > lastValue) lastValue = thisValue;
}
newCourse.Value.Value = lastValue + 1;

label.Label = course;
newCourse.Label.LocLabels = new LocLabel[] { label };
newCourse.Label.LocLabels[0].LanguageCode = new CrmNumber();
newCourse.Label.LocLabels[0].LanguageCode.Value = 1033;

request = new InsertOptionValueRequest();
request.EntityLogicalName = ENTITYLOGICALNAME;
request.AttributeLogicalName = LOGICALNAME;
request.Label = newCourse.Label;
request.Value = newCourse.Value;

InsertOptionValueResponse response =
(InsertOptionValueResponse)_metadataService.Execute(request);
publishChange();
}
}


The important thing to note is that you cannot edit the picklist directly. It is a concept I forget and struggle with sometimes when working with metadata that it exists in both a published and unpublished state. We use the InsertOptionValueRequest method to alter the picklist, adding an unpublished option to the list. If it were just to stay at that then the value would not appear until the next time someone published the entity, although you could see the value in the entity design screens. For clarity my two constants above that are declared elsewhere in the code are:


ENTITYLOGICALNAME = "contact";
LOGICALNAME="new_course"; //the list I want to change



The very last line of my codeblock then forces the publication of entity.




private void publishChange()
{
CrmService.PublishXmlRequest request =
new CrmService.PublishXmlRequest();

request.ParameterXml = @"<importexportxml>
<entities>
<entity>contact</entity>
</entities>
<nodes>
<securityroles>
<settings>
<workflows>
</importexportxml>";


CrmService.PublishXmlResponse response =
(CrmService.PublishXmlResponse)_crmService.Execute(request);
}



For performance I had put this code into a DLL in the GAC (it contains a library of actions I wanted in a single place) and is called by asynchronous process in a plugin. Having said that the action that fires it will be infrequent and may not actually happen once the system is provisioned and goes live. But either way I hope that I have mitigated any lag when the user clicks the Save button.

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:

  1. Create a new contact record
  2. Create the related entity
  3. 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.

private static DynamicEntity GetBlankEntity(string entityName,
string primaryKeyAttribute, Guid id)
{
DynamicEntity entity = new DynamicEntity(entityName);
entity.Properties[primaryKeyAttribute] = new Key(id);
return entity;
}

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.

Finally the whole point of the plugin. To 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;
}

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.

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;
}