Skip to content

Commit a4d6c0d

Browse files
committed
acrolinx
1 parent f6ed18f commit a4d6c0d

File tree

6 files changed

+176
-184
lines changed

6 files changed

+176
-184
lines changed

powerapps-docs/developer/data-platform/best-practices/business-logic/develop-iplugin-implementations-stateless.md

Lines changed: 138 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -32,83 +32,84 @@ Members of classes that implement the <xref href="Microsoft.Xrm.Sdk.IPlugin?text
3232

3333
## Guidance
3434

35-
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.
3636

37-
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.
3838

39-
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.
4040

4141
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:
4242

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-
```csharp
62-
public class Valid_ClassFieldConfigMember : IPlugin
63-
{
64-
private string validConfigField;
65-
66-
public Valid_ClassFieldConfigMember(string unsecure, string secure)
67-
{
68-
this.validConfigField = String.IsNullOrEmpty(secure)
69-
? unsecure
70-
: secure;
71-
}
72-
73-
public void Execute(IServiceProvider serviceProvider)
74-
{
75-
if (!String.IsNullOrEmpty(this.validConfigField))
76-
{
77-
var message = ValidHelperMethod();
78-
}
79-
}
80-
81-
private string ValidHelperMethod()
82-
{
83-
return String.Format("{0} is the config value.", this.validConfigField);
84-
}
85-
}
86-
```
87-
88-
- Stateless method implementation
89-
90-
```csharp
91-
public class Valid_ClassStatelessMethodMember : IPlugin
92-
{
93-
public void Execute(IServiceProvider serviceProvider)
94-
{
95-
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
96-
97-
if (ValidMemberMethod(context))
98-
{
99-
//Then continue with execution
100-
}
101-
}
102-
103-
private bool ValidMemberMethod(IPluginExecutionContext context)
104-
{
105-
if (context.MessageName.ToLower() == "create")
106-
return true;
107-
else
108-
return false;
109-
}
110-
}
111-
```
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+
```
112113

113114
<a name='problem'></a>
114115

@@ -117,86 +118,85 @@ Read-only, static, and constant members are inherently thread-safe and can also
117118
> [!WARNING]
118119
> These patterns should be avoided.
119120
120-
- Assigning plug-in class field member during plug-in execution
121+
### Assigning plug-in class field member during plug-in execution
121122

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

184185
<a name='additional'></a>
185186

186187
## Additional information
187188

188-
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.
189190

190-
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.
191192

192-
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.
193194

194195
<a name='seealso'></a>
195196

196197
### See also
197198

198199
[Write a plug-in](../../write-plug-in.md)<br />
199-
[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 />
200200

201201

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

powerapps-docs/developer/data-platform/best-practices/business-logic/do-not-duplicate-plugin-step-registration.md

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: "Do not duplicate plug-in step registration | MicrosoftDocs"
3-
description: "Duplicate plug-in step registration will cause the plug-in to fire multiple times on the same message/event."
2+
title: "Don't duplicate plug-in step registration | MicrosoftDocs"
3+
description: "Duplicate plug-in step registration causes the plug-in to fire multiple times on the same message/event."
44
suite: powerapps
55
author: jowells
66
ms.topic: article
@@ -10,9 +10,7 @@ ms.author: jowells
1010
search.audienceType:
1111
- developer
1212
---
13-
# Do not duplicate plug-in step registration
14-
15-
13+
# Don't duplicate plug-in step registration
1614

1715
**Category**: Performance
1816

@@ -22,19 +20,20 @@ search.audienceType:
2220

2321
## Symptoms
2422

25-
Duplicate plug-in step registration will cause the plug-in to fire multiple times on the same message/event. This could lead to:
23+
Duplicate plug-in step registration causes the plug-in to fire multiple times on the same message/event. Duplicate plug-in step registration could lead to:
2624

2725
- Delayed processing of asynchronous jobs when registered as an asynchronous execution mode.
2826
- Degraded user performance experience when registered as a synchronous execution mode. Experiences include:
29-
- Unresponsive model-driven apps
30-
- Slow client interactions
31-
- The browser stops responding
27+
28+
- Unresponsive model-driven apps
29+
- Slow client interactions
30+
- The browser stops responding
3231

3332
<a name='guidance'></a>
3433

3534
## Guidance
3635

37-
Ensure you are updating existing plug-in registration steps rather than deleting and re-creating them. Additionally, only create and update plug-in registration steps in a supported manner.
36+
Ensure you're updating existing plug-in registration steps rather than deleting and re-creating them. Additionally, only create and update plug-in registration steps in a supported manner.
3837

3938
<a name='problem'></a>
4039

@@ -43,17 +42,17 @@ Ensure you are updating existing plug-in registration steps rather than deleting
4342
> [!WARNING]
4443
> These patterns should be avoided.
4544
46-
Deleting and recreating a step in the source instance (test, dev, preprod) will also create a duplicate step being registered in the target environment if that step had been registered before.
45+
Deleting and recreating a step in the source instance (test, dev, preprod) creates a duplicate step being registered in the target environment if that step was registered before.
4746

4847
![Duplicate Plug-in Step Registration.](../media/duplicate-plugin-registration-step.png)
4948

50-
Manually creating the `SDKMessageProcessingSteps` with a new GUID or updating the existing GUID within the `customizations.xml` file will result in a duplicate step being registered. These types of tasks are unsupported as outlined in [When to edit the customizations file](/power-platform/alm/when-edit-customization-file).
49+
Manually creating the `SDKMessageProcessingSteps` with a new GUID or updating the existing GUID within the `customizations.xml` file results in a duplicate step being registered. These types of tasks are unsupported as outlined in [When to edit the customizations file](/power-platform/alm/when-edit-customization-file).
5150

5251
<a name='additional'></a>
5352

5453
## Additional information
5554

56-
Duplicate plug-in step registration could cause SQL deadlocking when the events are registered on an update message. When issuing an update on a record, SQL will create a row lock on that record. If another transaction tries to update that same record, it will have to wait until the lock is released before it's able to make the update. If a timeout occurs, the transaction is rolled back and the update is not committed to the SQL database.
55+
Duplicate plug-in step registration could cause SQL deadlocking when the events are registered on an update message. When issuing an update on a record, SQL creates a row lock on that record. If another transaction tries to update that same record, it has to wait until the lock is released before it's able to make the update. If a timeout occurs, the transaction is rolled back and the update isn't committed to the SQL database.
5756

5857
<a name='seealso'></a>
5958

0 commit comments

Comments
 (0)