MEF导入在Windows服务中失败

Phi*_*ray 5 .net vb.net mef visual-studio-2012

我在Windows服务中的MEF组合中遇到了问题

以下类是继承自的部分类 System.ServiceProcess.ServiceBase

Imports System.ServiceProcess

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class svc_EpmsOPCService_6Cylinder_Zone1
    Inherits System.ServiceProcess.ServiceBase

    'UserService overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                If _catelog IsNot Nothing Then _catelog.Dispose()
                If _mefContainer IsNot Nothing Then _mefContainer.Dispose()
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    ' The main entry point for the process
    <MTAThread()> _
    Shared Sub Main()

#If Not Debug Then
        Dim ServicesToRun() As System.ServiceProcess.ServiceBase
        ServicesToRun = New System.ServiceProcess.ServiceBase() {New svc_EpmsOPCService_6Cylinder_Zone1}
        System.ServiceProcess.ServiceBase.Run(ServicesToRun)
#Else
        Dim service = New Worker
        service.InitWork()
#End If

    End Sub

    'Required by the Component Designer
    Private components As System.ComponentModel.IContainer

    ' NOTE: The following procedure is required by the Component Designer
    ' It can be modified using the Component Designer.  
    ' Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.ServiceName = "JCB.EpmsOPCService_6Cylinder_Zone1"

    End Sub

End Class

Imports System.ComponentModel.Composition
Imports System.ComponentModel.Composition.Hosting
Imports Service.Common

Public Class svc_EpmsOPCService_6Cylinder_Zone1

    Private _catelog As AssemblyCatalog
    Private _mefContainer As CompositionContainer

    <Import(GetType(IServiceWorker))>
    Private Property ServiceWorker As IServiceWorker

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Dim catelog As AggregateCatalog = New AggregateCatalog(New DirectoryCatalog("."), New AssemblyCatalog(Reflection.Assembly.GetExecutingAssembly().CodeBase))
        _mefContainer = New CompositionContainer(_catelog)
        _mefContainer.ComposeParts(Me)

    End Sub

    Protected Overrides Sub OnStart(ByVal args() As String)
        ServiceWorker.InitWork()
    End Sub

    Protected Overrides Sub OnStop()
        ServiceWorker.StopWork()
        _mefContainer.Dispose()
    End Sub

End Class
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是当MEF尝试在IServiceWorker属性上运行组合时失败.最初我虽然IServiceWorker不是作为其中一个可用部分而来的AggregateCatalog

Dim catelog As AggregateCatalog = New AggregateCatalog(New DirectoryCatalog("."), New AssemblyCatalog(Reflection.Assembly.GetExecutingAssembly().CodeBase))
Run Code Online (Sandbox Code Playgroud)

为了确认这是一个有效的部分,我使用下面的代码将部件集合输出到文本文件.

For Each p As System.ComponentModel.Composition.Primitives.ComposablePartDefinition In catelog.Parts
    System.IO.File.AppendAllText(compositionErrorLog, String.Concat(vbCrLf, p.ToString, vbCrLf), Text.Encoding.Unicode)

    For Each meta As KeyValuePair(Of String, Object) In p.Metadata
        System.IO.File.AppendAllText(compositionErrorLog, String.Concat("Meta Data Key : ", meta.Key, vbCrLf), Text.Encoding.Unicode)
        System.IO.File.AppendAllText(compositionErrorLog, String.Concat("Meta Data Val : ", meta.Value.ToString, vbCrLf), Text.Encoding.Unicode)
    Next

    For Each exp As Primitives.ExportDefinition In p.ExportDefinitions
        System.IO.File.AppendAllText(compositionErrorLog, String.Concat("Export Definition Contract Name : ", exp.ContractName, vbCrLf), Text.Encoding.Unicode)
    Next

    For Each imp As Primitives.ImportDefinition In p.ImportDefinitions
        System.IO.File.AppendAllText(compositionErrorLog, String.Concat("Import Definition Contract Name : ", imp.ContractName, vbCrLf), Text.Encoding.Unicode)
    Next

    System.IO.File.AppendAllText(compositionErrorLog, vbCrLf, Text.Encoding.Unicode)

