init commit
This commit is contained in:
11
bin/pac/tools/templates/plugin/.gitignore
vendored
Normal file
11
bin/pac/tools/templates/plugin/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# msbuild output directories
|
||||
/bin
|
||||
/obj
|
||||
|
||||
# vs intermediate directory
|
||||
/.vs
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
30
bin/pac/tools/templates/plugin/.vscode/template_tasks.json
vendored
Normal file
30
bin/pac/tools/templates/plugin/.vscode/template_tasks.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/$safeprojectname$.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"${workspaceFolder}/$safeprojectname$.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
44
bin/pac/tools/templates/plugin/template_Plugin1.cs
Normal file
44
bin/pac/tools/templates/plugin/template_Plugin1.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Microsoft.Xrm.Sdk;
|
||||
using System;
|
||||
|
||||
namespace $safeprojectname$
|
||||
{
|
||||
/*
|
||||
* Plugin development guide: https://docs.microsoft.com/powerapps/developer/common-data-service/plug-ins
|
||||
* Best practices and guidance: https://docs.microsoft.com/powerapps/developer/common-data-service/best-practices/business-logic/
|
||||
*/
|
||||
public class Plugin1 : PluginBase
|
||||
{
|
||||
public Plugin1(string unsecureConfiguration, string secureConfiguration)
|
||||
: base(typeof(Plugin1))
|
||||
{
|
||||
// TODO: Implement your custom configuration handling
|
||||
// https://docs.microsoft.com/powerapps/developer/common-data-service/register-plug-in#set-configuration-data
|
||||
}
|
||||
|
||||
// Entry point for custom business logic execution
|
||||
protected override void ExecuteDataversePlugin(ILocalPluginContext localPluginContext)
|
||||
{
|
||||
if (localPluginContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(localPluginContext));
|
||||
}
|
||||
|
||||
var context = localPluginContext.PluginExecutionContext;
|
||||
|
||||
// TODO: Implement your custom business logic
|
||||
|
||||
// Check for the entity on which the plugin would be registered
|
||||
//if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
|
||||
//{
|
||||
// var entity = (Entity)context.InputParameters["Target"];
|
||||
|
||||
// // Check for entity name on which this plugin would be registered
|
||||
// if (entity.LogicalName == "account")
|
||||
// {
|
||||
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
282
bin/pac/tools/templates/plugin/template_PluginBase.cs
Normal file
282
bin/pac/tools/templates/plugin/template_PluginBase.cs
Normal file
@@ -0,0 +1,282 @@
|
||||
using Microsoft.Xrm.Sdk;
|
||||
using Microsoft.Xrm.Sdk.Extensions;
|
||||
using Microsoft.Xrm.Sdk.PluginTelemetry;
|
||||
using System;
|
||||
using System.ServiceModel;
|
||||
|
||||
namespace $safeprojectname$
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all plug-in classes.
|
||||
/// Plugin development guide: https://docs.microsoft.com/powerapps/developer/common-data-service/plug-ins
|
||||
/// Best practices and guidance: https://docs.microsoft.com/powerapps/developer/common-data-service/best-practices/business-logic/
|
||||
/// </summary>
|
||||
public abstract class PluginBase : IPlugin
|
||||
{
|
||||
protected string PluginClassName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pluginClassName">The <see cref="Type"/> of the plugin class.</param>
|
||||
internal PluginBase(Type pluginClassName)
|
||||
{
|
||||
PluginClassName = pluginClassName.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main entry point for he business logic that the plug-in is to execute.
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">The service provider.</param>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Execute")]
|
||||
public void Execute(IServiceProvider serviceProvider)
|
||||
{
|
||||
if (serviceProvider == null)
|
||||
{
|
||||
throw new InvalidPluginExecutionException("serviceProvider");
|
||||
}
|
||||
|
||||
// Construct the local plug-in context.
|
||||
var localPluginContext = new LocalPluginContext(serviceProvider);
|
||||
|
||||
localPluginContext.Trace($"Entered {PluginClassName}.Execute() " +
|
||||
$"Correlation Id: {localPluginContext.PluginExecutionContext.CorrelationId}, " +
|
||||
$"Initiating User: {localPluginContext.PluginExecutionContext.InitiatingUserId}");
|
||||
|
||||
try
|
||||
{
|
||||
// Invoke the custom implementation
|
||||
ExecuteDataversePlugin(localPluginContext);
|
||||
|
||||
// Now exit - if the derived plugin has incorrectly registered overlapping event registrations, guard against multiple executions.
|
||||
return;
|
||||
}
|
||||
catch (FaultException<OrganizationServiceFault> orgServiceFault)
|
||||
{
|
||||
localPluginContext.Trace($"Exception: {orgServiceFault.ToString()}");
|
||||
|
||||
throw new InvalidPluginExecutionException($"OrganizationServiceFault: {orgServiceFault.Message}", orgServiceFault);
|
||||
}
|
||||
finally
|
||||
{
|
||||
localPluginContext.Trace($"Exiting {PluginClassName}.Execute()");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder for a custom plug-in implementation.
|
||||
/// </summary>
|
||||
/// <param name="localPluginContext">Context for the current plug-in.</param>
|
||||
protected virtual void ExecuteDataversePlugin(ILocalPluginContext localPluginContext)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This interface provides an abstraction on top of IServiceProvider for commonly used PowerPlatform Dataverse Plugin development constructs
|
||||
/// </summary>
|
||||
public interface ILocalPluginContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The PowerPlatform Dataverse organization service for the Current Executing user.
|
||||
/// </summary>
|
||||
IOrganizationService InitiatingUserService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The PowerPlatform Dataverse organization service for the Account that was registered to run this plugin, This could be the same user as InitiatingUserService.
|
||||
/// </summary>
|
||||
IOrganizationService PluginUserService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// IPluginExecutionContext contains information that describes the run-time environment in which the plug-in executes, information related to the execution pipeline, and entity business information.
|
||||
/// </summary>
|
||||
IPluginExecutionContext PluginExecutionContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Synchronous registered plug-ins can post the execution context to the Microsoft Azure Service Bus. <br/>
|
||||
/// It is through this notification service that synchronous plug-ins can send brokered messages to the Microsoft Azure Service Bus.
|
||||
/// </summary>
|
||||
IServiceEndpointNotificationService NotificationService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Provides logging run-time trace information for plug-ins.
|
||||
/// </summary>
|
||||
ITracingService TracingService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// General Service Provider for things not accounted for in the base class.
|
||||
/// </summary>
|
||||
IServiceProvider ServiceProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// OrganizationService Factory for creating connection for other then current user and system.
|
||||
/// </summary>
|
||||
IOrganizationServiceFactory OrgSvcFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ILogger for this plugin.
|
||||
/// </summary>
|
||||
ILogger Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Writes a trace message to the CRM trace log.
|
||||
/// </summary>
|
||||
/// <param name="message">Message name to trace.</param>
|
||||
void Trace(string message, [CallerMemberName] string method = null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plug-in context object.
|
||||
/// </summary>
|
||||
public class LocalPluginContext : ILocalPluginContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The PowerPlatform Dataverse organization service for current user account.
|
||||
/// </summary>
|
||||
public IOrganizationService InitiatingUserService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The PowerPlatform Dataverse organization service for system user account.
|
||||
/// </summary>
|
||||
public IOrganizationService PluginUserService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// IPluginExecutionContext contains information that describes the run-time environment in which the plug-in executes, information related to the execution pipeline, and entity business information.
|
||||
/// </summary>
|
||||
public IPluginExecutionContext PluginExecutionContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Synchronous registered plug-ins can post the execution context to the Microsoft Azure Service Bus. <br/>
|
||||
/// It is through this notification service that synchronous plug-ins can send brokered messages to the Microsoft Azure Service Bus.
|
||||
/// </summary>
|
||||
public IServiceEndpointNotificationService NotificationService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Provides logging run-time trace information for plug-ins.
|
||||
/// </summary>
|
||||
public ITracingService TracingService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// General Service Provider for things not accounted for in the base class.
|
||||
/// </summary>
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// OrganizationService Factory for creating connection for other then current user and system.
|
||||
/// </summary>
|
||||
public IOrganizationServiceFactory OrgSvcFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ILogger for this plugin.
|
||||
/// </summary>
|
||||
public ILogger Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Helper object that stores the services available in this plug-in.
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider"></param>
|
||||
public LocalPluginContext(IServiceProvider serviceProvider)
|
||||
{
|
||||
if (serviceProvider == null)
|
||||
{
|
||||
throw new InvalidPluginExecutionException("serviceProvider");
|
||||
}
|
||||
|
||||
ServiceProvider = serviceProvider;
|
||||
|
||||
Logger = serviceProvider.Get<ILogger>();
|
||||
|
||||
PluginExecutionContext = serviceProvider.Get<IPluginExecutionContext>();
|
||||
|
||||
TracingService = new LocalTracingService(serviceProvider);
|
||||
|
||||
NotificationService = serviceProvider.Get<IServiceEndpointNotificationService>();
|
||||
|
||||
OrgSvcFactory = serviceProvider.Get<IOrganizationServiceFactory>();
|
||||
|
||||
PluginUserService = serviceProvider.GetOrganizationService(PluginExecutionContext.UserId); // User that the plugin is registered to run as, Could be same as current user.
|
||||
|
||||
InitiatingUserService = serviceProvider.GetOrganizationService(PluginExecutionContext.InitiatingUserId); //User who's action called the plugin.
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a trace message to the CRM trace log.
|
||||
/// </summary>
|
||||
/// <param name="message">Message name to trace.</param>
|
||||
public void Trace(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message) || TracingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PluginExecutionContext == null)
|
||||
{
|
||||
TracingService.Trace(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
TracingService.Trace(string.format(
|
||||
"{0}, Correlation Id: {1}, Initiating User: {2}",
|
||||
message,
|
||||
PluginExecutionContext.CorrelationId,
|
||||
PluginExecutionContext.InitiatingUserId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specialized ITracingService implementation that prefixes all traced messages with a time delta for Plugin performance diagnostics
|
||||
/// </summary>
|
||||
public class LocalTracingService : ITracingService
|
||||
{
|
||||
private readonly ITracingService _tracingService;
|
||||
|
||||
private DateTime _previousTraceTime;
|
||||
|
||||
public LocalTracingService(IServiceProvider serviceProvider)
|
||||
{
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
|
||||
var context = (IExecutionContext)serviceProvider.GetService(typeof(IExecutionContext));
|
||||
|
||||
DateTime initialTimestamp = context.OperationCreatedOn;
|
||||
|
||||
if (initialTimestamp > utcNow)
|
||||
{
|
||||
initialTimestamp = utcNow;
|
||||
}
|
||||
|
||||
_tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
|
||||
|
||||
_previousTraceTime = initialTimestamp;
|
||||
}
|
||||
|
||||
public void Trace(string message, params object[] args)
|
||||
{
|
||||
var utcNow = DateTime.UtcNow;
|
||||
|
||||
// The duration since the last trace.
|
||||
var deltaMilliseconds = utcNow.Subtract(_previousTraceTime).TotalMilliseconds;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if (args == null || args.Length == 0)
|
||||
_tracingService.Trace($"[+{deltaMilliseconds:N0}ms] - {message}");
|
||||
else
|
||||
_tracingService.Trace($"[+{deltaMilliseconds:N0}ms] - {string.Format(message, args)}");
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
throw new InvalidPluginExecutionException($"Failed to write trace message due to error {ex.Message}", ex);
|
||||
}
|
||||
_previousTraceTime = utcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
bin/pac/tools/templates/plugin/template_pluginproject.csproj
Normal file
20
bin/pac/tools/templates/plugin/template_pluginproject.csproj
Normal file
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<PowerAppsTargetsPath>$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\PowerApps</PowerAppsTargetsPath>$SignAssemblyBlock$
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<ProjectTypeGuids>{4C25E9B5-9FA6-436c-8E19-B395D2A65FAF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Plugin.props" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Plugin.props')" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CrmSdk.CoreAssemblies" Version="9.0.2.*" />
|
||||
<PackageReference Include="Microsoft.PowerApps.MSBuild.Plugin" Version="1.*" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Plugin.targets" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Plugin.targets')" />
|
||||
</Project>
|
||||
Reference in New Issue
Block a user