媒体浏览器有一个提供者模型,这基本上是为每个实体按特定顺序调用的一系列类。
例如我们有:
providers = new List<IMetadataProvider>();
providers.Add(new ImageFromMediaLocationProvider());
providers.Add(new ImageByNameProvider());
providers.Add(new MovieProviderFromXml());
providers.Add(new MovieDbProvider());
providers.Add(new TVProviderFromXmlFiles());
providers.Add(new TvDbProvider());
providers.Add(new VirtualFolderProvider());
providers.Add(new FrameGrabProvider());
providers.Add(new MediaInfoProvider());
列表中提供商的顺序是重要高阶提供商优先于低阶提供商。
最近,我试图使这部分可扩展。因此,第三方DLL可以定义自己的提供程序,这些提供程序将被注入我们的链中。
问题在于,一旦您允许第三方将自己注入链中,您将失去定义此订单的中心位置。
我当前有点不舒服的解决方案是为每个提供商定义一个可选的优先级属性,然后按优先级排序。
例如我现在有:
[ProviderPriority(20)]
class ImageByNameProvider{}
这允许第三方定义他们在链中的位置。
我想到的另一个解决方案是在属性Eg之前和之后。
[Before(typeof(ImageByNameProvider))]
class ImageFromMediaLocationProvider {}
但是,我不确定这是否更容易或更难以编程。
这个问题还有其他解决办法吗?你会选择哪种解决方案?
也许,我应该保留核心提供商的列表,并为第三方提供商添加前/后属性...
答案 0 :(得分:1)
这里似乎有一些不同的问题需要解决。根本问题是尝试提出一种机制,允许在该列表中间的某个位置将任意对象插入到现有列表中。
您没有描述IMetadataProvider接口的实际外观,但它应该有一些方法来唯一地标识提供者(最好的选择是使用Guid)。使用类名的好处是,它允许您在重构期间根据需要重命名类,而不会影响自定义(第三方)提供程序,只要您保持Guid相同。
您可能应该派生自己的列表,而不是使用简单的List:
class ProviderList : List<IMetadataProvider { }
它为自定义(第三方)提供程序提供了一种从该列表安装/卸载自身的方法。这些机制需要足够聪明,知道如何将新提供者插入链的中间,但也足够聪明,知道如何处理已插入的多个自定义提供者。同样,删除过程也需要聪明,以处理类似的问题,并确保有人不会尝试删除您的“核心”提供商之一。
这里的一个好方法可能是将您想要插入的提供程序的Guid作为参数传递给Install()方法。 Remove()方法同样会删除提供者的Guid。
例如,假设我在MovieProviderFromXml之后插入一个新的提供者。然后另一个第三方也在MovieProviderFromXml之后安装一个新的提供者。新的连锁订单应该是什么?第二个提供者是否总是在MovieProviderFromXml之后立即插入或者从那里开始然后跳过任何自定义提供者并在安装最后一个自定义提供者之后插入(因此就在下一个“核心”提供者之前?
与该问题相关的是,您需要有一些方法来区分您的“核心”提供者和自定义提供者。
最后,您需要确保有一种方法来处理链中的故障,尤其是在自定义提供程序插入错误位置时。
您确实希望始终维护默认链的基本(“主”)列表。当在该链的中间安装新的提供者时,应该创建一个新的链但是您不想松开基链。这使您能够将链重置回默认状态。
基于优先级的链接存在问题,因为您必须确定如何处理优先级冲突。对于Before / After属性集,您是否允许在同一个提供程序中使用它们?可能不是,因此创建具有ChainInsert枚举属性的ProviderChainAttribute可能更有意义,其中ChainInsert将Before和After定义为枚举值。这允许您强制自定义提供程序决定是否在指定提供程序之后的或之前安装。我仍然会使用Guid而不是类型。
希望这能为您提供有关如何解决此问题的其他想法。