【问题标题】:WF4 RC - Cannot create unknown type when loading WF Service from loose Xaml with ActivityXamlServicesWF4 RC - 使用 ActivityXamlServices 从松散 Xaml 加载 WF 服务时无法创建未知类型
【发布时间】:2010-02-25 00:32:14
【问题描述】:

我正在尝试动态托管 WF4 (RC) 服务。我有一个包含两个项目的测试解决方案。第一个是一个声明性工作流服务库,其中包含一个根流程图活动和一个简单的自定义代码活动。工作流服务库不依赖于任何其他自定义程序集或引用。第二个是我的主机应用程序,在我的测试解决方案中它只是一个控制台应用程序。

在我的宿主应用程序中,我尝试使用 ActivityXamlServices 将工作流服务的 Xaml 加载到活动中,然后使用 WorkflowServiceHost 使用该活动启动工作流实例。

当我尝试新建 WorkflowServiceHost 对象时,我会收到此异常...

无法创建未知类型 '{clr-namespace:DeclarativeServiceLibrary1}CodeActivity1'。

如果我从流程图设计器中删除 CodeActivity1,一切都会正常运行。如果我从我的宿主项目添加对工作流服务项目的直接引用,然后使用我的流程图活动的实例而不是从 Xaml 创建的活动创建一个 WorkflowServiceHost,它也可以正常工作。

由于某种原因,动态加载时似乎不喜欢使用我的 CodeActivity。

谁知道为什么我不能动态创建我的工作流服务?

我的代码如下...

DeclarativeServiceLibrary1.Activity1.xaml...

<Activity mc:Ignorable="sap" x:Class="DeclarativeServiceLibrary1.Activity1" sap:VirtualizedContainerService.HintSize="654,676" mva:VisualBasic.Settings="Assembly references and imported namespaces for internal implementation" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:local="clr-namespace:DeclarativeServiceLibrary1" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Xml" xmlns:s3="clr-namespace:System;assembly=System.Core" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System" xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core" xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Flowchart sad:XamlDebuggerXmlReader.FileName="C:\dev\test\MyWorkflow\DeclarativeServiceLibrary1\Activity1.xaml" sap:VirtualizedContainerService.HintSize="614,636">
    <sap:WorkflowViewStateService.ViewState>
      <scg3:Dictionary x:TypeArguments="x:String, x:Object">
        <x:Boolean x:Key="IsExpanded">False</x:Boolean>
        <av:Point x:Key="ShapeLocation">270,2.5</av:Point>
        <av:Size x:Key="ShapeSize">60,75</av:Size>
        <av:PointCollection x:Key="ConnectorLocation">300,77.5 300,107.5 300,165</av:PointCollection>
      </scg3:Dictionary>
    </sap:WorkflowViewStateService.ViewState>
    <Flowchart.StartNode>
      <FlowStep x:Name="__ReferenceID0">
        <sap:WorkflowViewStateService.ViewState>
          <scg3:Dictionary x:TypeArguments="x:String, x:Object">
            <av:Point x:Key="ShapeLocation">172.5,165</av:Point>
            <av:Size x:Key="ShapeSize">255,90</av:Size>
            <av:PointCollection x:Key="ConnectorLocation">300,255 300,285 300,299.5</av:PointCollection>
          </scg3:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
        <p:Receive CanCreateInstance="True" sap:VirtualizedContainerService.HintSize="255,90" OperationName="MyOperation" ServiceContractName="MyContractName" />
        <FlowStep.Next>
          <FlowStep x:Name="__ReferenceID1">
            <sap:WorkflowViewStateService.ViewState>
              <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                <av:Point x:Key="ShapeLocation">194.5,299.5</av:Point>
                <av:Size x:Key="ShapeSize">211,61</av:Size>
                <av:PointCollection x:Key="ConnectorLocation">300,360.5 300,390.5 300,399</av:PointCollection>
              </scg3:Dictionary>
            </sap:WorkflowViewStateService.ViewState>
            <WriteLine sap:VirtualizedContainerService.HintSize="211,61" Text="Workflow started" />
            <FlowStep.Next>
              <FlowStep x:Name="__ReferenceID3">
                <sap:WorkflowViewStateService.ViewState>
                  <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                    <av:Point x:Key="ShapeLocation">200,399</av:Point>
                    <av:Size x:Key="ShapeSize">200,22</av:Size>
                    <av:PointCollection x:Key="ConnectorLocation">300,421 300,451 300,479.5</av:PointCollection>
                  </scg3:Dictionary>
                </sap:WorkflowViewStateService.ViewState>
                <local:CodeActivity1 sap:VirtualizedContainerService.HintSize="200,22" />
                <FlowStep.Next>
                  <FlowStep x:Name="__ReferenceID2">
                    <sap:WorkflowViewStateService.ViewState>
                      <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                        <av:Point x:Key="ShapeLocation">194.5,479.5</av:Point>
                        <av:Size x:Key="ShapeSize">211,61</av:Size>
                      </scg3:Dictionary>
                    </sap:WorkflowViewStateService.ViewState>
                    <WriteLine sap:VirtualizedContainerService.HintSize="211,61" Text="The code activity worked!" />
                  </FlowStep>
                </FlowStep.Next>
              </FlowStep>
            </FlowStep.Next>
          </FlowStep>
        </FlowStep.Next>
      </FlowStep>
    </Flowchart.StartNode>
    <x:Reference>__ReferenceID0</x:Reference>
    <x:Reference>__ReferenceID1</x:Reference>
    <x:Reference>__ReferenceID2</x:Reference>
    <x:Reference>__ReferenceID3</x:Reference>
  </Flowchart>
