C# 運算式

Windows Workflow Foundation (WF) 從 .NET Framework 4.5 開始支援 C# 運算式。 在 Visual Studio 2012 中建立,且目標為 .NET Framework 4.5 的新 C# 工作流程專案使用 C# 運算式,Visual Basic 工作流程專案則使用 Visual Basic 運算式。 不論採用何種專案語言,使用 Visual Basic 運算式的現有 .NET Framework 4 工作流程專案均可移轉至 .NET Framework 4.6.1 且皆受到支援。 本主題提供 WF 中的 C# 運算式概觀。

在工作流程中使用 C# 運算式

在工作流程設計工具中使用 C# 運算式

Windows Workflow Foundation (WF) 從 .NET Framework 4.5 開始支援 C# 運算式。 在 Visual Studio 2012 中建立,且目標為 .NET Framework 4.5 的新 C# 工作流程專案使用 C# 運算式,Visual Basic 工作流程專案則使用 Visual Basic 運算式。 若要指定想要的 C# 運算式,請在標示為 [輸入 C# 運算式] 的方塊中輸入運算式。 在設計工具中選取活動時,會在屬性視窗中顯示此標籤,而此標籤也會顯示在工作流程設計工具中的活動之上。 在下列範例中,WriteLine 內的 Sequence 包含兩個 NoPersistScope 活動。

Screenshot that shows an automatically created sequence activity.

注意

只有 Visual Studio 支援 C# 運算式,重新裝載的工作流程設計工具則不支援。 關於重新裝載設計工具支援的新 WF45 功能詳細資訊,請參閱重新裝載工作流程設計工具中的新 Workflow Foundation 4.5 功能支援

回溯相容性

支援已移轉至 .NET Framework 4.6.1 的現有 .NET Framework 4 C# 工作流程專案中的 Visual Basic 運算式。 在工作流程設計工具中檢視 Visual Basic 運算式時,除非現有 Visual Basic 運算式為有效的 C# 語法,否則在 XAML 中設定的值會取代運算式的內容。 如果 Visual Basic 運算式為有效的 C# 語法,則會顯示該運算式。 若要將 Visual Basic 運算式更新為 C#,您可以在工作流程設計工具中編輯這些運算式,並指定相等的 C# 運算式。 您不需要將 Visual Basic 運算式更新為 C#,不過一旦這些運算式在工作流程設計工具中更新,將會轉換為 C#,且可能無法還原為 Visual Basic。

在程式碼工作流程中使用 C# 運算式

.NET Framework 4.6.1 程式碼工作流程支援 C# 運算式,但必須使用 TextExpressionCompiler.Compile 編譯 C# 運算式,工作流程才能叫用運算式。 工作流程作者可以使用 CSharpValue 來表示運算式的右值 (r-value),並使用 CSharpReference 來表示運算式的左值 (l-value)。 下列範例使用 Assign 活動內含的 WriteLine 活動和 Sequence 活動,來建立工作流程。 該範例為 CSharpReferenceTo 引數,指定一個 Assign,表示運算式的左值。 CSharpValueValue 引數和 AssignText 引數指定一個 WriteLine,表示這兩個運算式的右值。

Variable<int> n = new Variable<int>
{
    Name = "n"
};

Activity wf = new Sequence
{
    Variables = { n },
    Activities =
    {
        new Assign<int>
        {
            To = new CSharpReference<int>("n"),
            Value = new CSharpValue<int>("new Random().Next(1, 101)")
        },
        new WriteLine
        {
            Text = new CSharpValue<string>("\"The number is \" + n")
        }
    }
};

CompileExpressions(wf);

WorkflowInvoker.Invoke(wf);

建構工作流程之後,會呼叫 CompileExpressions Helper 方法來編譯 C# 運算式,然後叫用工作流程。 以下範例是 CompileExpressions 方法。