Next
Run Code Online (Sandbox Code Playgroud)

您可以从下面的摘录中看到该Service_EPMS_OPC_6Cylinder_Zone1.Worker部件IServiceWorker在其中实现ExportDefinitions

Service.Common.DataAccess.AuditLogger
Export Definition Contract Name : Service.Common.DataAccess.IAuditLogger
Import Definition Contract Name : Service.Common.DataAccess.IDatabaseHelperFactory

Service.Common.DataAccess.DataHelper
Export Definition Contract Name : Service.Common.DataAccess.IDataHelper
Import Definition Contract Name : Service.Common.DataAccess.IDatabaseHelperFactory

Service.Common.DataAccess.SqlDatabaseHelperFactory
Export Definition Contract Name : Service.Common.DataAccess.IDatabaseHelperFactory

Service.Common.Logging.ErrorLogger
Export Definition Contract Name : Service.Common.Logging.ILogger
Import Definition Contract Name : Service.Common.DataAccess.IDataHelper

Service.Common.Network.NetworkAvailability
Export Definition Contract Name : Service.Common.Network.INetworkAvailability
Import Definition Contract Name : Service.Common.DataAccess.IDataHelper
Import Definition Contract Name : Service.Common.Network.IRemoteServiceHost

Service.Common.Network.RemoteServiceHost
Export Definition Contract Name : Service.Common.Network.IRemoteServiceHost


Service.Common.ObjectCreation.EngineBuilder
Export Definition Contract Name : Service.Common.ObjectCreation.IEngineBuilder
Import Definition Contract Name : Service.Common.DataAccess.IDataHelper

Service.Common.Opc.OpcHelper
Export Definition Contract Name : Service.Common.Opc.IOpcHelper

Service_EPMS_OPC_6Cylinder_Zone1.Worker
Export Definition Contract Name : Service.Common.IServiceWorker

Service_EPMS_OPC_6Cylinder_Zone1.ZoneProcess
Export Definition Contract Name : Service.Common.IZoneProcess
Import Definition Contract Name : Service.Common.Logging.ILogger
Import Definition Contract Name : Service.Common.DataAccess.IDataHelper
Import Definition Contract Name : Service.Common.Network.INetworkAvailability
Import Definition Contract Name : Service.Common.ObjectCreation.IEngineBuilder
Import Definition Contract Name : Service.Common.Opc.IOpcHelper
Import Definition Contract Name : Service.Common.DataAccess.IAuditLogger
Run Code Online (Sandbox Code Playgroud)

这是我所期望的,因为Worker类在类型上执行导出IServiceWorker.我已经在这个类上注释了两个额外的Imports,以确保它们不会导致问题.

Imports System.ComponentModel.Composition
Imports System.ComponentModel.Composition.Hosting
Imports System.Threading
Imports Service.Common
Imports Service.Common.Enums
Imports Service.Common.Logging

<Export(GetType(IServiceWorker))>
Public Class Worker
    Implements IServiceWorker

    Private _thread As Thread

    '<Import(GetType(IZoneProcess))>
    'Private Property Processor() As IZoneProcess

    '<Import(GetType(ILogger))>
    'Private Property Logger() As ILogger

#Region " Service Methods"

    ''' <summary>
    ''' Tear down the service
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub StopWork() Implements IServiceWorker.StopWork
        'Tear down the worker thread
        If Not _thread Is Nothing Then
            If Not _thread.Join(100) Then
                _thread.Abort()
            End If
        End If

    End Sub

    ''' <summary>
    ''' Initialise the worker thread
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub InitWork() Implements IServiceWorker.InitWork

        Dim objThreadStart As New ThreadStart(AddressOf Me.StartWork)
        _thread = New Thread(objThreadStart)
        _thread.Start()

    End Sub

    ''' <summary>
    ''' Start the worker thread functionality
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub StartWork()

        Try
            If Not Initialise() Then
                Me.StopWork()
            End If

        Catch ex As Exception
            ' If Logger IsNot Nothing Then Logger.LogError(My.Settings.ApplicationID, "StartWork", ex.Message, IssueSeverity.Critical)
            Me.StopWork()
        End Try
    End Sub