</Activity>

DeclarativeServiceLibrary1.CodeActivity1.cs ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;

namespace DeclarativeServiceLibrary1
{

    public sealed class CodeActivity1 : CodeActivity
    {
        // Define an activity input argument of type string
        //public InArgument<string> Text { get; set; }

        // If your activity returns a value, derive from CodeActivity<TResult>
        // and return the value from the Execute method.
        protected override void Execute(CodeActivityContext context)
        {
            // Obtain the runtime value of the Text input argument
            //string text = context.GetValue(this.Text);
        }
    }
}

DeclarativeServiceLibrary1.Web.Config ...

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

ConsoleApplication1.Program.cs ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Activities.XamlIntegration;
using System.ServiceModel;
using System.ServiceModel.Activities;
using System.ServiceModel.Description;
using System.Xaml;
using System.Reflection;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {            
            string baseAddress = @"http://localhost:8081/MyContractName";

            string curDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string wfDefPath =  Path.Combine(curDir, "Activity1.xaml");

            Activity workflowActivity = (Activity)ActivityXamlServices.Load(wfDefPath); 
            WorkflowService service = new WorkflowService { Body = workflowActivity }; 
            Uri serviceUri = new Uri(baseAddress, UriKind.Absolute); 
            WorkflowServiceHost host = new WorkflowServiceHost(service, new Uri[] { serviceUri }); 
            host.Open();

            //Display that we are listening on the console window
            Console.WriteLine("Workflow '{0}' is listening at '{1}'", host.Activity.DisplayName, baseAddress);
            Console.ReadLine();
        }
    }
}

我在 delarative 工作流服务库中有一个构建后事件,它将 Assembly 和 Xaml 文件复制到主机控制台应用程序的 bin\debug\ 文件夹中。