static void CompileExpressions(Activity activity)
{
    // activityName is the Namespace.Type of the activity that contains the
    // C# expressions.
    string activityName = activity.GetType().ToString();

    // Split activityName into Namespace and Type.Append _CompiledExpressionRoot to the type name
    // to represent the new type that represents the compiled expressions.
    // Take everything after the last . for the type name.
    string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
    // Take everything before the last . for the namespace.
    string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());

    // Create a TextExpressionCompilerSettings.
    TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
    {
        Activity = activity,
        Language = "C#",
        ActivityName = activityType,
        ActivityNamespace = activityNamespace,
        RootNamespace = null,
        GenerateAsPartialClass = false,
        AlwaysGenerateSource = true,
        ForImplementation = false
    };

    // Compile the C# expression.
    TextExpressionCompilerResults results =
        new TextExpressionCompiler(settings).Compile();

    // Any compilation errors are contained in the CompilerMessages.
    if (results.HasErrors)
    {
        throw new Exception("Compilation failed.");
    }

    // Create an instance of the new compiled expression type.
    ICompiledExpressionRoot compiledExpressionRoot =
        Activator.CreateInstance(results.ResultType,
            new object[] { activity }) as ICompiledExpressionRoot;

    // Attach it to the activity.
    CompiledExpressionInvoker.SetCompiledExpressionRoot(
        activity, compiledExpressionRoot);
}

注意

如果未編譯 C# 運算式,叫用工作流程時會擲回 NotSupportedException,並顯示類似下面的訊息:Expression Activity type 'CSharpValue1「必須編譯才能執行。 請確定工作流程已編譯。」

如果您的自訂程式碼為主之工作流程使用 DynamicActivity,則需要對 CompileExpressions 方法進行一些變更,如下列程式碼範例所示。

static void CompileExpressions(DynamicActivity dynamicActivity)
{
    // activityName is the Namespace.Type of the activity that contains the
    // C# expressions. For Dynamic Activities this can be retrieved using the
    // name property , which must be in the form Namespace.Type.
    string activityName = dynamicActivity.Name;

    // Split activityName into Namespace and Type.Append _CompiledExpressionRoot to the type name
    // to represent the new type that represents the compiled expressions.
    // Take everything after the last . for the type name.
    string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
    // Take everything before the last . for the namespace.
    string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());

    // Create a TextExpressionCompilerSettings.
    TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
    {
        Activity = dynamicActivity,
        Language = "C#",
        ActivityName = activityType,
        ActivityNamespace = activityNamespace,
        RootNamespace = null,
        GenerateAsPartialClass = false,
        AlwaysGenerateSource = true,
        ForImplementation = true
    };

    // Compile the C# expression.
    TextExpressionCompilerResults results =
        new TextExpressionCompiler(settings).Compile();

    // Any compilation errors are contained in the CompilerMessages.
    if (results.HasErrors)
    {
        throw new Exception("Compilation failed.");
    }

    // Create an instance of the new compiled expression type.
    ICompiledExpressionRoot compiledExpressionRoot =
        Activator.CreateInstance(results.ResultType,
            new object[] { dynamicActivity }) as ICompiledExpressionRoot;

    // Attach it to the activity.
    CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation(
        dynamicActivity, compiledExpressionRoot);
}

在動態活動中編譯 C# 運算式的 CompileExpressions 多載有一些差異。

  • 傳給 CompileExpressions 的參數為 DynamicActivity

  • 擷取型別名稱和命名空間需使用 DynamicActivity.Name 屬性。

  • TextExpressionCompilerSettings.ForImplementation 設定為 true

  • 呼叫 CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation,而不是 CompiledExpressionInvoker.SetCompiledExpressionRoot

如需有關在程式碼中使用運算式的詳細資訊,請參閱使用命令式程式碼撰寫工作流程、活動和運算式

在 XAML 工作流程中使用 C# 運算式

XAML 工作流程支援 C# 運算式。 編譯的 XAML 工作流程會編譯為型別,而鬆散的 XAML 工作流程會在工作流程執行時,由執行階段載入並編譯為活動樹狀。

編譯的 XAML

經過編譯的 XAML 工作流程支援 C# 運算式。該工作流程編譯而成的型別隨附於目標為 .NET Framework 4.6.1 的 C# 工作流程專案。 經過編譯的 XAML 是在 Visual Studio 中製作之工作流程的型別。在 Visual Studio 中建立且目標為 .NET Framework 4.6.1 的 C# 工作流程專案使用 C# 運算式。