#End Region

    Private Function Initialise() As Boolean

#If DEBUG Then
        RunDebugModeComposition()
#End If
        'Return Processor.InitialiseApplication()

    End Function

    Private Sub RunDebugModeComposition()

        Try
            Dim catelog As AggregateCatalog = New AggregateCatalog(New DirectoryCatalog("."), New AssemblyCatalog(Reflection.Assembly.GetExecutingAssembly().CodeBase))
            Dim container As CompositionContainer = New CompositionContainer(catelog)
            container.ComposeParts(Me)
        Catch ex As CompositionException
            Throw New ApplicationException("The composition of the application failed. Pleae check the underlying exception", ex)
        End Try

    End Sub

End Class
Run Code Online (Sandbox Code Playgroud)

我用下面的代码进一步查询了Composition问题

Try
    _mefContainer.ComposeParts(Me)
Catch ex As CompositionException
    For Each e As CompositionError In ex.Errors
        System.IO.File.AppendAllText(compositionErrorLog, String.Concat(vbCrLf, "Description : ", e.Description, vbCrLf), Text.Encoding.Unicode)
        System.IO.File.AppendAllText(compositionErrorLog, String.Concat(vbCrLf, "Message : ", e.Exception.Message, vbCrLf), Text.Encoding.Unicode)
        System.IO.File.AppendAllText(compositionErrorLog, String.Concat(vbCrLf, "Stack Trace : ", e.Exception.StackTrace, vbCrLf), Text.Encoding.Unicode)

        If e.Exception.InnerException IsNot Nothing Then
            System.IO.File.AppendAllText(compositionErrorLog, String.Concat(vbCrLf, "Message : ", e.Exception.InnerException.Message, vbCrLf), Text.Encoding.Unicode)
            System.IO.File.AppendAllText(compositionErrorLog, String.Concat(vbCrLf, "Stack Trace : ", e.Exception.InnerException.StackTrace, vbCrLf), Text.Encoding.Unicode)
        End If

    Next
End Try
Run Code Online (Sandbox Code Playgroud)

这个游戏我在文本文件中输出如下

Description : Cannot set import 'Service_EPMS_OPC_6Cylinder_Zone1.svc_EpmsOPCService_6Cylinder_Zone1.ServiceWorker (ContractName="Service.Common.IServiceWorker")' on part 'Service_EPMS_OPC_6Cylinder_Zone1.svc_EpmsOPCService_6Cylinder_Zone1'.

Message : The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) No exports were found that match the constraint: 
    ContractName    Service.Common.IServiceWorker
    RequiredTypeIdentity    Service.Common.IServiceWorker


Stack Trace : 
Run Code Online (Sandbox Code Playgroud)

现在我不确定的是为什么IServiceWorkerMEF Catelog Parts集合中的Export没有得到ComposeParts.据我所知,Worker类的Export属性应与ServiceWorker属性中的Import匹配svc_EpmsOPCService_6Cylinder_Zone1

知道为什么这个导入不起作用?

编辑:我重构了代码,以便'Worker'类被称为具体实现,然后组合在'Worker'类中进行,一切正常.是否有限制,你不能从继承自'System.ServiceProcess.ServiceBase'的Windows服务组件组成MEF部分?

Mab*_*lah 0

迟到了,但我想知道这个问题是否与 Windows 如何注册和维护服务更相关。我相信 SCM 或其他后端服务组件在注册服务时会锁定资源。

我不认为这是服务基类本身的问题,它看起来并不太奇怪,它不是静态的,它只是扩展了组件等。相反,我打赌它与服务本身有关资源管理。

哈哈,您是否在 MEF DirectoryCatalog 中使用 ShadowCopy?如果你没有,我想知道它是否会有帮助......其中一些是猜测,因为我没有时间设置自己的测试床,但这是根据分别使用 MEF 和服务的经验进行的有根据的猜测。