You have to use a plugin to execute custom .Net code.
A plugin lets me execute my own custom .net code by implementing the Microsoft.Xrm.Sdk.IPlugin interface, then registering the .Net assembly with CRM. Only one method is allowed - Execute().
How is it executed?
Each entity has certain events that execute during operation. The Execute() method is called during those events.
How does CRM know about the .Net class?
You register the compiled assembly using the Plugin Registration Tool that comes with the CRM SDK. Assemblies are stored in the GAC, on Disk, or in the database. You basically pick the entity and pick the event. Then you run it. When the event fires for that entity, the .Net code executes.
Event pipleline for entities
Events can be registered based on a singular event; ie. pre or post operation of a create event of an entity.
- Pre-validation (stage 10): execution occurs outside the context of a DB transaction (transaction-enabled)
- Pre-operation (stage 20): execution occurs inside the transaction
- Main operation (stage 30) - cannot register a plugin operation in here.
- Post-operation (stage 40) - execution still occurs inside the transaction
Execution options
- Synchronous - causes the execution of the XRM app to pause during execution
- Asynchronous - does it in a different thread
- The sandbox provides isolation of custom plugins
- Plugins can be executed in offline/online/both modes.To determine the current state, use %ExecutionContext%.IsExecutingOffline
- Note - make sure the plugin is idempotent (if it executes twice, then it doesnt matter)
Writing a plugin
- Implement IPlugin, using Unsecure or Secure config.
- To get the context, use the IPluginExecutionContext, which is a late-bound Entity.
- To transform to an early-bound type (or strongly typed entity) use myEntity.ToEntity<Customer>()
- Note that this is STATELESS - all data is lost between calls to the server.
- To assist with resulting concurrency issues, use Pre/Post event images. This is a snapshot of the entity at the time of execution of the plugin
Transaction support
- If a plugin that was registered OUTSIDE transaction, but executes as a nested transaction inside another transactional parent, will become transactional itself. So if there is an error, the whole transaction will roll back.
- Uncaught pre- and post-operations exceptions will cause a rollback to the local CRM transaction. (no distributed transaction support)
CRM 2011 Sandbox
- Provides a secure environment for plugins (partial trust)
- File system, registry and database is unavailable.
- Reflection is disabled to prevent access to private members
- This is a separate service, one per machine (load balanced with multiple machines).
- New worker process for each org monitors resource usage of each org.
Create a plugin using Entity Framework to write to a database
- Use Visual studio to create a c# class library. Call it PluginTest:
- Create a class called JamesPluginTest.cs
- Add an entity framework object to connect to a database and import the table you want to write to. In this case, the database is called GlobalLookups, and the table is called Country.
- Add the following 2 references to the class library. These are part of the sdk and reside on my PC at C:\CRM\sdk\bin.
- C:\CRM\sdk\bin\microsoft.xrm.sdk.dll
- C:\CRM\sdk\bin\microsoft.crm.sdk.proxy.dll
- Note that the app.config file cannot be shipped with the plugin. One way to bypass this is to override the usage of the .config, which contains the connection string, and use a hard coded connection string when instantiating the Entity Framework object.
- The class must implement IPlugin, with the associated Execute() method implementation
- This is the entire code of the plugin:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xrm.Sdk;
namespace PluginTest
{
public class JamesPluginTest:IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
GlobalLookupsEntities gle;
const string
connectionString = "metadata=res://*/GlobalLookups.csdl|res://*/GlobalLookups.ssdl|res://*/GlobalLookups.msl;provider=System.Data.SqlClient;provider
connection string='data source=apptestcs.develop.fcbt;initial
catalog=GlobalLookups;integrated
security=True;multipleactiveresultsets=True;App=EntityFramework'";
System.Data.EntityClient.EntityConnection
c = new System.Data.EntityClient.EntityConnection(connectionString);
gle = new GlobalLookupsEntities(c);
gle.AddToCountries(new Country
{
Name = "nambabwe",
Printable_name = "Nambabwe",
Iso = new Random().Next().ToString().Substring(0,
2)
});
gle.SaveChanges();
}
}
}
Note that this connectionstring uses integrated security - which means it operates on the database using the credentials of the executor. In this case, the executor is the CRM service, which runs under an identity in IIS. That same identity must have write permissions on the database.
Locating the identity of the CRM app |
Adding the CRM identity to the database |
Now compile the library and note the location of the compiled DLL. Next step is to plut it into the CRM pipeline
Registration of plugins
Each ORG has its own plugin assembly. To run a sandbox plugin, you have to have Create/Update/Delete privileges for the PluginAssembly and PluginType entities. These statusses can be assigned to any user.
The Plugin Registration Tool
- In order to register a plugin in CRM2011 you can be a Deployment Administrator or an Administrator on the CRM Server box. In CRM4 you had to be a Deployment Administrator to register a plugin.
- Open CRM and get the location of the Discovery service. This is under Settings/Customizations/Developer Resources/Service Endpoints/Discovery Service:
- Note that the domain is missing in the url - when using it, I have to add it to the URL otherwise the registration tool won't work. i.e. http://crmdevmt.develop.fcbt:5555/XRMServices/2011/Discovery.svc
- Using Visual Studio open the Plugin Registration Solution located at C:\CRM\sdk\tools\pluginregistration\
- In my case we have a CRM server, and it is on a different domain to my development environment, so note the following:
- Compile and run. Click Create New Connection to add a reference to your CRM environment:
- Click Connect to save.
- When the tree opens, once again modify the CRMService URL to reflect the correct domain, then click Connect.
- A List of your Orgs will appear. Click on the one you want to work with, make sure the URL is correct, then click Register/ New Assembly:
- Navigate to the DLL upi want to register and click Register Selected Plugins:
Navigate to the required DLL and click Register Selected Plugins
You may get an error like "Assembly must be registered in isolation". If this happens, read the following article http://msdn.microsoft.com/en-us/library/gg309620.aspx and note the following:
Security RestrictionsThere is a security restriction that enables only privileged users to register plug-ins. For plug-ins that are not registered in isolation, the system user account under which the plug-in is being registered must exist in the Deployment Administrators group of Deployment Manager. Only the System Administrator user account or any user account included in the Deployment Administrators group can run Deployment Manager.
Also, make sure that you register with the same domain credentials.Now expand the Registered assembly, right-click the plugin you want to register then click Register New Step. This is where you hook up the Execute() method with an event on an Entity:At this point you you click on the Message textbox then type Update. (This is a list of available options. The ones you will probably use most are highlighted )AddItemExecuteByIdRetrieveAddListMembersExportRetrieveExchangeRateAddMemberExportAllRetrieveFilteredFormsAddMembersExportCompressedRetrieveMultipleAddPrivilegesExportCompressedAllRetrievePersonalWallAddProductToKitGrantAccessRetrievePrincipalAccessAddRecurrenceHandleRetrieveRecordWallAddToQueueImportRetrieveSharedPrincipalsAndAccessAssignImportAllRetrieveUnpublishedAssignUserRolesImportCompressedAllRetrieveUnpublishedMultipleAssociateImportCompressedWithProgressRevokeAccessBackgroundSendImportWithProgressRouteBookLockInvoicePricingSendCancelLockSalesOrderPricingSendFromTemplateCheckIncomingLoseSetRelatedCheckPromoteMergeSetStateCloneModifyAccessSetStateDynamicEntityClosePublishTriggerServiceEndpointCheckCopyDynamicListToStaticPublishAllUnlockInvoicePricingCopySystemFormQualifyLeadUnlockSalesOrderPricingCreateRecalculateUpdateCreateExceptionRemoveItemValidateRecurrenceRuleCreateInstanceRemoveMemberWinDeleteRemoveMembersDeleteOpenInstancesRemovePrivilegeDeliverIncomingRemoveProductFromKitDeliverPromoteRemoveRelatedDetachFromQueueRemoveUserRolesDisassociateReplacePrivilegesExecuteRescheduleFill in the values and press Register New Step to save:
No comments:
Post a Comment