Expressões C#

A partir do .NET Framework 4.5, há suporte para expressões C# no WF (Windows Workflow Foundation). Novos projetos de fluxo de trabalho C# criados no Visual Studio 2012 direcionados ao .NET Framework 4.5 usam expressões C#, e projetos de fluxo de trabalho do Visual Basic usam expressões do Visual Basic. Os projetos de fluxo de trabalho existentes do .NET Framework 4 que usam expressões do Visual Basic podem ser migrados para o .NET Framework 4.6.1 independentemente da linguagem do projeto e são suportados. Este tópico fornece uma visão geral de expressões C# no WF.

Usando expressões C# em fluxos de trabalho

Usando expressões C# no Designer de Fluxo de Trabalho

A partir do .NET Framework 4.5, há suporte para expressões C# no WF (Windows Workflow Foundation). Os projetos de fluxo de trabalho C# criados no Visual Studio 2012 direcionados ao .NET Framework 4.5 usam expressões C#, enquanto os projetos de fluxo de trabalho Visual Basic usam expressões Visual Basic. Para especificar a expressão desejada C#, digite-a na caixa rotulada Inserir uma expressão C#. Esse rótulo é exibido na janela de propriedades quando a atividade é selecionada no designer ou na atividade no designer de fluxo de trabalho. No exemplo a seguir, duas atividades WriteLine estão contidas no Sequence dentro de NoPersistScope.

Screenshot that shows an automatically created sequence activity.

Observação

As expressões C# têm suporte apenas no Visual Studio e não têm suporte no designer de fluxo de trabalho hospedado novamente. Para obter mais informações sobre os novos recursos do WF45 com suporte no designer re-hospedado, confira Suporte para novos recursos do Workflow Foundation 4.5 no Designer de Fluxo de Trabalho re-hospedado.

Compatibilidade com versões anteriores

Há suporte para expressões do Visual Basic em projetos de fluxo de trabalho .NET Framework 4 C# existentes que foram migrados para .NET Framework 4.6.1. Quando as expressões do Visual Basic são exibidas no designer de fluxo de trabalho, o texto da expressão existente do Visual Basic é substituído por O valor foi definido em XAML, a menos que a expressão do Visual Basic seja uma sintaxe C# válida. Se a expressão do Visual Basic for uma sintaxe C# válida, a expressão será exibida. Para atualizar as expressões do Visual Basic para C#, você poderá editá-las no designer de fluxo de trabalho e especificar a expressão C# equivalente. Não é necessário atualizar as expressões do Visual Basic para C#, mas, assim que as expressões forem atualizadas no designer de fluxo de trabalho, serão convertidas em C# e não poderão ser revertidas para o Visual Basic.

Usando expressões C# em fluxos de trabalho de código

Expressões C# têm suporte em fluxos de trabalho baseados em código .NET Framework 4.6.1, mas antes que o fluxo de trabalho possa ser invocado, as expressões C# devem ser compiladas usando TextExpressionCompiler.Compile. Os autores de fluxo de trabalho podem usar CSharpValue para representar o valor r de uma expressão e CSharpReference para representar o valor l de uma expressão. No exemplo a seguir, um fluxo de trabalho é criado com uma atividade Assign e uma atividade WriteLine contidas em uma atividade Sequence. Um CSharpReference é especificado para o argumento To do Assign, e representa o valor l da expressão. Um CSharpValue é especificado para o argumento Value do Assign e para o argumento Text do WriteLine e representa o valor r para essas duas expressões.

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

Depois que o fluxo de trabalho for criado, as expressões C# serão compiladas chamando o método auxiliar do CompileExpressions e, em seguida, o fluxo de trabalho será chamado. O exemplo a seguir é o método 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);
}

Observação

Se as expressões C# não forem compiladas, um NotSupportedException será gerado quando o fluxo de trabalho for invocado com uma mensagem semelhante à seguinte: Expression Activity type 'CSharpValue1' requer compilação para ser executado. Verifique se o fluxo de trabalho foi compilado.'

Se seu fluxo de trabalho baseado em código personalizado usar DynamicActivity, algumas alterações ao método CompileExpressions serão necessárias, como mostrado no seguinte exemplo de código.

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

Há várias diferenças na sobrecarga do CompileExpressions que cria as expressões C# em uma atividade dinâmica.

  • O parâmetro para CompileExpressions é um DynamicActivity.

  • O nome e o namespace do tipo são recuperados usando a propriedade DynamicActivity.Name.

  • TextExpressionCompilerSettings.ForImplementation é definido como true.

  • CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation é chamado em vez de CompiledExpressionInvoker.SetCompiledExpressionRoot.

Para obter mais informações sobre como trabalhar com expressões no código, confira Criação de fluxos de trabalho, atividades e expressões usando código imperativo.

Usando expressões C# em fluxos de trabalho XAML

As expressões C# têm suporte em fluxos de trabalho XAML. Os fluxos de trabalho XAML compilados são criados em um tipo e os fluxos de trabalho XAML flexíveis são carregados pelo runtime e compilados em uma árvore de atividade quando o fluxo de trabalho é executado.

Xaml compilado

As expressões C# têm suporte nos fluxos de trabalho XAML compilados criados em um tipo como parte de um projeto de fluxo de trabalho de C# destinado para .NET Framework 4.6.1. O XAML compilado é o tipo padrão de criação de fluxo de trabalho no Visual Studio e projetos de fluxo de trabalho em C# criados no Visual Studio destinados ao .NET Framework 4.6.1 usam expressões C#.

Xaml flexível

As expressões C# têm suporte em fluxos de trabalho XAML flexíveis. O programa do host de fluxo de trabalho que carrega e chama o fluxo de trabalho XAML flexível deve ter como destino o .NET Framework 4.6.1 e CompileExpressions deve ser definido como true (o padrão é false). Para definir CompileExpressions como true, crie uma instância ActivityXamlServicesSettings com o conjunto de propriedades CompileExpressions definido como true e passá-lo como um parâmetro para ActivityXamlServices.Load. Se CompileExpressions não está definido como true, uma NotSupportedException será gerada com uma mensagem semelhante à seguinte: Expression Activity type 'CSharpValue1' requer compilação para execução. Verifique se o fluxo de trabalho foi compilado.'

ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
    CompileExpressions = true
};

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

Para obter mais informações sobre como trabalhar com fluxos de trabalho XAML, confira Serializando fluxos de trabalho e atividades de e para XAML.

Usando expressões C# em serviços de fluxo de trabalho do XAMLX

Expressões C# têm suporte em serviços de fluxo de trabalho do XAMLX. Quando um serviço de fluxo de trabalho é hospedado no IIS ou WAS, nenhuma etapa adicional é necessária. Porém, se o serviço de fluxo de trabalho XAML for auto-hospedado, as expressões C# deverão ser compiladas. Para criar expressões C# em um serviço fluxo de trabalho XAMLX auto-hospedado, primeiro carregue o arquivo XAMLX em um WorkflowService e passe o Body do WorkflowService para o método CompileExpressions descrito na seção anterior Usando expressões C# em fluxos de trabalho de código. No exemplo a seguir, um serviço de fluxo de trabalho XAMLX é carregado, as expressões C# são criadas e o serviço do fluxo de trabalho é aberto e aguarda solicitações.

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

Se as expressões C# não forem compiladas, a operação Open terá êxito, mas o fluxo de trabalho falhará quando for chamado. O método CompileExpressions a seguir é o mesmo que o método da seção anterior Usando expressões C# em fluxos de trabalho de código.

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