C# 合成后,如何在MEF中更改部分合成?

C# 合成后,如何在MEF中更改部分合成?,c#,.net,unity-container,mef,composition,C#,.net,Unity Container,Mef,Composition,我已将我的应用程序设置为具有可发现的安全服务(ISecurityService),该服务具有单个方法IPrincipal GetPrincipal())。然后,实现者可以自由决定如何获取主体(通过域登录、DB等)。因此,我的应用程序在启动时会根据用户所处的角色进行操作,例如,我导入的界面部分如下所示: [Import] public ISecurityService SecurityService { get; set; } [ImportMany] public IEnumerable

我已将我的应用程序设置为具有可发现的安全服务(
ISecurityService
),该服务具有单个方法
IPrincipal GetPrincipal()
)。然后,实现者可以自由决定如何获取主体(通过域登录、DB等)。因此,我的应用程序在启动时会根据用户所处的角色进行操作,例如,我导入的界面部分如下所示:

[Import]
public ISecurityService SecurityService {
    get; set;
}
[ImportMany]
public IEnumerable<ISectionPanel> ImportedPanels {
    get; set;
}
public ObservableCollection<ISectionPanel> Panels {
    get; set;
}
public void OnImportsSatisfied() {
    Panels.Clear();
    IPrincipal p = Thread.CurrentPrincipal;
    foreach (ISectionPanel sp in ImportedPanels.Where(sp => sp.RequiredRole == null || p.IsInRole(sp.RequiredRole))) {
        Panels.Add(p);
    }
}
[导入]
公共ISecurityService安全服务{
获得;设置;
}
[进口数量]
公共IEnumerable导入面板{
获得;设置;
}
公众可观察收集小组{
获得;设置;
}
公共无效OnImportsAssetized(){
面板。清除();
IPrincipal p=Thread.CurrentPrincipal;
foreach(导入面板中的ISectionPanel sp.Where(sp=>sp.RequiredRole==null | | p.IsInRole(sp.RequiredRole))){
增加(p);
}
}
不要过多地关注实现,稍后将更改为注释,然而,这里让我变得更加大胆的重要一点是,在设置安全主体之前,部件的组合已经发生了。这意味着我现在有猫捉老鼠的情况

我现在已经通过在影响链接发生的导入上使用
Lazy
解决了这个问题,但是如果一个部件的另一个实现者忘记使用
Lazy
,它可能会触发链接加载并导致应用程序失败

其他人用什么来克服这种情况

以前我有unity,我通过简单地使用
RegisterInstance(T)
以更手动的方式控制它,现在我尝试使用“官方”MEF编写应用程序,因为这是框架附带的,我不再需要担心unity

理想情况下,我希望能够做到的是

  • 在合成之前,在启动时手动创建零件
  • 创建一个合成容器,在unity中手动添加我的预构建部件(如
    RegisterInstance(T)
  • 使用文档中显示的常用合成方法查找剩余零件

  • 您可以分两个阶段初始化应用程序:

    public static void Main(string[] args)
    {
        using (var container = new CompositionContainer(...))
        {
            // phase 1: compose security service and initialize principal
            var securityService = container.GetExportedValue<ISecurityService>();
            securityService.InitializePrincipal();
    
            // phase 2: compose the rest of the application and start it
            var form = container.GetExportedvalue<MainForm>();
            Application.Run(form);
        }
    }
    
    publicstaticvoidmain(字符串[]args)
    {
    使用(var container=newcompositioncontainer(…)
    {
    //阶段1:组合安全服务并初始化主体
    var securityService=container.GetExportedValue();
    securityService.InitializePrincipal();
    //阶段2:编写应用程序的其余部分并启动它
    var form=container.GetExportedvalue();
    申请表格;
    }
    }
    
    在MEF中,或多或少与注册状态相对应的是方法。如果主机在不使用MEF的情况下创建了安全服务,这将起作用。因为您仍然希望使用MEF发现安全服务,类似Wim建议的方法可能是一个很好的解决方案。

    我很困惑:您说您得到了主体吗来自
    ISecurityService.GetPrincipal()
    ,但您的示例代码实际上是从
    线程中获取的。CurrentPrincipal
    。这是您问题的原因吗?不,对不起,我应该解释一下。调用我的安全服务GetPrincipal设置线程。CurrentPrincipal在应用程序启动过程中,您刚刚给了我一个灯泡时刻。来自codeplex I的所有MEF文档我想我需要调用
    ComposeParts()
    来构建依赖关系图,但现在我想我明白了,我可以使用MEF,就像我以前调用
    GetExportedValue
    时使用Unity来构建我的图一样,该依赖关系的任何依赖关系都将得到解决,对吧?我非常感谢您向我指出这一点,我个人认为MEF文档这一点可以大大提高:)@Brett:对
    GetExportedValue
    的功能与Unity的
    Resolve
    几乎相同。有点遗憾的是,方法名和文档在这方面太神秘了。这可能是因为MEF非常关注可扩展性场景(在一些关键的扩展点上调用
    ComposeParts
    ),而不是全面的依赖注入,现在我的大脑中有两个方面在为从统一走向MEF而斗争,一方试图说服另一方回到统一;)非常感谢Daniel,是的,我想我之前看到过这一点,我实际上面临的问题是我编写的安全服务也有它自己的依赖性,即
    IUserProvider
    ,这是我知道在哪里加载用户数据库的方式,如static(test)或db。我可能有点天真地认为在容器构建之后需要调用
    ComposeParts
    ,但现在我意识到情况并非如此。丹尼尔,你也帮了很多忙,真的非常感谢你。