Archive

Archive for the ‘Plug-Ins’ Category

Dataverse | Plugins | ILMerge Alternative | Shared Project

Role of ILMerge in Plugin Development:

If you are familiar with writing Plugins in Dataverse, chances are that you would have used ILMerge to merge the Assemblies.

In a typical Dynamics Plug-in development, we will have following .Net Class Library Projects.

  • Plugins.csproj
  • Plugins.Helper.csproj

When you compile above projects, you get two .dlls (i.e., Plugins.dll and Plugins.Helper.dll). As we can only deploy one .dll to Dataverse, we use ILMerge to merge both Plugins.dll and Plugins.Helper.dll in to one.

Is it recommended to use ILMerge in Plugin development? Answer is No. As per this Microsoft article ILMerge is not supported in Plugins development.

Now whats the alternative? Answer is Shared Projects.

Steps to use Shared Projects in Plug-in Development:

To explain the Shared Projects, I am going to build a simple Plugin project ‘Plugins.csproj’ with ‘PreAccountCreate’ class, which refers a ‘Shared’ Helper project ‘Plugins.Helper’.

  • Create a new C# class library project and add ‘PreAccountCreate’ class as below.
    • You can copy the code I’ve used from here.
  • Now lets add a ‘Shared’ Helper project which our Plugin project would refer.
  • Right click your Solution and click ‘New Project’.
  • Select a Project template of type C# ‘Shared Project’.
  • Give a Name to your ‘Shared Project’.
    • I’ve named it as ‘Plugins.Helper’.
  • ‘Plugins.Helper’ Shared Project, looks as below in the Solution Explorer.
  • Now add a Class file ‘AccountHelper.cs’ to the ‘Shared Project’.
  • I’ve added a simple function ‘GetAccountName()’ which returns ‘Microsoft India’.
  • To use the ‘Shared Project’ in our Plug-in project, right click ‘Plugins’ project and add ‘Reference’.
  • From ‘Shared Projects’, choose your project (i.e., Plugins.Helper in my case).
  • Once you referred the ‘Shared Project’ in your Plugin project, it looks as below.
  • Now its time to call ‘GetAccountName()’ from our ‘PreAccountCreate’ class.
  • Sign the Assembly.
  • Build the Plug-in project and you would get the ‘Plugins.dll’ in bin/Debug folder.
  • Go ahead and deploy the ‘Plugins.dll’ to Dataverse and register step using Plugin Registration Tool.

🙂

Categories: Plug-Ins Tags: , ,

[Dynamics CE] – Strange Plug-in issue due to the usage of global variables

December 26, 2018 Leave a comment

Other day, one of my colleagues reported a plug-in issue which could not be reproduced consistently. All the troubleshooting options (i.e., Plug-in Profiler, Tracing) had been used but was unable to found the root cause.

On close retrospection of code, we found out the usage global variables in Plug-in class file, which made the Plug-in ‘Stateful’.

Due to this, issue could not be reproduced when tested with Plug-in Profiler as it creates single Plug-in instance. Issue will only be reproduced when concurrent users instantiate plug-ins.

You have to make sure to create your Plug-in as ‘Stateless’ to avoid issues during concurrent Plug-in execution.

What is ‘Stateful’ Plug-in?

  • If the Plug-in ‘Execute’ method refer any global variable(s), makes the Plug-in ‘Stateful’.

