Поделиться через


Выражения C#

Начиная с .NET Framework 4.5 выражения C# поддерживаются в Windows Workflow Foundation (WF). Новые проекты рабочих процессов C#, созданные в Visual Studio 2012, предназначенные для .NET Framework 4.5, используют выражения C# и проекты рабочих процессов Visual Basic используют выражения Visual Basic. Существующие проекты рабочих процессов .NET Framework 4, использующие выражения Visual Basic, можно перенести в .NET Framework 4.6.1 независимо от языка проекта и поддерживаются. В этом разделе представлен обзор выражений C# в WF.

Использование выражений C# в рабочих процессах

Использование выражений C# в конструкторе рабочих процессов

Начиная с .NET Framework 4.5 выражения C# поддерживаются в Windows Workflow Foundation (WF). Проекты рабочих процессов C#, созданные в Visual Studio 2012, предназначенные для .NET Framework 4.5, используют выражения C#, а проекты рабочих процессов Visual Basic используют выражения Visual Basic. Чтобы указать требуемое выражение C#, введите его в поле с меткой ВВОД выражения C#. Эта метка отображается в окне свойств, когда действие выбрано в конструкторе, или на действии в конструкторе рабочих процессов. В следующем примере два WriteLine действия содержатся внутри Sequence и NoPersistScope.

Снимок экрана, показывающий автоматически созданное действие последовательности.

Замечание

Выражения C# поддерживаются только в Visual Studio и не поддерживаются в повторно размещенном конструкторе рабочих процессов. Дополнительные сведения о новых функциях WF45, поддерживаемых в повторно размещенном конструкторе, см. в разделе "Поддержка новых функций Workflow Foundation 4.5" в конструкторе повторно размещенных рабочих процессов.

Обратная совместимость

Поддерживаются выражения Visual Basic в существующих проектах рабочих процессов .NET Framework 4 C#, перенесенных в .NET Framework 4.6.1. При просмотре выражений Visual Basic в конструкторе рабочих процессов текст существующего выражения Visual Basic заменяется на Значение задано в XAML, если выражение Visual Basic не соответствует допустимому синтаксису C#. Если выражение Visual Basic является допустимым синтаксисом C#, отображается выражение. Чтобы обновить выражения Visual Basic на C#, их можно изменить в конструкторе рабочих процессов и указать эквивалентное выражение C#. Для обновления выражений Visual Basic до C# не требуется, но после обновления выражений в конструкторе рабочих процессов они преобразуются в C# и не могут быть возвращены в Visual Basic.

Использование выражений C# в рабочих процессах кода

Выражения C# поддерживаются в рабочих процессах на основе кода .NET Framework 4.6.1, но перед вызовом рабочего процесса выражения C# необходимо скомпилировать с помощью TextExpressionCompiler.Compile. Авторы рабочих процессов могут использовать CSharpValue для представления r-значения выражения и CSharpReference представления l-значения выражения. В следующем примере создается рабочий процесс с действием Assign, а действие WriteLine содержится в действии Sequence. Указан CSharpReference для аргумента To в Assign, и он представляет l-значение выражения. Для CSharpValue аргумента Value и для Assign аргумента Text указано значение WriteLine, которое представляет r-значение для этих двух выражений.

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);

После создания рабочего процесса выражения C# компилируются путем вызова вспомогательного CompileExpressions метода, а затем вызывается рабочий процесс. В следующем примере используется 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);
}

Существует несколько различий в перегрузке CompileExpressions , которая компилирует выражения C# в динамическом действии.

  • Параметр для CompileExpressions является DynamicActivity.

  • Имя типа и пространство имен извлекаются с помощью DynamicActivity.Name свойства.

  • TextExpressionCompilerSettings.ForImplementation задан как true.

  • CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation вызывается вместо CompiledExpressionInvoker.SetCompiledExpressionRoot.

Дополнительные сведения о работе с выражениями в коде см. в статье "Создание рабочих процессов, действий и выражений с помощью императивного кода".

Использование выражений C# в рабочих процессах XAML

Выражения C# поддерживаются в рабочих процессах XAML. Скомпилированные рабочие процессы XAML компилируются в тип, и свободные рабочие процессы XAML загружаются средой выполнения и компилируются в дерево действий при выполнении рабочего процесса.

Скомпилированный Xaml

Выражения C# поддерживаются в скомпилированных рабочих процессах XAML, скомпилированных в тип в рамках проекта рабочего процесса C#, предназначенного для .NET Framework 4.6.1. Скомпилированный XAML — это тип разработки рабочих процессов по умолчанию в Visual Studio, а проекты рабочих процессов C#, созданные в Visual Studio, предназначенные для .NET Framework 4.6.1, используют выражения C#.

Динамический XAML

Выражения C# поддерживаются в свободных рабочих процессах XAML. Программа хоста рабочего процесса, загружающая и вызывающая свободный рабочий процесс 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 и из нее.

Использование выражений C# в службах рабочих процессов XAMLX

Выражения C# поддерживаются в службах рабочих процессов XAMLX. Если служба рабочего процесса размещена в службах IIS или WAS, то никаких дополнительных шагов не требуется, но если служба рабочего процесса XAML размещена самостоятельно, необходимо скомпилировать выражения C#. Чтобы скомпилировать выражения C# в автономной службе рабочих процессов XAMLX, сначала загрузите XAMLX-файл в WorkflowService, а затем передайте метод Body в WorkflowServiceCompileExpressions, описанный в предыдущем разделе "Использование выражений C# в рабочих процессах кода". В следующем примере загружается служба рабочего процесса 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);
}