MEF部分无法导入Autofac自动生成的工厂

发布于 2024-09-04 12:57:53 字数 2386 浏览 6 评论 0原文

这是一个(对我来说)非常奇怪的问题,因为它已经完美运行,但在一些不相关的更改之后完全南下。

我有一个 Repository,它通过 Autofacs MEF 集成在其构造函数中导入 IExtensions 列表。这些扩展之一包含对 Repository 的反向引用,作为 Lazy(Of IRepository) (因为会发生循环引用而延迟)。

但是,一旦我尝试使用存储库,Autofac 就会抛出 ComponentNotRegisteredException ,并显示消息“请求的服务‘ContractName=Assembly.IRepository()’尚未注册。”

然而,这并不完全正确,因为当我在容器构建后立即中断并探索服务列表时,它就在那里 - Exported() 并具有正确的 ContractName。

如果有任何帮助,我将不胜感激...
Michael

[编辑] 这是代码的精简版本:

Repository

Public Class DocumentRepository
    Implements IDocumentRepository

    Private _extensions As IEnumerable(Of IRepositoryExtension)
    
    Public Sub New(ByVal extensions As IEnumerable(Of IRepositoryExtension))
        _extensions = extensions
    End Sub

    Public Sub AddDocument(ByVal document As Contracts.IDocument) Implements Contracts.IDocumentRepository.AddDocument
        For Each extension In _extensions
            extension.OnAdded(document.Id)
        Next
    End Sub
End Class

Plugin

<Export(GetType(IRepositoryExtension))>
<PartCreationPolicy(ComponentModel.Composition.CreationPolicy.Shared)>
Public Class PdfGenerator
    Implements IRepositoryExtension

    Private _repositoryFactory As Lazy(Of IDocumentRepository)
    
    Public Sub New(ByVal repositoryFactory As Lazy(Of IDocumentRepository))
        _repositoryFactory = repositoryFactory
    End Sub

    Public Sub CreatePdf(ByVal id As Guid) Implements Contracts.IRepositoryExtension.OnAdded
        Dim document = _repositoryFactory.Value.GetDocumentById(id)
    End Sub
End Class

Bootstrapper

Public Class EditorApplication
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        Dim builder As New ContainerBuilder()
        
        Dim catalog1 As New TypeCatalog(GetType(DataRepositoryScheme))
        Dim catalog2 As New DirectoryCatalog(HttpContext.Current.Server.MapPath("/Plugins"))
        builder.RegisterComposablePartCatalog(New AggregateCatalog(catalog1, catalog2))
        
        builder.RegisterType(Of DocumentRepository).As(Of IDocumentRepository).SingleInstance().Exported(Function(x) x.As(Of IDocumentRepository)())
        
        AutofacServiceHostFactory.Container = builder.Build()
    End Sub
End Class

This is a (to me) pretty weird problem, because it was already running perfectly but went completely south after some unrelated changes.

I've got a Repository which imports in its constructor a list of IExtensions via Autofacs MEF integration. One of these extensions contains a backreference to the Repository as Lazy(Of IRepository) (lazy because of the circular reference that would occur).

But as soon as I try to use the repository, Autofac throws a ComponentNotRegisteredException with the message "The requested service 'ContractName=Assembly.IRepository()' has not been registered."

That is, however, not really correct, because when I break right after the container-build and explore the list of services, it's there - Exported() and with the correct ContractName.

I'd appreciate any help on this...
Michael

[Edit] Here's a thinned-out version of the code:

Repository

Public Class DocumentRepository
    Implements IDocumentRepository

    Private _extensions As IEnumerable(Of IRepositoryExtension)
    
    Public Sub New(ByVal extensions As IEnumerable(Of IRepositoryExtension))
        _extensions = extensions
    End Sub

    Public Sub AddDocument(ByVal document As Contracts.IDocument) Implements Contracts.IDocumentRepository.AddDocument
        For Each extension In _extensions
            extension.OnAdded(document.Id)
        Next
    End Sub
