OptionSetValue oValue = (OptionSetValue)entity.Attributes["new_myentity"]; int optionSetValue = oValue.Value; var pOptionMetadata = optionList.Where(c => c.Value == optionSetValue).FirstOrDefault(); string myOptionSetText=pOptionMetadata .Label.LocalizedLabels[0].Label public OptionMetadata[] GetOptionMetadata(string logicalEntityName, string optionSetAttributeName, IOrganizationService service) { RetrieveAttributeRequest retrieveAttributeRequest = new RetrieveAttributeRequest { EntityLogicalName = logicalEntityName, LogicalName = optionSetAttributeName, RetrieveAsIfPublished = true }; // Execute the request. RetrieveAttributeResponse retrieveAttributeResponse = (RetrieveAttributeResponse)service.Execute( retrieveAttributeRequest); // Access the retrieved attribute. PicklistAttributeMetadata retrievedPicklistAttributeMetadata = (PicklistAttributeMetadata) retrieveAttributeResponse.AttributeMetadata; // Get the current options list for the retrieved attribute. OptionMetadata[] optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray(); return optionList; }
Thursday, December 22, 2011
How to get OptionSet value inside a plugin
Wednesday, December 21, 2011
Auto number solution for Dynamics CRM 2011
Today I am going to show you how to write an auto number plugin for CRM 2011.
Here are the aims of this plugin
As you can see from the screen shot the auto number entity contains the following fields:
Entity Name: this would hold the name of the entity where you want to apply the auto number to
Entify Autonumber Field: this is the field of the Entity that would show and hold the autonumber.
Prefix, Prefix Separator, Suffix and Suffix Separator are self explanatory.
Increment unit: It is the increment number for the auto number field.
Counter: It is the starting number for the auto number field and it will subsequently show the incremented number as updated in the plugin.
Number formatter: This is a formatter that will be applied to the number field (i.e. counter) so for example the counter is 544 and the formatter is 0000 that will end up being 0544.
Okay so now that we have our auto number entity set up, we need to write the plugin.
I am also using a mutex, this is there to ensure that concurrent create requests cannot occur simultaneously, it's not the best solution but it works.
The above plugin code will generate the increment number, but before that can happen we have to register the plugin using the plugin registeration tool. We need to register the assembly and the create a step for the entity where we want to run this auto number plugin on. We have to register it on the post-create step for the "Create" event of your custom entity. The registeration tool can be found in the SDK\tools directory.
Thats it! your auto number is ready to go. Notice that this plugin can service multiple entities and can be easily ported to any organisation, also it can be customized from the interface and prefixes and suffixes can be added / changed.
Here are the aims of this plugin
- First and foremost it should be able to generate auto numbers
- Can be used for multiple entities
- Can be ported to multiple organisations
- Should be configurable from the interface
- We should be able to change the starting number
- We should be able to change the increment number
- We should be able to change the prefix/suffix and add any separators
![]() |
Auto Number Entity |
Entity Name: this would hold the name of the entity where you want to apply the auto number to
Entify Autonumber Field: this is the field of the Entity that would show and hold the autonumber.
Prefix, Prefix Separator, Suffix and Suffix Separator are self explanatory.
Increment unit: It is the increment number for the auto number field.
Counter: It is the starting number for the auto number field and it will subsequently show the incremented number as updated in the plugin.
Number formatter: This is a formatter that will be applied to the number field (i.e. counter) so for example the counter is 544 and the formatter is 0000 that will end up being 0544.
Okay so now that we have our auto number entity set up, we need to write the plugin.
public class AutoNumberPlugin:IPlugin { public void Execute(IServiceProvider serviceProvider) { Microsoft.Xrm.Sdk.IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext) serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext)); if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { Entity entity = (Entity)context.InputParameters["Target"]; IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); string fetchXml = @"<fetch mapping='logical'> <entity name='agd_autonumber'><all-attributes/> <filter type=""and""> <condition attribute=""agd_entityname"" operator=""eq"" value='" + entity.LogicalName + "'" + " /></filter></entity></fetch>"; System.Threading.Mutex mtx = null; try { string mutextName = string.Format("{0}{1}", entity.LogicalName, "Autonumber"); mtx = new System.Threading.Mutex(false, mutextName); mtx.WaitOne(); //get hold on the mutex and only release it after update was done ot the agd_counter entity // not sure if this will work in a multi server environment EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetchXml)); string nextIncrementNumber = string.Empty; if (result.Entities.Count == 1) { //retrieve the counter Entity autoNumber = result.Entities[0]; if (!autoNumber.Attributes.Contains("agd_counter")) throw new InvalidPluginExecutionException("agd_counter must contain a value"); if (!autoNumber.Attributes.Contains("agd_incrementunit")) throw new InvalidPluginExecutionException("agd_incrementunit must contain a value"); int counter = Int32.Parse(autoNumber.Attributes["agd_counter"].ToString()); int incrementUnit = Int32.Parse(autoNumber.Attributes["agd_incrementunit"].ToString()); string prefix = autoNumber.Attributes.Contains("agd_prefix") ? autoNumber.Attributes["agd_prefix"].ToString() : string.Empty; string prefixSeparator = autoNumber.Attributes.Contains("agd_prefixseparator") ? autoNumber.Attributes["agd_prefixseparator"].ToString() : string.Empty; string suffix = autoNumber.Attributes.Contains("agd_suffix") ? autoNumber.Attributes["agd_suffix"].ToString() : string.Empty; string suffixseparator = autoNumber.Attributes.Contains("agd_suffixseparator") ? autoNumber.Attributes["agd_suffixseparator"].ToString() : string.Empty; string numberFormatter = autoNumber.Attributes.Contains("agd_numberformatter") ? autoNumber.Attributes["agd_numberformatter"].ToString() : string.Empty; string fieldToUpdate; if (autoNumber.Attributes.Contains("agd_entityautonumberfield")) fieldToUpdate = autoNumber.Attributes["agd_entityautonumberfield"].ToString(); else throw new InvalidPluginExecutionException("agd_entityautonumberfield should not be emplty"); nextIncrementNumber = BuildAutoNumber(prefix, prefixSeparator, suffix, suffixseparator, counter, incrementUnit, numberFormatter); entity.Attributes[fieldToUpdate] = nextIncrementNumber; service.Update(entity); //increment the autonumber entity //and update it to record the counter autoNumber.Attributes["agd_counter"] = counter + incrementUnit; service.Update(autoNumber); } } catch (Exception ex) { if (mtx != null) { mtx.ReleaseMutex(); mtx = null; } throw new InvalidPluginExecutionException("An error occured in Autonumber plugin", ex); } finally { if (mtx != null) mtx.ReleaseMutex(); } } } private string BuildAutoNumber(string prefix, string prefixSeparator, string suffix, string suffixSeparator, int counter, int incrementUnit, string numberFormatter) { bool hasPrefix = false, hasSuffix = false; string returnNumber = string.Empty; if (!string.IsNullOrEmpty(prefix)) { hasPrefix = true; } if (!string.IsNullOrEmpty(suffix)) { hasSuffix = true; } counter = counter + incrementUnit; returnNumber = (hasPrefix ? prefix + prefixSeparator : "") + counter.ToString(numberFormatter) + (hasSuffix ? suffix + suffixSeparator : ""); return returnNumber; }Some notes about the code above, I've used fetch XML to get the fields from the auto number entity. You can use LINQ as well if you generated strongly typed clasess via CRMSvcUtil tool in the SDK. I am using late bound entities so that it is easier to change later on without the need to regenerate strongly typed classes.
I am also using a mutex, this is there to ensure that concurrent create requests cannot occur simultaneously, it's not the best solution but it works.
The above plugin code will generate the increment number, but before that can happen we have to register the plugin using the plugin registeration tool. We need to register the assembly and the create a step for the entity where we want to run this auto number plugin on. We have to register it on the post-create step for the "Create" event of your custom entity. The registeration tool can be found in the SDK\tools directory.
Thats it! your auto number is ready to go. Notice that this plugin can service multiple entities and can be easily ported to any organisation, also it can be customized from the interface and prefixes and suffixes can be added / changed.
Sunday, November 6, 2011
CRM 2011 opening forms using URL
This is a very neat feature in CRM. Basically all entity forms can be accessed by using main.aspx and some pre definied query string parameters.
So for example your organisation is called constoso you can access any entity form url using the syntax
http://<yourserver>/contoso/main.aspx?etn=<yourEntity>&pagetype=entityrecord
note the two query string parameters etn and pagetype. These are pre defined parameters, this is not something I have come up with.
Here are the list of all possible parameters you can pass (from msdn)
This is a very handy function. You can place button on forms and open up other forms or call it from your web resource such as asp.net or html page or silverlight or even add your custom buttons to the ribbon and open forms with prefilled values.
In my next post I will show you how to populate form values using query string parameters in the url.
The tricky one is populating the lookups.
Happy CRMing!
So for example your organisation is called constoso you can access any entity form url using the syntax
http://<yourserver>/contoso/main.aspx?etn=<yourEntity>&pagetype=entityrecord
note the two query string parameters etn and pagetype. These are pre defined parameters, this is not something I have come up with.
Here are the list of all possible parameters you can pass (from msdn)
- etn -entity logical name
- extraqs -optional parameters that can set values in a form.
- pagetype - one of two values entityrecord or entitylist
- id- optional for forms when you want to open a specific record
- viewid - id for saved query or user query.
- viewtype - 1039 for saved query and 4230 for user query.
This is a very handy function. You can place button on forms and open up other forms or call it from your web resource such as asp.net or html page or silverlight or even add your custom buttons to the ribbon and open forms with prefilled values.
In my next post I will show you how to populate form values using query string parameters in the url.
The tricky one is populating the lookups.
Happy CRMing!
Thursday, November 3, 2011
Getting attibute value in CRM 2011
This is a generic function which will get you value of any attribute on the form.
Only two things you need to remember, for an optionset it will get the text for the selected option set and for a lookup it will get the first value in the lookup. So it will not get you values for thesystem lookups which can have multiple values.
Only two things you need to remember, for an optionset it will get the text for the selected option set and for a lookup it will get the first value in the lookup. So it will not get you values for thesystem lookups which can have multiple values.
function GetAttributeValue(attribute) { var attrib = Xrm.Page.data.entity.attributes.get(attribute); var type = attrib.getAttributeType(); var value; switch (type) { case "boolean": case "decimal": case "double": case "integer": case "memo": case "string": case "money": value = attrib.getValue(); break; case "optionset": value = attrib.getSelectionOption().text; break; case "datetime": value = attrib.getValue().toLocaleString(); break; case "lookup": var lookupArray = attrib.getValue(); value = lookupArray[0].name; break; default: value = null; break; } return value; }Happy CRMing!
Tuesday, May 31, 2011
CRM 2011 List Component does not work
I had a lot of trouble getting the list component to work on my Dev Machine, I eventually managed to fix the problem, so in the hope that this might help someone please try some of the following steps:
- Ensure that CRM is running under a domain account with access to the sharepoint site where you plan to install the CRM List Component, see my other post
- Try creating a new Sharepoint Web application
- Install the List Component.WSP in the Sharepoint site. Go to site settings -> Galleries -> solutions. Activate the solution. This one was hard to solve. Try the following:
- Ensure Sharepoint Foundation Sandboxed code service is running ( found user central admin -> System Settings -> Manage servies on the server.
- In Manage Applications, select your application - select General Settings (ribbon), make sure that Browser file handling is set to permissive.
- reset iis
- if you get the error that user code service is too busy, restart the service and try again.
- Finally and this one was the hardest, edit your hosts file located at system32\etc. add the following entry 127.0.0.1 crl.microsoft.com. I found the soluation here
Thursday, May 19, 2011
The user Id is invalid. at Microsoft.Crm.BusinessEntities
Make sure that the user which is running the application pool for the CRM application is added to the following groups
PrivUserGroup
CRM_WPG
also make sure the account has access to sql server in sysadmin role
PrivUserGroup
CRM_WPG
also make sure the account has access to sql server in sysadmin role
CRM 2011 Error Sandbox Host - Access Denied
Okay for those of you who have stumbled upon this issue when changing Dynamics CRM to run under a custom account then here is hte solution:
Your account should be a domain user account with the following permissions as recommended by Micrsoft:
Domain User membership.
That account must be granted the Logon as service permission in the Local Security Policy.
Folder read and write permission on the \Trace, by default located under \Program Files\Microsoft Dynamics CRM\Trace, and user account %AppData% folders on the local computer.
Read permission to the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM subkey in the Windows Registry.
The service account may need an SPN for the URL used to access the Web site that is associated with it. To set the SPN for the Sandbox Processing Service account, run the following command at a command prompt on the computer where the service is running.
Your account should be a domain user account with the following permissions as recommended by Micrsoft:
- If you add the account as a member of local administrator then that should be sufficient.
SETSPN –a MSCRMSandboxService/<ComputerName> <service account>
If you want to change your Dynamics CRM web app to run under this account then you will need to do some more stuff
The account should be in the sysadmin group of the SQL Server where your crm database is located.
The account should be in the SQLAccessAGroup
Add to CRM_WPG group
Add to PrivUserGroup (this is usually missed)
Hope this helps someone out there.
Subscribe to:
Posts (Atom)