文档中的警告并非如此。 IServiceProvider 在这种情况下不是全局变量;它是一个方法参数,因此Execute 的每次调用都有自己的提供者。
为了提高性能,Microsoft Dynamics CRM 缓存插件实例。插件的 Execute 方法应该写成无状态的,因为不是每次调用插件都会调用构造函数。此外,多个线程可以同时运行插件。所有每次调用的状态信息都存储在上下文中。这意味着您不应该在插件中使用全局类变量[Emphasis mine]。
没有错将对象从上下文传递到需要它们的辅助类。警告建议不要在插件类本身的字段(“类变量”)中存储某些内容,这可能会影响随后在同一实例上对 Execute 的调用,或者如果 @987654324 会导致并发问题@ 被同一实例上的多个线程同时调用。
当然,这种“全局性”必须被认为是可传递的。如果您将任何内容存储在插件类或中的帮助类以任何方式对Execute 的多次调用都可以访问(使用插件类上的字段或静态例如,无论是插件类还是帮助类),您都会面临同样的问题。
作为一个单独的考虑因素,我会编写所涉及的辅助类来接受尽可能特定于其功能的类型 - 直至单个实体的级别 - 而不是整个 IServiceProvider。测试只需要 EntityReference 的类比需要模拟整个 IServiceProvider 和 IPluginExecutionContext 的类容易得多。
关于全局变量与类所需的注入值
你说得对,这是面向对象代码中随处可见的东西。看看这两个实现:
public class CustomEntityFrubber
{
public CustomEntityFrubber(IOrganizationService service, Guid entityIdToFrub)
{
this.service = service;
this.entityId = entityIdToFrub;
}
public void FrubTheEntity()
{
// Do something with service and entityId.
}
private readonly IOrganizationService service;
private readonly Guid entityId;
}
// Initialised by the plugin's Execute method.
public static class GlobalPluginParameters
{
public static IOrganizationService Service
{
get { return service; }
set { service = value; }
}
public static Guid EntityIdToFrub
{
get { return entityId; }
set { entityId = value; }
}
[ThreadStatic]
private static IOrganizationService service;
[ThreadStatic]
private static Guid entityId;
}
public class CustomEntityFrubber
{
public FrubTheEntity()
{
// Do something with the members on GlobalPluginParameters.
}
}
假设您已经实现了类似于第二种方法的东西,现在您有一堆使用GlobalPluginParameters 的类。一切正常,直到您发现其中 一个 偶尔失败,因为它需要通过调用 CreateOrganizationService(null) 获得的 IOrganizationService 实例,因此它以系统用户而不是调用用户身份访问 CRM (他们并不总是拥有所需的权限)。
修复第二种方法需要您在不断增长的全局变量列表中添加另一个字段,记住将其设为 ThreadStatic 以避免并发问题,然后更改 CustomEntityFrubber 的代码以使用新的 SystemService 属性。所有这些类之间都有紧密的耦合。
不仅如此,所有这些全局变量都会在插件调用之间徘徊。如果您的代码有一个错误以某种方式绕过GlobalPluginParameters.EntityIdToFrub 的分配,那么您的插件突然莫名其妙地对当前对Execute 的调用未传递给它的数据进行操作。
除非你阅读它的代码,否则CustomEntityFrubber 需要这些全局变量中的哪一个也不是很明显。将其乘以您拥有的许多帮助程序类,维护此代码开始变得令人头疼。 “现在,这个对象在我调用它之前需要我设置Guid1 或Guid2 吗?”最重要的是,类本身不能确定其他代码不会改变它所依赖的全局变量的值。
如果您使用第一种方法,您只需将不同的值传递给CustomEntityFrubber 构造函数,无需进一步更改代码。此外,没有过时的数据。构造函数使类具有哪些依赖项一目了然,一旦有了它们,就可以确保它们不会改变,除非它们的设计方式有所改变。