End Class

Plugin

<Export(GetType(IRepositoryExtension))>
<PartCreationPolicy(ComponentModel.Composition.CreationPolicy.Shared)>
Public Class PdfGenerator
    Implements IRepositoryExtension

    Private _repositoryFactory As Lazy(Of IDocumentRepository)
    
    Public Sub New(ByVal repositoryFactory As Lazy(Of IDocumentRepository))
        _repositoryFactory = repositoryFactory
    End Sub

    Public Sub CreatePdf(ByVal id As Guid) Implements Contracts.IRepositoryExtension.OnAdded
        Dim document = _repositoryFactory.Value.GetDocumentById(id)
    End Sub
End Class

Bootstrapper

Public Class EditorApplication
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        Dim builder As New ContainerBuilder()
        
        Dim catalog1 As New TypeCatalog(GetType(DataRepositoryScheme))
        Dim catalog2 As New DirectoryCatalog(HttpContext.Current.Server.MapPath("/Plugins"))
        builder.RegisterComposablePartCatalog(New AggregateCatalog(catalog1, catalog2))
        
        builder.RegisterType(Of DocumentRepository).As(Of IDocumentRepository).SingleInstance().Exported(Function(x) x.As(Of IDocumentRepository)())
        
        AutofacServiceHostFactory.Container = builder.Build()
    End Sub
End Class

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浅语花开 2024-09-11 12:57:53

啊,在我发布最后一条评论后,我想我明白了:

The requested service 'ContractName=ConsoleApplication7.IDocumentRepository()'
has not been registered.

请注意,合约名称后面有一对括号 - 这是因为合约是一个函数,即该消息是由以下构造函数生成的,即与示例中的稍有不同:

Public Sub New(ByVal repositoryFactory As Func(Of IDocumentRepository))
    _repositoryFactory = repositoryFactory
End Sub

请注意其中的“Func”。 MEF 与 Autofac 不同,它并不将 Func 视为特殊类型,因此不会将其转换为与 Lazy 相同的合约。

如果要向 MEF 组件提供 Func,则需要将其作为 Func 从 Autofac 导出。这有点棘手:

builder.RegisterType(Of DocumentRepository).As(Of IDocumentRepository)

builder.Register(Function(c) c.Resolve(Of Func(Of IDocumentRepository))) _
    .As(New UniqueService()) _
    .Exported(Function(x) x.As(Of Func(Of IDocumentRepository))

您可能需要稍微调整一下语法,我的 VB.NET 相当不稳定。

我的猜测是 /Extensions 目录中存在过时的二进制文件,这些二进制文件会干扰调试。

希望这是正确的!

缺口

Ah immediately after I posted that last comment I think I figured it out:

The requested service 'ContractName=ConsoleApplication7.IDocumentRepository()'
has not been registered.

Note that there is a pair of parentheses after the contract name - this is because the contract is a function, i.e., this message was produced by the following constructor, which is slightly different from the one in your sample:

Public Sub New(ByVal repositoryFactory As Func(Of IDocumentRepository))
    _repositoryFactory = repositoryFactory
End Sub

Note the 'Func' in there. MEF, unlike Autofac, does not regard Func as a special type and so will not translate this into the same contract as for Lazy.

If you want to provide a Func to a MEF component, you need to export it as a Func from Autofac. This is a bit tricky:

builder.RegisterType(Of DocumentRepository).As(Of IDocumentRepository)

builder.Register(Function(c) c.Resolve(Of Func(Of IDocumentRepository))) _
    .As(New UniqueService()) _
    .Exported(Function(x) x.As(Of Func(Of IDocumentRepository))

You may need to play with the syntax a bit, my VB.NET is fairly shaky.

My guess is that there are stale binaries in your /Extensions directory that are interfering with debugging this.

Hope this is on the mark!

Nick

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文