Posts Tagged ‘Plug-Ins’


 
 Both workflows and plug-ins can attach to exactly the same events. Well, plug-ins have available a couple of more events but essentially both work on top of the same event model.Remember also that workflows always run asynchronous and hence, the Asynchronous Processing Service must be running on the server in order to run.

Workflows are more suitable if:

  • you want to achieve simple tasks faster, such as sending an e-mail or creating / updating assigning records. These actions can be set up very quickly with a workflow without any need of writing code.
  • you want to easily scale things to managers (if they were setup for user records), as it is possible to assign records to them.
  • you want to allow an advanced user to make changes to logic. As using the integrated workflow designer is user-friendly, an advanced user would be able to edit an existing workflow and change some rules according to business changes.
  • the logic should be available to be run on demand. I mean, when you are within an entity and navigates to “workflows” option in the left pane, all workflows marked as available to run on demand can be executed making them independent of an event trigger.
  • you want to send emails making use of templates and attaching files.

Workflows also allow running child workflows which may make a lot of sense in some scenarios. Nevertheless, be careful if you need the child workflow results in order to make decisions on your main workflow, as child workflows always run asynchronous, which means that it will trigger the child workflow and continue. If you need your primary workflow to wait until child ends, you will need to write a custom activity.

On the other hand, plug-ins are more suitable if:

  • you need to manipulate data before is saved.
  • you need to make validations before submitting the operation.
  • you want to be able to cancel an operation based on your validations.
  • immediate response to the user is needed.
  • you need retrieve values and/or take actions after operation has been completed (i.e. getting and autogenerated id)

It is important to note that since Dynamics CRM 4, plug-ins can also be configured to run asynchronous (Mode attribute while registering plug-in). Nevertheless, pre-event asynchronous plug-ins are not supported. In this case, you will have to set it up as synchronous mode.

Another important thing about plug-ins is the Deployment option which says if the plug-in is going to be executed on the server and/or Outlook client. If both executions are set up and client goes offline and online, plug-in calls are triggered after synchronization so be prepared in this case to execute your code twice!

Regarding to security:

  • Workflows triggered automatically will run under the security context of the workflow owner. On the contrary, if executed on demand, the security context of the user who executed the workflow will be used.
  • Plug-ins execute under the security context of the CRM Web application pool identity (typically NETWORK SERVICE). As this account typically maps to generic CRM SYSTEM user this typically works fine.

However, within plug-ins you can make use of impersonation to work under the credentials of the user who is making the request. For doing so, you just need to pass True to the CreatCrmService method under the context object.If you need to always impersonate with a specific user, you can do that by passing True as above and setting impersonatinguserid attribute while registering the plug-in.It is important to know that plug-in impersonation does not work offline. The logged on user credentials are always used in this case.

Some other important url for reference are..
http://msdn.microsoft.com/en-us/library/dd393303.aspx
http://blogs.msdn.com/lezamax/archive/2008/04/02/plug-in-or-workflow.aspx.
Hope it helps..

Advertisements

using System;

using System.Collections.Generic;

using System.Text;

using Microsoft.Crm.Sdk;

using Microsoft.Crm.SdkTypeProxy;

using Microsoft.Crm.Sdk.Query;

using System.Web.Services.Protocols;

namespace CRM.Customization.AutoNumber

