Plug-in registered on Power Apps
Tutorial: Write and register a plug-in
Effective November 2020:
- Common Data Service has been renamed to Microsoft Dataverse.Learn more
- Some terminology in Microsoft Dataverse has been updated. For example,entityis nowtableandfieldis nowcolumn.Learn more
This article will be updated soon to reflect the latest terminology.
This tutorial is the first in a series that will show you how to work with plug-ins. This tutorial is a pre-requisite for the following tutorials:
For detailed explanation of supporting concepts and technical details see:
Goal
Create an asynchronous plug-in registered on the Create message of the account entity. The plug-in will create a task activity that will remind the creator of the account to follow up one week later.
Note
This goal can be easily achieved using a workflow without writing code. We are using this simple example so that we can focus on the process of creating and deploying a plug-in.
Prerequisites
- Administrator level access to a Microsoft Dataverse environment
- A model-driven app that includes the account and task entities.
- If you don't have a model-driven app that includes these, seeBuild your first model-driven app from scratchfor steps to make one in just a few minutes.
- Visual Studio 2017
- Knowledge of the Visual C# programming language
- Download the Plug-in Registration tool.
- Information about downloading the Plug-in registration tool is at :Download tools from NuGet. That topic includes instructions to use a PowerShell script to download the latest tools from NuGet.
Create a plug-in project
You need to use Visual Studio to write a plug-in. Use these steps to write a basic plug-in. Alternately, you can find the complete plug-in solution files here:Sample: Create a basic plug-in.
Create a Visual Studio Project for the Plug-in
-
Open Visual Studio 2017 and open a newClass Library (.NET Framework)project using.NET Framework 4.6.2
The name used for the project will be the name of the assembly. This tutorial uses the name
BasicPlugin
. -
InSolution Explorer, right-click the project and selectManage NuGet Packages…from the context menu.
-
SelectBrowseand search for
Microsoft.CrmSdk.CoreAssemblies
and install the latest version. -
You must selectI Acceptin theLicense Acceptancedialog.
Note
Adding the
Microsoft.CrmSdk.CoreAssemblies
NuGet package will include these assemblies in the build folder for your assembly, but you will not upload these assemblies with the assembly that includes your logic. These assemblies are already present in the sandbox runtime.Do not include any other NuGet packages or assemblies to the build folder of your project. You cannot include these assemblies when you register the assembly with your logic. You cannot assume that the assemblies other than those included in the
Microsoft.CrmSdk.CoreAssemblies
NuGet package will be present and compatible with your code. -
InSolution Explorer, right-click the
Class1.cs
file and chooseRenamein the context menu. -
Rename the
Class1.cs
file toFollowupPlugin.cs
. -
When prompted, allow Visual Studio to re-name the class to match the file name.
Edit the Class file to enable a plug-in
-
Add the following
C#using
statements to the top of theFollowupPlugin.cs
file:using System.ServiceModel; using Microsoft.Xrm.Sdk;
-
Implement theIPluginInterface by editing the class.
Note
If you just type
C#: IPlugin
after the class name, Visual Studio will auto-suggest implementing a stub for theExecuteMethod.public class FollowupPlugin : IPlugin { public void Execute(IServiceProvider serviceProvider) { throw new NotImplementedException(); } }
-
Replace the contents of the
Execute
method with the following code:
// Obtain the tracing service
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
// Obtain the organization service reference which you will need for
// web service calls.
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
// Plug-in business logic goes here.
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occurred in FollowUpPlugin.", ex);
}
catch (Exception ex)
{
tracingService.Trace("FollowUpPlugin: {0}", ex.ToString());
throw;
}
}
About the code
- TheITracingServiceenables writing to the tracing log. You can see an example in the final catch block. More information:Use Tracing
- TheIPluginExecutionContextprovides access to the context for the event that executed the plugin. More information:Understand the execution context.
- The code verifies that the contextInputParametersincludes the expected parameters for theCreateRequestthat this plug-in will be registered for. If theTargetproperty is present, theEntitythat was passed to the request will be available.
- TheIOrganizationServiceFactoryinterface provides access to a service variable that implements theIOrganizationServiceinterface which provides the methods you will use to interact with the service to create the task.
Add business logic
The plug-in will create a task activity that will remind the creator of the account to follow up one week later.
Add the following code to the try block. Replace the comment:// Plug-in business logic goes here
. with the following:
// Create a task activity to follow up with the account customer in 7 days.
Entity followup = new Entity("task");
followup["subject"] = "Send e-mail to the new customer.";
followup["description"] =
"Follow up with the customer. Check if there are any new issues that need resolution.";
followup["scheduledstart"] = DateTime.Now.AddDays(7);
followup["scheduledend"] = DateTime.Now.AddDays(7);
followup["category"] = context.PrimaryEntityName;
// Refer to the account in the task activity.
if (context.OutputParameters.Contains("id"))
{
Guid regardingobjectid = new Guid(context.OutputParameters["id"].ToString());
string regardingobjectidType = "account";
followup["regardingobjectid"] =
new EntityReference(regardingobjectidType, regardingobjectid);
}
// Create the task in Microsoft Dynamics CRM.
tracingService.Trace("FollowupPlugin: Creating the task activity.");
service.Create(followup);
About the code
- This code uses the late-bound style to create a task and associate it with the account being created. More information:Create entities using the Organization Service
- Early bound classes can be used, but this requires generating the classes for the entities and including the file defining those classes with the assembly project. This is mostly a personal preference, so those steps have been left out of this tutorial for brevity. More information:Late-bound and Early-bound programming using the Organization service
- TheIdof the account being created is found in the contextOutputParametersand set as the
regardingobjectid
lookup attribute for the task.
Build plug-in
In Visual Studio, pressF6to build the assembly. Verify that it compiles without error.
Sign plug-in
-
InSolution Explorer, right click theBasicPluginproject and in the context menu selectProperties.
-
In the project properties, select theSigningtab and select theSign the assemblycheckbox.
-
In theChoose a strong name key file: dropdown, select<New…>.
-
In theCreate Strong Name Keydialog, enter akey file nameand deselect theProtect my key file with a passwordcheckbox.
-
ClickOKto close theCreate Strong Name Keydialog.
-
In the project propertiesBuildtab, verify that theConfigurationis set toDebug.
-
PressF6to build the plug-in again.
-
Using windows explorer, find the built plug-in at:
\bin\Debug\BasicPlugin.dll
.
Note
Build the assembly usingDebugconfiguration because you will use the Plug-in profiler to debug it in a later tutorial. Before you include a plug-in with your solution, you should build it using the release configuration.
Register plug-in
To register a plug-in, you will need the plug-in registration tool
Connect using the Plug-in Registration tool
-
After you have downloaded the Plug-in registration tool, click the
PluginRegistration.exe
to open it. -
ClickCreate new Connectionto connect to your instance.
-
Make sureOffice 365is selected. If you are connecting using a Microsoft account other than one you are currently using, clickShow Advanced.
-
Enter your credentials and clickLogin.
-
If your Microsoft Account provides access to multiple environments, you will need to choose an environment.
-
After you are connected, you will see any existing registered plug-ins & custom workflow activities
Register your assembly
-
In theRegisterdrop-down, selectNew Assembly.
-
In theRegister New Assemblydialog, select the ellipses (…) button and browse to the assembly you built in the previous step.
-
For Microsoft 365 users, verify that theisolation modeissandboxand thelocationto store the assembly isDatabase.
Note
Other options forisolation modeandlocationapply to on-premises Dynamics 365 deployments. For the location, you can specify the D365 server's database, the server's local storage (disk), or the server's Global Assembly Cache. For more information seePlug-in Storage.
-
ClickRegister Selected Plug-ins.
-
You will see aRegistered Plug-insconfirmation dialog.
-
ClickOKto close the dialog and close theRegister New Assemblydialog.
-
You should now see the(Assembly) BasicPluginassembly which you can expand to view the(Plugin) BasicPlugin.FollowUpPluginplugin.
Register a new step
-
Right-click the(Plugin) BasicPlugin.FollowUpPluginand selectRegister New Step.
-
In theRegister New Stepdialog, set the following fields:
Setting Value Message Create Primary Entity account Event Pipeline Stage of Execution PostOperation Execution Mode Asynchronous -
ClickRegister New Stepto complete the registration and close theRegister New Stepdialog.
-
You can now see the registered step.
Note
At this point the assembly and steps are part of the systemDefault Solution. This is a good time to add them to the unmanaged solution you will distribute. These steps are not included in this tutorial. SeeAdd your assembly to a solutionandAdd step to solutionfor more information.
Test plug-in
-
Open a model-driven app and create an account entity.
-
Within a short time, open the account and you can verify the creation of the task.
What if the task wasn't created?
Because this is an asynchronous plug-in, the operation to create the task occurs after the account is created. Usually, this will happen immediately, but if it doesn't you may still be able to view the system job in the queue waiting to be applied. This step registration used theDelete AsyncOperation if StatusCode = Successfuloption which is a best practice. This means as soon as the system job completes successfully, you will not be able to view the system job data unless you re-register the plug-in with theDelete AsyncOperation if StatusCode = Successfuloption unselected.
However, if there was an error, you can view the system job to see the error message.
View System jobs
Use theDynamics 365 --customapp to view system jobs.
-
In your model-driven app, navigate to the
-
In theDynamics 365 --customapp, navigate toSettings>System>System Jobs.
-
When viewing system jobs, you can filter byEntity. SelectAccount.
-
If the job failed, you should see a record with the nameBasicPlugin.FollowupPlugin: Create of account
-
If you open the system job, you can expand theDetailssection to view the information written to the trace and details about the error.
Query System jobs
You can use the following Web API query to return failed system jobs for asynchronous plug-ins
GET <your org uri>/api/data/v9.0/asyncoperations?$filter=operationtype eq 1 and statuscode eq 31&$select=name,message
More information:Query Data using the Web API
Or use the following FetchXml:
XML<fetch top='50' >
<entity name='asyncoperation' >
<attribute name='message' />
<attribute name='name' />
<filter type='and' >
<condition attribute='operationtype' operator='eq' value='1' />
<condition attribute='statuscode' operator='eq' value='31' />
</filter>
</entity>
</fetch>
More information:Use FetchXML with FetchExpression
View trace logs
The sample code wrote a message to the trace log. The steps below describe how to view the logs.
By default, plug-in trace logs are not enabled.
Tip
IF you prefer to change this setting in code: This setting is in theOrganization Entity PluginTraceLogSetting attribute.
The valid values are:
Value | Label |
---|---|
0 | Off |
1 | Exception |
2 | All |
Use the following steps to enable them in a model-driven app.
-
Open the Dynamics 365 - custom app.
-
Navigate toSettings>System>Administration.
-
InAdministration, selectSystem Settings.
-
In theSystem Settingsdialog, in the customization tab, setEnable logging to plug-in trace logtoAll.
Note
You should disable logging after you are finished testing your plug-in, or at least set it toExceptionrather thanAll.
-
ClickOKto close theSystem Settingsdialog.
-
Repeat the steps to test your plug-in by creating a new account.
-
In theDynamics 365 -- custom app, navigate toSettings>Customization>Plug-In Trace Log.
-
You should find that a new Plug-in Trace Log record has been created.
-
If you open the record you might expect that it would include the information you set in your trace, but it does not. It only verifies that the trace occurred.
-
To see the details, it is easier to query this data using the Web API in your browser using the following query with theplugintracelog EntityType, using the
typename
property to filter results in themessageblock
property based on the name of the plug-in class:GET <your org uri>/api/data/v9.0/plugintracelogs?$select=messageblock&$filter=typename eq 'BasicPlugin.FollowUpPlugin'
-
You can expect to see the following returned with the Web API query:
JSON{ "@odata.context": "<your org uri>/api/data/v9.0/$metadata#plugintracelogs(messageblock)", "value": [{ "messageblock": "FollowupPlugin: Creating the task activity.", "plugintracelogid": "f0c221d1-7f84-4f89-acdb-bbf8f7ce9f6c" }] }