Tuesday, March 12, 2013

How to execute custom .Net code plugin with CRM 2011

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 ) 
    AddItem
     ExecuteById
     Retrieve
     AddListMembers
     Export
     RetrieveExchangeRate
     AddMember
     ExportAll
     RetrieveFilteredForms
     AddMembers
     ExportCompressed
     RetrieveMultiple
     AddPrivileges
     ExportCompressedAll
     RetrievePersonalWall
     AddProductToKit
     GrantAccess
     RetrievePrincipalAccess
     AddRecurrence
     Handle
     RetrieveRecordWall
     AddToQueue
     Import
     RetrieveSharedPrincipalsAndAccess
     Assign
     ImportAll
     RetrieveUnpublished
     AssignUserRoles
     ImportCompressedAll
     RetrieveUnpublishedMultiple
     Associate
     ImportCompressedWithProgress
     RevokeAccess
     BackgroundSend
     ImportWithProgress
     Route
     Book
     LockInvoicePricing
     Send
     Cancel
     LockSalesOrderPricing
     SendFromTemplate
     CheckIncoming
     Lose
     SetRelated
     CheckPromote
     Merge
     SetState
     Clone
     ModifyAccess
     SetStateDynamicEntity
     Close
     Publish
     TriggerServiceEndpointCheck
     CopyDynamicListToStatic
     PublishAll
     UnlockInvoicePricing
     CopySystemForm
     QualifyLead
     UnlockSalesOrderPricing
     Create
     Recalculate
     Update
     CreateException
     RemoveItem
     ValidateRecurrenceRule
     CreateInstance
     RemoveMember
     Win
     Delete
     RemoveMembers
     DeleteOpenInstances
     RemovePrivilege
     DeliverIncoming
     RemoveProductFromKit
     DeliverPromote
     RemoveRelated
     DetachFromQueue
     RemoveUserRoles
     Disassociate
     ReplacePrivileges
     Execute
     Reschedule

    Fill in the values and press Register New Step to save:


Tuesday, March 5, 2013

Loan IQ Function Syntax

The purpose of this document is to provide a breakdown of the structure of MySis LoanIQ functions. This one does an extraction of Bills using the XML Inquirey API function
<!--[if gte mso 9]> James Cooke James Cooke 2 50 2013-03-05T13:28:00Z 2013-03-05T13:28:00Z 2 475 2710 Farm Credit Bank of Texas 22 6 3179 14.00 <![endif]
declare function BillTest(){
       <FacilitySection>
{ for $fac in /Facility[LIQ.XQS.EQUAL(@id,';P9KH7WQ')] return
                     <facilityId>{$fac/fac/id}</facilityId>
                     <facilityName>{$fac/fac/name}</facilityName>
                     <facilityNum>{$fac/fac/controlNumber}</facilityNum>
                     <type>{$fac/fac/type}</type>
                     <displayStatus>{$fac/fac/displayStatus}</displayStatus>
                     { for $ost in /Outstanding[LIQ.XQS.EQUAL(@objectStateCode,'LRELS')][LIQ.XQS.EQUAL(@facilityId,$fac/fac/id)] return
                            <OutstandingSection>
                                  <ostId>{$ost/ost/id}</ostId>
                                  <alias>{$ost/ost/alias}</alias>
                                  <currency>{$ost/ost/currency}</currency>
                                  <type>{$ost/ost/outstandingType}</type>
                                  <borrowerId>{$ost/ost/borrowerId}</borrowerId>
                                  <balance>{$ost/ost/currentAmount}</balance>
                                   {BillInterest($ost/ost/id)}
                                  {BillPrincipal($ost/ost/id)}
                            </OutstandingSection>
                     }
                     { for $fee in /AccruedFee[LIQ.XQS.EQUAL(@objectStateCode,'RELSD')][LIQ.XQS.EQUAL(@facilityId,$fac/fac/id)] return
                     <CommitmentFeeSection>
                            <feeId>{$fee/fee/id}</feeId>     
                            <currency>{$fee/fee/currency}</currency>
                            <type>{$fee/fee/type}</type>
                            <borrowerId>{$fee/fee/borrower}</borrowerId>
                            <balance>{$fee/fee/balanceAmount}</balance>
                            <rate>{$fee/fee/currentRate}</rate>
                            {BillFee($fee/fee/id)}
                     </CommitmentFeeSection>
                     }
}
       </FacilitySection>
};
declare function BillInterest($ostId){
<InterestCycle>
{ for $int in AccrualCycle[LIQ.XQS.EQUAL(@ownerId,$ostId)] return
  <InterestRecord>
       startDate{$intaccstartDate}startDate
       endDate{$intaccendDate}endDate
       dueDate{$intaccdueDate}dueDate
       adjustedDueDate{$intaccadjustedDueDate}adjustedDueDate
       cycleDue{$intacccycleDue}cycleDue
       adjustedCycleDue{$intaccadjustedCycleDueThruLastNight}adjustedCycleDue
       cycleBilledAmount{$intacccycleBilledAmount}cycleBilledAmount
  <InterestRecord/>
}
</InterestCycle>
};