public class CustomersVirtual : IPlugin{

// ‘context’ variable declared as Global
IPluginExecutionContext context = null;
EntityCollection results = new EntityCollection();
public void Execute(IServiceProvider serviceProvider) {
context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

if (context.MessageName.ToLower() == “retrieve”){
Entity customer = new Entity(“raj_virtualcustomer”);
customer[“raj_customersid”] = Guid.NewGuid();
customer[“raj_name”] = “ABC Corporation”;

//add it to the collection
results.Entities.Add(customer);
}

In the above Plug-in sample, variables ‘context’ and ‘results’ been defined as global and referred in ‘Execute’ method which makes it ‘Stateful’

What is ‘Stateless’ Plug-in:

  • If the Plug-in ‘Execute’ method, has its own local variables and does not refer any global variables make the Plug-in ‘Stateless’.

public class CustomersVirtual : IPlugin{
public void Execute(IServiceProvider serviceProvider){

// ‘context’ variable declared as Local variable
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));

EntityCollection results = new EntityCollection();

if (context.MessageName.ToLower() == “retrieve”)
{
Entity customer = new Entity(“raj_virtualcustomer”);
customer[“raj_customersid”] = Guid.NewGuid();
customer[“raj_name”] = “ABC Corporation”;

//add it to the collection
results.Entities.Add(customer);
}

In this sample, variables ‘context’ and ‘results’ been defined as Local in ‘Execute’ method which makes it ‘Stateless’

Why ‘Stateless’ Plug-in is recommended?

  • As per the design, Dynamics platform caches plug-in class instances and hence the constructor is not called for every invocation of plug-in execution.
  • IPlugins should be stateless is that multiple system threads could execute the same, shared, plug-in instance concurrently.
  • This opens up members of classes that implement IPlugin to potential thread-safety issues which could lead to data inconsistency or performance problems.
  • Read-only, static, and constant members are inherently thread-safe and can also be used reliably within a plug-in class.

How the Dynamics platform Caches Plug-in?

  • For performance reasons, Dynamics DOES NOT dispose of the object after it completes execution.
  • Dynamics caches the object and calls Execute on the same object instance on the next activation of the plug-in.
  • Certain operations, such as changing a plug-in’s registration properties, will trigger a notification to the platform to refresh the cache. In these scenarios, the plug-in will be reinitialized..
  • This means that you need to be very careful when defining class-level variables in the plug-in class as multiple threads can execute the code at one time

Bottom line:

  • Don’t use Global variables in Plug-ins

Refer below articles for more insights:

🙂

Categories: CRM, Plug-Ins Tags: , ,

The server was unable to process the request due to an internal error – plugin registration tool

June 13, 2013 1 comment

The other day my plugin registration tool suddenly stopped connecting to my CRM server and was getting below error

Unhandled Exception: System.ServiceModel.FaultException: The server was unable to process the request due to an internal error

Below steps helped me to resolve the issue

  • Go to “Plugin Registration Tool” physical folder
    • Delete existing “Microsoft.Crm.Sdk.Proxy.dll” and “Microsoft.Xrm.Sdk.dll”

    Plugin registration tool folder

    • Get back the 2 .dlls either from CRM SDK or from “C:\Program Files\Microsoft Dynamics CRM\Server\bin” path of CRM server machine
    • Paste them in “Plugin Registration Tool” folder
  • Reset the IIS
  • Open the Plugin Registration Tool and try to connect to the server

🙂

Associate/Disassociate plugin messages in CRM

April 17, 2013 13 comments

In CRM, the Associate or Disassociate event happens

  • If you have a N:N relationship between two entities and when you try to associate or disassociate records either from Associated view or Sub grid.

Entity Associate View

Entity Associate View

In Plugins, the Associate & Disassociate messages behave little different than other messages.

  • When you register a plugin on Associate message, you have to leave “Primary and Secondary” entities as ‘none’.

Associate Plugin Step

Associate Plugin Step

  • Since we don’t provide entity names, the registered Plug-in step triggers on all “Associate” operations, so we have to check few conditions to let the “Association” trigger happen only between intended entities.

You can use the below code template for Associate or Disassociate plugins

            EntityReference targetEntity = null;

            string relationshipName = string.Empty;

            EntityReferenceCollection relatedEntities = null;

            EntityReference relatedEntity = null;

            if (context.MessageName == “Associate”) {

                // Get the “Relationship” Key from context

                if (context.InputParameters.Contains(“Relationship”)) {

                    relationshipName = context.InputParameters[“Relationship”].ToString();

                }                                   

                // Check the “Relationship Name” with your intended one

                if (relationshipName != “{YOUR RELATION NAME}”) {

                    return;

                } 

                // Get Entity 1 reference from “Target” Key from context

                if (context.InputParameters.Contains(“Target”) && context.InputParameters[“Target”] is EntityReference)  {

                    targetEntity = (EntityReference)context.InputParameters[“Target”];

                }                      

                // Get Entity 2 reference from ” RelatedEntities” Key from context

                if (context.InputParameters.Contains(“RelatedEntities”) && context.InputParameters[“RelatedEntities”] is EntityReferenceCollection) {

                    relatedEntities = context.InputParameters[“RelatedEntities”] as EntityReferenceCollection;

                    relatedEntity = relatedEntities[0];

                }

            }

🙂

Passing configuration data using Plug-in registration tool (Secured vs. Unsecured)

October 22, 2012 1 comment
  • CRM plugin registration tool contain “Unsecure & Secure” configuration sections while registering a “Step”
  • We can pass configuration data and can be used in the plug-in logic.

Plug-in registration - Secured and Unsecured sections

Plug-in registration – Secured and Unsecured sections

Secured vs. Unsecured

Below are key differentiations between Secured and Unsecured data configuration

  • Access
    • Data passed through “Unsecure” section is PUBLIC (i.e., It can be read by any user in CRM).
    • Only users with “System Administrator” role have access to the data passed through “Secure” configuration section
  • Storage
    • “Unsecure” config data will be stored along with the Plugin ‘Step’ registration information (i.e., In SdkMessageProcessingStep entity)
    • “Secure” config data will be stored in a separate entity named “SdkMessageProcessingStepSecureConfig
      • Only “System Administrator” has Read access on this entity, hence only users with ‘Sys Admin’ role can access this data
    • Both “Secured & Unsecured” configuration data stored as “Plain text” in DB
  • Outlook Sync
    • “Unsecured” configuration data is downloaded to the user’s computer when they go offline making it Unsecure
    • “Secured” configuration data is NOT downloaded to User’s Computer when they go Offline

How to read Configuration data in Plug-in

In our plug-in class, we can define a constructor that passes two parameters (i.e., unsecure configuration and secure configuration)

public class AccountCreateHandler: IPlugin{

public AccountCreateHandler(string unsecure, string secure){

// Do something with the parameter strings.

}

public void Execute(IPluginExecutionContext context){

// Do something here.

}

}

Note :- If you want to read “Secure” configuration in the plug-in code, either change the user context in plugin registration as “CRM administrator ” or Impersonate to “CRM Administrator” role user in the code

🙂

Add or Remove users to Team programmatically in CRM 2011

October 11, 2012 Leave a comment

In CRM,

  • Team is a group of users.
  • Each team must be associated with only one Business Unit (BU).
  • A team can include users from any business unit, not only the BU with which the team is associated.
  • Users can be associated with more than one team.  
  • You can get the members of Team from “TeamMembership” view

TeamMemberShip view

TeamMemberShip view

We can Add or Remove users from Team using below requests programmatically

  • AddMembersTeamRequest
  • RemoveMembersTeamRequest

Below is the code to add User(s) to a Team

AddMembersTeamRequest memberTeamRequest = new AddMembersTeamRequest();

memberTeamRequest.TeamId = teamId; //Guid of Team

// Prepare Member (i.e.,User) array to add to Team

Guid[] arrMembers = new Guid[2];

arrMembers[0] = userId1; // GUID of User 1

arrMembers[1] = userId2; // GUID of User 2

// Set MemberIds property with “arrMembers”

memberTeamRequest.MemberIds = arrMembers;

// Execute the Request

AddMembersTeamResponse response = (AddMembersTeamResponse)service.Execute(memberTeamRequest);

Below is the code to remove User(s) from a Team

RemoveMembersTeamRequest removeMemberTeamRequest = new RemoveMembersTeamRequest();

removeMemberTeamRequest.TeamId = teamId; //Guid of Team

// Prepare Member (i.e.,User) array to remove from Team

Guid[] arrMembers = new Guid[2];

arrMembers[0] = userId1; // GUID of User 1

arrMembers[1] = userId2; // GUID of User 2

// Set MemberIds property with “arrMembers”

removeMemberTeamRequest.MemberIds = arrMembers;

// Execute the Request

RemoveMembersTeamResponse response = (RemoveMembersTeamResponse)service.Execute(removeMemberTeamRequest); 

🙂

UserId & InitiatingUserId properties in Plugin of CRM

August 19, 2012 3 comments

In CRM plugin, “IExecutionContext” contains 2 properties

  • UserId
    • Gets the GUID of the user for whom the plug-in invokes “on behalf of”.
  • InitiatingUserId
    • Gets the GUID of the user under which the current pipeline is executing.

Consider a scenario

  • You have a user “RAJ” with “Sales Person” role with only “User Level” “Read” privilege on ‘Contact’
  • You have a plugin on Post Deletion of ‘Contact’ with name “PostContactDelete”
  • Assume in one particular scenario user “RAJ” should be able to delete a ‘Contact’
  • So you can run the “PostContactDelete” plugin in the user with “SystemAdministrator” role
    • (i.e., Set “Run in User’s Context” to User with admin role; In sample screen shot below I chosen my admin user whose name is  ‘CRM WaSu1)

Run in User Context

Run in User Context

  • When User “RAJ” logs in and try to delete ‘Contact’ the plug-in “PostContactDelete” fires. When you debug
    • IExecutionContext.UserId = GUID of  SystemAdministrator (i.e., OnBehalfOf User ‘RAJ’)
    • IExecutionContext. InitiatingUserId =GUID of  RAJ   (i.e., Actual User)

🙂

Configure Tracing and view trace log files in CRM

August 16, 2012 2 comments

Tracing is important in CRM especially when your plugin throw exceptions in Test or Production environments

We can configure and view the trace log file easily using “Crm Trace Log Viewer” tool and it work for both CRM 4.0 and 2011

Download

Once you download the tool

Configure Tracing

  • Double click on the “Crm Trace Log Viewer” Application

Crm Trace Log Viewer Application

Crm Trace Log Viewer Application

  • Configure Tracing by go to Tools –> Configure Tracing

Configure Tracing

Configure Tracing

  • Provide your CRM server machine IP or Name and click “Ok”

CRM server IP or Machine Name

CRM server IP or Machine Name

  • Provide the details as given below
    • Directory – Path where you want your log files to generate
    • Level & Schedule

Configuring Trace

Configuring Trace

View Trace Log Files

  • Once you run the CRM application log files get generate in the path which we provided

Trace Log File

Trace Log File

  • To view the logs, Go to File –> Open Log

Open the trace log

Open the trace log

  • Once you open the file, you can view the details in “Details” pane

Exception Details

Exception Details

Note – Don’t forget to disable the tracing after you are done with debugging.

  • To disable tracing, open the tool and uncheck the option “Trace Enabled” (Refer image #4)

Outlook Client Tracing

  • Using CRM 2011 “Diagnostics Tool”, we can enable/disable tracing in outlook client
  • Here is the link

🙂

Plug-ins in CRM 2011 – Useful points

Plug-in stages

  • Pre validation
    • Registered Plug-in run before the form is validated
    • Useful if you want to implement business logic before the actual validation starts.  i.e., Changes made in plug-in won’t be saved if the validation of the main system plugins complain because the changes are outside the database transaction.
    • Ex – Some “delete” plug-ins. Deletion cascades happen prior to pre-operation, therefore if you need any information about the child records, the delete plugin must be pre-validation.
  • Pre -operation
    • After validation and before the values are saved to the database
  • Post operation
    • Plugin will run after the values have been inserted/changed on the database

Database Transactions in Plug-Ins

  • Plug-ins may or may not execute within the database transaction
  • You can check if the plug-in is executing in-transaction by reading the ‘IsInTransaction‘ property of IPluginExecutionContext
  • Stages 20 and 40 are part of the database transaction while stage 10 and 50 may be part of the transaction
  • If plugin throws an exception, every action done during the transaction will be rollback

Few more Points

  • Whether a plug-in executes synchronously or asynchronously, there is a 2 minute time limit imposed on the execution of a (message) request.
  • If the execution of your plug-in logic exceeds the time limit, a Timeout exception is thrown
  • If a plug-in needs more processing time than the 2 minute time limit, consider using a workflow or other background process
  • ‘Pre-operation’ operations that CRM will do will not be carried out in pre-validation stage.
    • If you are deleting a record that has many-to-many relationship records with another entity; these relationship records will still be available in pre-validation stage, but not in pre-operation stage.
  • “Target” entity (i.e., pluginContext.InputParameters[“Target”])
    • It’s the entity on which plug-in registered
    • It only contains “dirty” attributes. if you convert to early bound, the value of the unchanged attribute will be null

Useful MSDN article :)

Adding file as “Attachment” to activities programmatically in plug-in

Hi,

We can add file as attachment and associate to the CRM objects i.e., Email, Task etc…

Email with attachment

Email with .pdf file as attachment

The entity that store attachment information is “activitymimeattachment”

Below is the code snippet to add “.pdf” file as attachment to the Email activity

Entity attachment = new Entity(“activitymimeattachment”);

attachment[“subject”] = “My Subject”;

string fileName = “Sample.pdf”;

attachment[“filename”] = fileName;

byte[] fileStream= = new byte[] { }; //Set file stream bytes

attachment[“body”] = Convert.ToBase64String(fileStream);

attachment[“mimetype”] = “text/plain”;

attachment[“attachmentnumber”] = 1;

Guid emailId; //GUID of Email activity

attachment[“objectid”] = new EntityReference(“email”, emailId);

attachment[“objecttypecode”] = “email”;

IOrganizationService service;

service.Create(attachment);

Note :-

  • In the above code the property “objecttypecode” is slight misleading, as it expects “EntityLogicalName” rather than “EntityTypeCode”
  • So make sure to set  attachment[“objecttypecode”] = EntityLogicalName (i.e., email)

🙂