Skip to content

Commit 8a5f23b

Browse files
committed
Merge branch 'main' into FetchXml
2 parents feb1074 + 13dcec3 commit 8a5f23b

31 files changed

+441
-322
lines changed

.openpublishing.redirection.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8810,11 +8810,6 @@
88108810
"redirect_url": "/power-apps/maker/canvas-apps/create-performant-apps-overview",
88118811
"redirect_document_id": false
88128812
},
8813-
{
8814-
"source_path": "powerapps-docs/maker/data-platform/azure-synapse-link-troubleshooting-guide.md",
8815-
"redirect_url": "/power-apps/maker/data-platform/export-data-lake-faq",
8816-
"redirect_document_id": false
8817-
},
88188813
{
88198814
"source_path": "powerapps-docs/maker/canvas-apps/slow-performance-sources.md",
88208815
"redirect_url": "/power-apps/maker/canvas-apps/create-performant-apps-overview",
Lines changed: 138 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
---
22
title: "Develop IPlugin implementations as stateless | MicrosoftDocs"
33
description: "Members of classes that implement IPlugin are exposed to potential thread-safety issues, which could lead to data inconsistency or performance problems."
4-
services: ''
54
suite: powerapps
6-
documentationcenter: na
75
author: jowells
86
ms.reviewer: phecke
9-
editor: ''
10-
tags: ''
11-
12-
ms.devlang: na
137
ms.topic: article
14-
ms.tgt_pltfrm: na
15-
ms.workload: na
168
ms.date: 9/05/2019
179
ms.subservice: dataverse-developer
1810
ms.author: jowells
@@ -40,83 +32,84 @@ Members of classes that implement the <xref href="Microsoft.Xrm.Sdk.IPlugin?text
4032

4133
## Guidance
4234

43-
When implementing <xref:Microsoft.Xrm.Sdk.IPlugin>, do not use member fields and properties and write the <xref:Microsoft.Xrm.Sdk.IPlugin.Execute*> method as a stateless operation. All per invocation state information should be accessed via the execution context only.
35+
When implementing <xref:Microsoft.Xrm.Sdk.IPlugin>, don't use member fields and properties and write the <xref:Microsoft.Xrm.Sdk.IPlugin.Execute*> method as a stateless operation. All per invocation state information should be accessed via the execution context only.
4436

45-
Do not attempt to store any execution state data in member fields or properties for use during the current or next plug-in invocation unless that data was obtained from the configuration parameter provided to the overloaded constructor.
37+
Don't attempt to store any execution state data in member fields or properties for use during the current or next plug-in invocation unless that data was obtained from the configuration parameter provided to the overloaded constructor.
4638

47-
Do not use code that registers to AppDomain events. Plugin logic should not rely on any AppDomain events or properties, since the internal implementation of the plugin infrastructure can change the execution behavior at any point of time. This can cause failures even if the code worked at some point in time.
39+
Don't use code that registers to AppDomain events. Plugin logic shouldn't rely on any AppDomain events or properties, since the internal implementation of the plugin infrastructure can change the execution behavior at any point of time. Registering to AppDomain events can cause failures even if the code worked at some point in time.
4840

4941
Read-only, static, and constant members are inherently thread-safe and can also be used reliably within a plug-in class. The following are some examples on how to maintain thread-safe plug-ins:
5042

51-
- Constant field members
52-
53-
```csharp
54-
public class Valid_ClassConstantMember : IPlugin
55-
{
56-
public const string validConstantMember = "Plugin registration not valid for {0} message.";
57-
58-
public void Execute(IServiceProvider serviceProvider)
59-
{
60-
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
61-
62-
if (context.MessageName.ToLower() != "create")
63-
throw new InvalidPluginExecutionException(String.Format(Valid_ClassConstantMember.validConstantMember, context.MessageName));
64-
}
65-
}
66-
```
67-
68-
- Storing configuration data assigned or set in plug-in class constructor
69-
```csharp
70-
public class Valid_ClassFieldConfigMember : IPlugin
71-
{
72-
private string validConfigField;
73-
74-
public Valid_ClassFieldConfigMember(string unsecure, string secure)
75-
{
76-
this.validConfigField = String.IsNullOrEmpty(secure)
77-
? unsecure
78-
: secure;
79-
}
80-
81-
public void Execute(IServiceProvider serviceProvider)
82-
{
83-
if (!String.IsNullOrEmpty(this.validConfigField))
84-
{
85-
var message = ValidHelperMethod();
86-
}
87-
}
88-
89-
private string ValidHelperMethod()
90-
{
91-
return String.Format("{0} is the config value.", this.validConfigField);
92-
}
93-
}
94-
```
95-
96-
- Stateless method implementation
97-
98-
```csharp
99-
public class Valid_ClassStatelessMethodMember : IPlugin
100-
{
101-
public void Execute(IServiceProvider serviceProvider)
102-
{
103-
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
104-
105-
if (ValidMemberMethod(context))
106-
{
107-
//Then continue with execution
108-
}
109-
}
110-
111-
private bool ValidMemberMethod(IPluginExecutionContext context)
112-
{
113-
if (context.MessageName.ToLower() == "create")
114-
return true;
115-
else
116-
return false;
117-
}
118-
}
119-
```
43+
### Constant field members
44+
45+
```csharp
46+
public class Valid_ClassConstantMember : IPlugin
47+
{
48+
public const string validConstantMember = "Plugin registration not valid for {0} message.";
49+
50+
public void Execute(IServiceProvider serviceProvider)
51+
{
52+
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
53+
54+
if (context.MessageName.ToLower() != "create")
55+
throw new InvalidPluginExecutionException(String.Format(Valid_ClassConstantMember.validConstantMember, context.MessageName));
56+
}
57+
}
58+
```
59+
60+
### Storing configuration data assigned or set in plug-in class constructor
61+
62+
```csharp
63+
public class Valid_ClassFieldConfigMember : IPlugin
64+
{
65+
private string validConfigField;
66+
67+
public Valid_ClassFieldConfigMember(string unsecure, string secure)
68+
{
69+
this.validConfigField = String.IsNullOrEmpty(secure)
70+
? unsecure
71+
: secure;
72+
}
73+
74+
public void Execute(IServiceProvider serviceProvider)
75+
{
76+
if (!String.IsNullOrEmpty(this.validConfigField))
77+
{
78+
var message = ValidHelperMethod();
79+
}
80+
}
81+
82+
private string ValidHelperMethod()
83+
{
84+
return String.Format("{0} is the config value.", this.validConfigField);
85+
}
86+
}
87+
```
88+
89+
### Stateless method implementation
90+
91+
```csharp
92+
public class Valid_ClassStatelessMethodMember : IPlugin
93+
{
94+
public void Execute(IServiceProvider serviceProvider)
95+
{
96+
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
97+
98+
if (ValidMemberMethod(context))
99+
{
100+
//Then continue with execution
101+
}
102+
}
103+
104+
private bool ValidMemberMethod(IPluginExecutionContext context)
105+
{
106+
if (context.MessageName.ToLower() == "create")
107+
return true;
108+
else
109+
return false;
110+
}
111+
}
112+
```
120113

121114
<a name='problem'></a>
122115

@@ -125,86 +118,85 @@ Read-only, static, and constant members are inherently thread-safe and can also
125118
> [!WARNING]
126119
> These patterns should be avoided.
127120
128-
- Assigning plug-in class field member during plug-in execution
121+
### Assigning plug-in class field member during plug-in execution
129122

130-
```csharp
131-
public class Violation_ClassAssignFieldMember : IPlugin
132-
{
133-
//The instance member used in multiple violation patterns
134-
internal IOrganizationService service = null;
135-
internal IPluginExecutionContext context = null;
136-
137-
public void Execute(IServiceProvider serviceProvider)
138-
{
139-
this.context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
140-
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
141-
142-
//The violation
143-
this.service = factory.CreateOrganizationService(this.context.UserId);
144-
145-
//Invoke another violation in method member
146-
AccessViolationProperties();
147-
}
148-
149-
private void AccessViolationProperties()
150-
{
151-
//Accessing the context and service fields exposes this IPlugin implementation to thread-safety issues
152-
var entity = new Entity("task");
153-
entity["regardingid"] = new EntityReference(this.context.PrimaryEntityName, this.context.PrimaryEntityId);
154-
155-
var id = this.service.Create(entity);
156-
}
157-
}
158-
```
159-
160-
- Setting plug-in class property member during plug-in execution
161-
162-
```csharp
163-
public class Violation_ClassAssignPropertyMember : IPlugin
164-
{
165-
//The instance member used in multiple violation patterns
166-
internal IOrganizationService Service { get; set; }
167-
internal IPluginExecutionContext Context { get; set; }
168-
169-
public void Execute(IServiceProvider serviceProvider)
170-
{
171-
this.Context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
172-
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
173-
174-
//The violation
175-
this.Service = factory.CreateOrganizationService(context.UserId);
176-
177-
//Invoke another violation in method member
178-
AccessViolationProperties();
179-
}
180-
181-
private void AccessViolationProperties()
182-
{
183-
//Accessing the Context and Service properties exposes this IPlugin implementation to thread-safety issues
184-
var entity = new Entity("task");
185-
entity["regardingid"] = new EntityReference(this.Context.PrimaryEntityName, this.Context.PrimaryEntityId);
186-
187-
var id = this.Service.Create(entity);
188-
}
189-
}
190-
```
123+
```csharp
124+
public class Violation_ClassAssignFieldMember : IPlugin
125+
{
126+
//The instance member used in multiple violation patterns
127+
internal IOrganizationService service = null;
128+
internal IPluginExecutionContext context = null;
129+
130+
public void Execute(IServiceProvider serviceProvider)
131+
{
132+
this.context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
133+
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
134+
135+
//The violation
136+
this.service = factory.CreateOrganizationService(this.context.UserId);
137+
138+
//Invoke another violation in method member
139+
AccessViolationProperties();
140+
}
141+
142+
private void AccessViolationProperties()
143+
{
144+
//Accessing the context and service fields exposes this IPlugin implementation to thread-safety issues
145+
var entity = new Entity("task");
146+
entity["regardingid"] = new EntityReference(this.context.PrimaryEntityName, this.context.PrimaryEntityId);
147+
148+
var id = this.service.Create(entity);
149+
}
150+
}
151+
```
152+
153+
### Setting plug-in class property member during plug-in execution
154+
155+
```csharp
156+
public class Violation_ClassAssignPropertyMember : IPlugin
157+
{
158+
//The instance member used in multiple violation patterns
159+
internal IOrganizationService Service { get; set; }
160+
internal IPluginExecutionContext Context { get; set; }
161+
162+
public void Execute(IServiceProvider serviceProvider)
163+
{
164+
this.Context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
165+
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
166+
167+
//The violation
168+
this.Service = factory.CreateOrganizationService(context.UserId);
169+
170+
//Invoke another violation in method member
171+
AccessViolationProperties();
172+
}
173+
174+
private void AccessViolationProperties()
175+
{
176+
//Accessing the Context and Service properties exposes this IPlugin implementation to thread-safety issues
177+
var entity = new Entity("task");
178+
entity["regardingid"] = new EntityReference(this.Context.PrimaryEntityName, this.Context.PrimaryEntityId);
179+
180+
var id = this.Service.Create(entity);
181+
}
182+
}
183+
```
191184

192185
<a name='additional'></a>
193186

194187
## Additional information
195188

196-
After Microsoft Dataverse instantiates the plug-in class, the platform caches that plug-in instance for performance reasons. The length of time that a plug-in instance is held in cache is managed by the platform. 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.
189+
After Microsoft Dataverse instantiates the plug-in class, the platform caches that plug-in instance for performance reasons. Dataverse manages the length of time that a plug-in instance is held in cache. Certain operations, such as changing a plug-in's registration properties, trigger a notification to the platform to refresh the cache. In these scenarios, the plug-in is reinitialized.
197190

198-
Because the platform caches plug-in class instances, the constructor is not called for every invocation of plug-in execution. For this reason, IPlugin implementations should not depend on the timing of operations in the constructor apart from obtaining static configuration data.
191+
Because the platform caches plug-in class instances, the constructor isn't called for every invocation of plug-in execution. For this reason, IPlugin implementations shouldn't depend on the timing of operations in the constructor apart from obtaining static configuration data.
199192

200-
Another reason 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.
193+
Another reason IPlugins should be stateless is that multiple system threads could execute the same, shared, plug-in instance concurrently. This anti-pattern opens up members of classes that implement IPlugin to potential thread-safety issues, which could lead to data inconsistency or performance problems.
201194

202195
<a name='seealso'></a>
203196

204197
### See also
205198

206199
[Write a plug-in](../../write-plug-in.md)<br />
207-
[CRM Team Blog: Thread Safety in Plug-ins](https://cloudblogs.microsoft.com/dynamics365/no-audience/2008/11/18/member-static-variable-and-thread-safety-in-plug-in-for-crm-4-0/)<br />
208200

209201

210202
[!INCLUDE[footer-include](../../../../includes/footer-banner.md)]

0 commit comments

Comments
 (0)