这与文档中的警告并不完全一致。在这种情况下,这IServiceProvider
不是全局变量;它是一个方法参数,因此每次调用Execute
都有自己的提供者。
为了提高性能,Microsoft Dynamics CRM 缓存插件实例。插件的 Execute 方法应该写成无状态的,因为不是每次调用插件都会调用构造函数。此外,多个线程可以同时运行插件。所有每次调用的状态信息都存储在上下文中。这意味着您不应该在插件中使用全局类变量[Emphasis mine]。
将对象从上下文传递给需要它们的辅助类没有任何问题。该警告建议不要将某些内容存储在插件类本身的字段(“类变量”)中,这可能会影响对同一实例的后续调用,或者如果同时在同一实例上被多个线程调用,Execute
则会导致并发问题。Execute
当然,这种“全球性”必须被认为是可传递的。如果您以任何方式将任何内容存储在插件类或帮助器类中,多个调用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
. 一切都很好,直到您发现其中一个偶尔会失败,因为它需要IOrganizationService
通过调用获取的实例CreateOrganizationService(null)
,因此它以系统用户而不是调用用户(并不总是具有所需权限)的身份访问 CRM。
修复第二种方法需要您将另一个字段添加到不断增长的全局变量列表中,记住使其ThreadStatic
避免并发问题,然后更改代码CustomEntityFrubber
以使用新SystemService
属性。所有这些类之间都有紧密的耦合。
不仅如此,所有这些全局变量都在插件调用之间徘徊。GlobalPluginParameters.EntityIdToFrub
如果您的代码有一个错误以某种方式绕过Execute
.
CustomEntityFrubber
除非您阅读其代码,否则这些全局变量中究竟需要哪些变量也并不明显。将其乘以您拥有的许多帮助程序类,维护此代码开始变得令人头疼。“现在,这个对象需要我设置Guid1
还是Guid2
在我调用它之前?” 最重要的是,类本身不能确定其他一些代码不会改变它所依赖的全局变量的值。
如果您使用第一种方法,您只需将不同的值传递给CustomEntityFrubber
构造函数,无需进一步更改代码。此外,没有过时的数据。构造函数使类具有哪些依赖项一目了然,一旦有了它们,就可以确定它们不会改变,除非它们的设计方式不同。