{

public class AutoNumberPlugIns:IPlugin

{

DynamicEntity entity = null;

int startNumber;

string prefix = string.Empty;

string seperator = string.Empty;

int nextMaxNumber;

string uniqueId = string.Empty;

#region IPlugin Members

public void Execute(IPluginExecutionContext context)

{

if (context.InputParameters.Properties.Contains(“Target”) &&

context.InputParameters.Properties[“Target”] is DynamicEntity)

{

entity = (DynamicEntity)context.InputParameters.Properties[“target”];

ICrmService service = context.CreateCrmService(true);

if (entity.Name ==“new_testentity”)

{

// Find maximum number

int maxNumber = FindMaxNumber(service);

nextMaxNumber=maxNumber + 1;

if (nextMaxNumber > 0)

{

bool isRetrivePrefixSuccess = RetrievePrefixSufixAndStartNumber(service);

if (isRetrivePrefixSuccess)

{

if (nextMaxNumber >= startNumber)

{

string buildAutoNumber = prefix + seperator + nextMaxNumber.ToString();

uniqueId = buildAutoNumber;

StringProperty uId = new StringProperty(“new_uniqueid”, uniqueId);

entity.Properties.Add(uId);

CrmNumber nextNumber = new CrmNumber();

nextNumber.Value = nextMaxNumber;

CrmNumberProperty uniqueNo = new CrmNumberProperty(“new_uniqueno”, nextNumber);

entity.Properties.Add(uniqueNo);

}

}

}

}

}

}

private int FindMaxNumber(ICrmService service)

{

int maxNo=0;

QueryExpression query = new QueryExpression();

query.EntityName = “new_testentity”;

ColumnSet cols = new ColumnSet();

cols.AddColumn(“new_uniqueno”);

query.ColumnSet = cols;

query.AddOrder(“new_uniqueno”, OrderType.Descending);

PagingInfo pageInfo = new PagingInfo();

pageInfo.Count = 1;

pageInfo.PageNumber = 1;

query.PageInfo = pageInfo;

RetrieveMultipleRequest request = new RetrieveMultipleRequest();

request.Query = query;

request.ReturnDynamicEntities = true;

RetrieveMultipleResponse response = (RetrieveMultipleResponse)service.Execute(request);

DynamicEntity testEntity = (DynamicEntity)response.BusinessEntityCollection.BusinessEntities[0];

for (int i = 0; i < testEntity.Properties.Count; i++)

{

if (testEntity.Properties.Contains(“new_uniqueno”))

{

CrmNumber uniqueNumber = (CrmNumber)testEntity.Properties[“new_uniqueno”];

maxNo = uniqueNumber.Value;

}

}

return maxNo;

}

private bool RetrievePrefixSufixAndStartNumber(ICrmService service)

{

try

{

QueryByAttribute query = new QueryByAttribute();

query.EntityName = “new_autonumber”;

ColumnSet cols = new ColumnSet();

cols.AddColumn(“new_startnumber”);

cols.AddColumn(“new_prefix”);

cols.AddColumn(“new_currentnumber”);

cols.AddColumn(“new_separator”);

query.ColumnSet = cols;

query.Attributes = new string[] { “new_entityname” };

query.Values = new object[] { “new_testentity” };

RetrieveMultipleRequest request = new RetrieveMultipleRequest();

request.Query = query;

request.ReturnDynamicEntities = true;

RetrieveMultipleResponse response = (RetrieveMultipleResponse)service.Execute(request);

DynamicEntity dynamicsEntity = (DynamicEntity)response.BusinessEntityCollection.BusinessEntities[0];

if (dynamicsEntity.Properties.Count > 0)

{

// DynamicEntity autoNumberEntity = (DynamicEntity)dynamicsEntity.BusinessEntities[0];

// Extract the fullname from the dynamic entity string fullname;

for (int i = 0; i < dynamicsEntity.Properties.Count; i++)

{

if (dynamicsEntity.Properties.Contains(“new_startnumber”))

{

CrmNumber startNo = (CrmNumber)dynamicsEntity.Properties[“new_startnumber”];

startNumber = startNo.Value;

}

if (dynamicsEntity.Properties.Contains(“new_prefix”))

{

String prefixId = (String)dynamicsEntity.Properties[“new_prefix”];

prefix = prefixId.ToString();

}

if (dynamicsEntity.Properties.Contains(“new_separator”))

{

String seperaterId = (String)dynamicsEntity.Properties[“new_separator”];

seperator = seperaterId.ToString();

}

}

}

return true;

}

catch (SoapException ex)

{

throw ex;

}

}

}

#endregion

}


If we want to execute some code in behalf of another user not the calling user then we have to handle this in PlugIn code. Whatever the user selected during PlugIns registration, it doesn’t matter, code will alwaysexecute on behalf of user mentioned in code.Example Create a Record in CRM on behalf of another user.

 

try

                            {

                                CrmSdk.CrmAuthenticationToken token = new CrmSdk.CrmAuthenticationToken();

                                token.AuthenticationType = 0;

                                token.OrganizationName = OrganizationName;

                              

 

                                // Create CrmService instance

                                CrmSdk.CrmService service= new CrmSdk.CrmService();

                                service.Url = CrmServiceUrl;

                                service.CrmAuthenticationTokenValue = token;

 

                                //execute on behalf of another user

                                service.Credentials = new System.Net.NetworkCredential(“username”,”password”,”domain”);

                                //service.CorrelationTokenValue = correlationToken;

                                //Create Proposal record

                                CrmSdk.new_proposal proposal = new CrmSdk.new_proposal();

                               

                                // set value of Opportunity Lookup in Proposal entity.

                                CrmSdk.Lookup oppLookup = new CrmSdk.Lookup();

                                oppLookup.type = EntityName.opportunity.ToString();

                                oppLookup.Value = new Guid(oppId);

 

                                proposal.new_opportunityid = oppLookup;

                                service.Create(proposal);

                            }

                            catch (SoapException ex)

                            {

                               // show message here.. 

                            }


To retrieve data from CRM Database( FilteredView)…..

SqlConnection connection = new SqlConnection(“DataSource=<dsName>;Initial Catalog=<databaseName> ;Integrated Security=SSPI;”);

string sqlQuery = @” select max(new_uniqueid) from FilteredView” ;

SqlCommand cmd = new SqlCommand(sqlQuery, connection);

connection.Open();

 

/// Write Logic here

 

connection.Close();

 

But this code wasn’t returning any results .It was because callout/plugin run under the context of NT Authority\Network Service.

So we need to use impersonation in this case, for this we can use the following code .

SqlConnection connection = new SqlConnection(“DataSource=<dsName>;Initial Catalog=<databaseName> ;Integrated Security=SSPI; Pooling=false);

string sqlQuery = @” SETUSER ‘domainName\administrator’ select max(new_uniqueid) from FilteredView” ;

SqlCommand cmd = new SqlCommand(sqlQuery, connection);

connection.Open();

 

/// Write Logic here

 

connection.Close();

 

SETUSER To impersonate the admin who has access to the filtered views. More about SETUSER 

http://msdn.microsoft.com/en-us/library/ms186297.aspx

If we are not using Pooling=false in connection string its giving this error.

Event Log Error: The connection has been dropped because the principal that opened it subsequently assumed a new security context, and then tried to reset the connection under its impersonated security context…….