5

我的 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

我遇到的问题是,当 MEF 尝试在IServiceWorker它失败的属性上运行组合时。最初我虽然IServiceWorker没有作为可用部分之一通过AggregateCatalog

Dim catelog As AggregateCatalog = New AggregateCatalog(New DirectoryCatalog("."), New AssemblyCatalog(Reflection.Assembly.GetExecutingAssembly().CodeBase))

为了确认这是一个有效的零件,我使用以下代码将零件集合输出到一个文本文件。

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

您可以从下面的摘录中看到该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

这是我所期望的,因为Worker该类在 type 上执行 Export 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

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

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

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

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 : 

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

知道为什么此导入不起作用吗?

编辑:我已经重构了代码,以便将“Worker”类称为具体实现,然后组合发生在“Worker”类中,一切正常。是否存在不能从继承自 'System.ServiceProcess.ServiceBase' 的 Windows 服务组件中组合 MEF 部件的限制?

4

1 回答 1

0

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

我不认为这是 Service 基类本身的问题,它看起来并不太奇怪,它不是静态的,它只是扩展了 Component 等。相反,我敢打赌它与 Services 本身和资源管理。

对于笑容和笑声,您是否将 ShadowCopy 用于您的 MEF DirectoryCatalog?如果您不是,我想知道它是否会有所帮助...其中一些是猜测,因为我还没有时间设置自己的测试台,但这是分别从 MEF 和服务经验得出的有根据的猜测。

于 2013-06-25T14:25:15.217 回答