鬆散的 XAML

鬆散的 XAML 工作流程支援 C# 運算式。 載入及叫用鬆散 XAML 工作流程的工作流程主機程式,必須以 .NET Framework 4.6.1 為目標,且 CompileExpressions 必須設為 true (預設值為 false)。 若要將 CompileExpressions 設定為 true,請建立 ActivityXamlServicesSettings 執行個體並將其 CompileExpressions 屬性設定為 true,然後當做參數傳給 ActivityXamlServices.Load。 如果 CompileExpressions 並非設為 true,會擲回 NotSupportedException 並顯示類似下面的訊息:Expression Activity type 'CSharpValue1「需要編譯才能執行。 請確定工作流程已編譯。」

ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
    CompileExpressions = true
};

DynamicActivity<int> wf = ActivityXamlServices.Load(new StringReader(serializedAB), settings) as DynamicActivity<int>;

如需有關 XAML 工作流程的詳細資訊,請參閱將來自 XAML 和傳送至 XAML 的工作流程及活動序列化

在 XAMLX 工作流程服務中使用 C# 運算式

XAMLX 工作流程服務支援 C# 運算式。 如果工作流程服務是以 IIS 或 WAS 裝載,則不需要其他步驟;但是,如果 XAML 工作流程服務為自我裝載,則必須編譯 C# 運算式。 若要在自行裝載 XAMLX 工作流程服務中編譯 C# 運算式,請先將 XAMLX 檔案載入 WorkflowService,然後再將 WorkflowServiceBody 傳遞至上一節<在程式碼工作流程中使用 C# 運算式>所述的 CompileExpressions 方法。 下列範例將載入 XAMLX 工作流程服務、編譯 C# 運算式,然後開啟工作流程服務並等候要求。

// Load the XAMLX workflow service.
WorkflowService workflow1 =
    (WorkflowService)XamlServices.Load(xamlxPath);

// Compile the C# expressions in the workflow by passing the Body to CompileExpressions.
CompileExpressions(workflow1.Body);

// Initialize the WorkflowServiceHost.
var host = new WorkflowServiceHost(workflow1, new Uri("http://localhost:8293/Service1.xamlx"));

// Enable Metadata publishing/
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);

// Open the WorkflowServiceHost and wait for requests.
host.Open();
Console.WriteLine("Press enter to quit");
Console.ReadLine();

如果未編譯 C# 運算式,Open 作業會成功,但工作流程在叫用時會失敗。 下列 CompileExpressions 方法與上一節<在程式碼工作流程中使用 C# 運算式>提及的方法相同。

static void CompileExpressions(Activity activity)
{
    // activityName is the Namespace.Type of the activity that contains the
    // C# expressions.
    string activityName = activity.GetType().ToString();

    // Split activityName into Namespace and Type.Append _CompiledExpressionRoot to the type name
    // to represent the new type that represents the compiled expressions.
    // Take everything after the last . for the type name.
    string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
    // Take everything before the last . for the namespace.
    string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());

    // Create a TextExpressionCompilerSettings.
    TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
    {
        Activity = activity,
        Language = "C#",
        ActivityName = activityType,
        ActivityNamespace = activityNamespace,
        RootNamespace = null,
        GenerateAsPartialClass = false,
        AlwaysGenerateSource = true,
        ForImplementation = false
    };

    // Compile the C# expression.
    TextExpressionCompilerResults results =
        new TextExpressionCompiler(settings).Compile();

    // Any compilation errors are contained in the CompilerMessages.
    if (results.HasErrors)
    {
        throw new Exception("Compilation failed.");
    }

    // Create an instance of the new compiled expression type.
    ICompiledExpressionRoot compiledExpressionRoot =
        Activator.CreateInstance(results.ResultType,
            new object[] { activity }) as ICompiledExpressionRoot;

    // Attach it to the activity.
    CompiledExpressionInvoker.SetCompiledExpressionRoot(
        activity, compiledExpressionRoot);
}