declare function BillPrincipal($ostId){
<Principal Section>
{ for $sci in /LoanPaymentSchedule[LIQ.XQS.EQUAL(@ownerId,$ostId)]/LoanPaymentScheduleItem return
  <Principal Record>
    <effectiveDate>{$sci/sci/effectiveDate}</effectiveDate>
    <principalDue>{$sci/sci/principal}</principalDue>
    <principalPaid>{$sci/sci/principalPaid}</principalPaid>
    <principalBilledAmount>{$sci/sci/principalBilledAmount}</principalBilledAmount>
  </Principal Record>
}
</Principal Section>
};
declare function BillFee($feeId){
<FeeCycle>
{ for $acc in /AccrualCycle[LIQ.XQS.EQUAL(@ownerId,$feeId)] return
       <startDate>{$acc/acc/startDate}</startDate>
       <endDate>{$acc/acc/endDate}</endDate>
       <dueDate>{$acc/acc/dueDate}</dueDate>
       <adjustedDueDate>{$acc/acc/adjustedDueDate}</adjustedDueDate>
       <cycleDue>{$acc/acc/cycleDue}</cycleDue>
       <adjustedCycleDue>{$acc/acc/adjustedCycleDueThruLastNight}</adjustedCycleDue>
       <cycleBilledAmount>{$acc/acc/cycleBilledAmount}</cycleBilledAmount>
}
</FeeCycle>
};
For-loop syntax
<mySectionTag>
  {for $myCustomVariableName in LIQObject[LIQComparFunc(targetVar, myVar)] return
    <WhatIWantToCallThisValue>$myCustomVariableName/LIQObjectPrefix/LIQColumnName</WhatIWantToCallThisValue>
  }
</mySectionTag>
Function Declaration syntax
declare function MyFunc($myVariable)}
<MyXMLTag>
//my stuff goes in here
</MyXMLTag>

Results
<FacilitySection>
<facilityId>;P9KH7WQ</facilityId>
                        <facilityName>$7MM TERM</facilityName>
                        <facilityNum>00000715</facilityNum>
                        <type>TAM</type>
                        <displayStatus>Active</displayStatus>
                        <OutstandingSection>
                        <ostId>K(9KRRWM</ostId>
                        <alias>89793502</alias>
                        <currency>USD</currency>
                        <type>LOAN</type>
                        <borrowerId>;P9KH7KO</borrowerId>
                        <balance>4312769.07</balance>
                        <InterestCycle>
<InterestRecord>
                        <startDate>2011-08-01</startDate>
                        <endDate>2011-10-31</endDate>
                        <dueDate>2011-11-01</dueDate>
                        <adjustedDueDate>2011-11-01</adjustedDueDate>
                        <cycleDue>0</cycleDue>
                        <adjustedCycleDue>0</adjustedCycleDue>
                        <cycleBilledAmount>0</cycleBilledAmount>
</InterestRecord>
<InterestRecord>
                        <startDate>2011-11-01</startDate>
                        <endDate>2012-01-31</endDate>
                        <dueDate>2012-02-01</dueDate>
                        <adjustedDueDate>2012-02-01</adjustedDueDate>
                        <cycleDue>0</cycleDue>
                        <adjustedCycleDue>0</adjustedCycleDue>
                        <cycleBilledAmount>0</cycleBilledAmount>
</InterestRecord>
<InterestRecord>
                        <startDate>2012-02-01</startDate>
                        <endDate>2012-04-30</endDate>
                        <dueDate>2012-05-01</dueDate>
                        <adjustedDueDate>2012-05-01</adjustedDueDate>
                        <cycleDue>0</cycleDue>
                        <adjustedCycleDue>0</adjustedCycleDue>
                        <cycleBilledAmount>0</cycleBilledAmount>
</InterestRecord>
<InterestRecord>
                        <startDate>2012-05-01</startDate>
                        <endDate>2012-07-31</endDate>
                        <dueDate>2012-08-01</dueDate>
                        <adjustedDueDate>2012-08-01</adjustedDueDate>
                        <cycleDue>0</cycleDue>
                        <adjustedCycleDue>0</adjustedCycleDue>
                        <cycleBilledAmount>0</cycleBilledAmount>
</InterestRecord>
<InterestRecord>
                        <startDate>2012-08-01</startDate>
                        <endDate>2012-10-31</endDate>
                        <dueDate>2012-11-01</dueDate>
                        <adjustedDueDate>2013-02-01</adjustedDueDate>
                        <cycleDue>53.83</cycleDue>
                        <adjustedCycleDue>53.83</adjustedCycleDue>
                        <cycleBilledAmount>0</cycleBilledAmount>
</InterestRecord>
<InterestRecord>
                        <startDate>2011-05-06</startDate>
                        <endDate>2011-07-31</endDate>
                        <dueDate>2011-08-01</dueDate>
                        <adjustedDueDate>2011-08-01</adjustedDueDate>
                        <cycleDue>0</cycleDue>
                        <adjustedCycleDue>0</adjustedCycleDue>
                        <cycleBilledAmount>0</cycleBilledAmount>
</InterestRecord>
<InterestRecord>
                        <startDate>2012-11-01</startDate>
                        <endDate>2013-01-31</endDate>
                        <dueDate>2013-02-01</dueDate>
                        <adjustedDueDate>2013-02-01</adjustedDueDate>
                        <cycleDue>12020.65</cycleDue>
                        <adjustedCycleDue>12020.65</adjustedCycleDue>
                        <cycleBilledAmount>0</cycleBilledAmount>
</InterestRecord>
</InterestCycle>

                        <PrincipalSection>
<PrincipalRecord>
<effectiveDate>2013-08-01</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2013-11-01</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2013-02-01</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2013-05-01</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2014-02-03</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2014-05-01</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2014-08-01</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2015-11-02</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2016-02-01</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2016-05-02</effectiveDate>
<principalDue>-3312407.5</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2014-11-03</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2015-02-02</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2015-05-01</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
<PrincipalRecord>
<effectiveDate>2015-08-03</effectiveDate>
<principalDue>-76950.89</principalDue>
<principalPaid>0</principalPaid>
<principalBilledAmount>0</principalBilledAmount>
</PrincipalRecord>
</PrincipalSection>

                        </OutstandingSection>
                       
                        </FacilitySection>