【问题讨论】:

    标签: xaml workflow-foundation workflow-foundation-4


    【解决方案1】:

    简短回答 - Xaml 加载无法推断本地(默认)程序集,因此您需要在 XamlReaderSettings.LocalAssembly 上指定它。

    【讨论】:

    • 谢谢,蒂姆。如您所述,我切换到使用 XamlXmlReader 使用 XamlXmlReaderSettings.LocalAssembly 读取 xaml 文件。这样做并将 XamlXmlReader 传递到 ActivityXamlServices.Load 方法就像一个魅力。
    • 另外,您可以拆分 XAML 工作流和它们使用的活动。当您这样做时,该活动不再位于本地 CLR 命名空间中,并且可以正常加载。因此,只需创建一个仅 XAML 工作流项目和一个 Activity 项目。
    【解决方案2】:

    活动的开源代码。 将 "xmlns:local="clr-namespace:DeclarativeServiceLibrary1" 更改为 xmlns:local="clr-namespace:DeclarativeServiceLibrary1;assembly=DeclarativeServiceLibrary1"。

    【讨论】:

    • 这也为我修复了另一个错误:stackoverflow.com/questions/9743133/… error: error "'Unexpected 'PROPERTYELEMENT' in parse rule 'Element ::= . EmptyElement | (StartElement ElementBody).'.'
    【解决方案3】:

    您正在直接反序列化 xaml 文件,但它引用了作为 CLR 类型编译到 DeclarativeServiceLibrary1 程序集中的类型 (CodeActivity1)。最明显的答案是控制台应用程序在运行时无法使用 DeclarativeServiceLibrary1 程序集。确保将此程序集复制到您正在运行控制台应用程序的文件夹 (\bin\debug) 中,看看是否会有所不同。

    最重要的是,即使您直接读取 xaml 文件,它仍然需要访问它引用的任何类型。

    【讨论】:

    • DeclarativeServiceLibrary1 程序集被复制到控制台应用程序的 bin\debug\ 文件夹中。我什至尝试从控制台应用程序中添加对它的直接引用。
    【解决方案4】:

    通过查看您的代码,我可以看出您应该加载一个 WorkflowService 而不是一个简单的活动。这里基本上有两种选择:

    选项 1:加载 Activity 并将其托管在生成的工作流服务中。

    您可以通过正常加载活动来做到这一点,并使用以下 sn-p 使其成为工作流服务实例:

    WorkflowService service = new WorkflowService();
    service.Body = loadedActivity;
    

    之后,您可以将其托管在工作流服务主机中。

    选项 2:直接加载工作流服务

    第二个选项与您现在的选择没有太大区别。但是,您需要使用 XamlServices 类来加载工作流服务,而不是使用 ActivityXamlServices 类。之后,只需使用您在示例中使用的设置启动工作流服务主机即可。

    【讨论】:

    • 如果我直接实例化 DeclarativeServiceLibrary1.Activity1 并使用它,选项 1 有效,但它仍然不喜欢我从磁盘加载 Xaml。尝试创建 WorkflowServiceHost 时出现原始错误。选项 2 给我一个错误,上面写着“'没有在类型'System.Activities.Activity'上找到匹配的构造函数。您可以使用 Arguments 或 FactoryMethod 指令来构造这种类型。'”一旦我尝试使用加载活动XamlServices.Load 方法。如果您查看我更新的代码,请告诉我,我会发布它。
    • 我已经仔细检查了我的解决方案,但我无法重现您遇到的错误。我尝试了以下代码 sn-p: Activity workflowActivity = (Activity)ActivityXamlServices.Load("Workflow1.xaml"); WorkflowService 服务 = 新 WorkflowService { Body = workflowActivity }; Uri serviceUri = new Uri("localhost:9001/testservice",UriKind.Absolute); WorkflowServiceHost host = new WorkflowServiceHost(service, new Uri[] { serviceUri }); host.Open();
    • 我集成到您的代码中被剪断了,但由于某种原因似乎仍然无法运行。我更新了上面帖子中的代码。我还将我的整个解决方案上传到dntest.criticaltech.com/MyWorkflow.zip。有机会就去看看。谢谢你的帮助,顺便说一句。
    • 在仔细评估了一些情况后,我想我找到了问题所在。如果您构建使用来自同一程序集的 CodeActivity 或 NativeActivity 的工作流,则似乎会发生此错误。让我再检查一些东西,我会尽快回复您。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-02
    • 1970-01-01
    • 2010-10-27
    • 2019-06-24
    相关资源
    最近更新 更多