Expresiones de C#

A partir de .NET Framework 4.5, se admiten las expresiones de C# en Windows Workflow Foundation (WF). Los nuevos proyectos de flujo de trabajo de C# creados en Visual Studio 2012 que tienen como destino .NET Framework 4.5 usan expresiones de C#, mientras que los proyectos de flujo de trabajo de Visual Basic usan expresiones de Visual Basic. Los proyectos de flujo de trabajo existentes de .NET Framework 4 que usan expresiones de Visual Basic se pueden migrar a .NET Framework 4.6.1 independientemente del lenguaje del proyecto y son compatibles. En este tema, se proporciona información general sobre las expresiones de C# en WF.

Uso de expresiones de C# en los flujos de trabajo

Usar expresiones de C# en el diseñador de flujo de trabajo

A partir de .NET Framework 4.5, se admiten las expresiones de C# en Windows Workflow Foundation (WF). Los proyectos de flujo de trabajo de C# creados en Visual Studio 2012 que tienen como destino .NET Framework 4.5 usan expresiones de C#, mientras que los proyectos de flujo de trabajo de Visual Basic usan expresiones de Visual Basic. Para especificar la expresión de C# deseada, escríbala en el cuadro con la etiqueta Escriba una expresión de C#. Esta etiqueta se muestra en la ventana de propiedades cuando se selecciona la actividad en el diseñador o en la actividad en el diseñador de flujo de trabajo. En el ejemplo siguiente, se incluyen dos actividades WriteLine en un Sequence dentro de NoPersistScope.

Screenshot that shows an automatically created sequence activity.

Nota:

Las expresiones de C# solo se admiten en Visual Studio y no en el diseñador de flujos de trabajo rehospedado. Para obtener más información sobre las nuevas características de WF45 admitidas en el diseñador rehospedado, consulte Compatibilidad con las nuevas características de Workflow Foundation 4.5 en el diseñador de flujo de trabajo rehospedado.

Compatibilidad con versiones anteriores

Se admiten expresiones de Visual Basic en proyectos de flujo de trabajo de C# de .NET Framework 4 existentes que se han migrado a .NET Framework 4.6.1. Cuando se visualizan las expresiones de Visual Basic en el diseñador de flujos de trabajo, el texto de la expresión de Visual Basic existente se reemplaza por El valor se estableció en XAML, a menos que la expresión de Visual Basic pertenezca a la sintaxis válida de C#. Si la expresión Visual Basic corresponde a una sintaxis de C# válida, se muestra la expresión. Para actualizar las expresiones de Visual Basic a C#, puede editarlas en el diseñador de flujo de trabajo y especificar la expresión equivalente en C#. No es necesario actualizar las expresiones de Visual Basic a C#, pero una vez las expresiones están actualizadas en el diseñador de flujo de trabajo, se convierten a C# y no pueden volver a Visual Basic.

Usar expresiones de C# en flujos de trabajo de código

Las expresiones de C# se admiten en los flujos de trabajo basados en código de .NET Framework 4.6.1, pero para poder invocar el flujo de trabajo, es necesario compilar las expresiones de C# mediante TextExpressionCompiler.Compile. Los autores de flujo de trabajo pueden usar CSharpValue para representar el valor r de una expresión y CSharpReference para representar el valor l de una expresión. En el siguiente ejemplo, se crea un flujo de trabajo con una actividad Assign y una actividad WriteLine incluida en una actividad Sequence. Se especifica CSharpReference para el argumento To de Assign y representa el valor l de la expresión. Se especifica CSharpValue para el argumento de Value de Assign, y para el argumento Text de WriteLine, y representa el valor r para esas dos expresiones.

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

Cuando ya se ha creado el flujo de trabajo, se compilan las expresiones de C# mediante una llamada al método del asistente CompileExpressions y, a continuación, se invoca el flujo de trabajo. El siguiente ejemplo es el 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);
}

Nota:

Si las expresiones de C# no se compilan, se produce una excepción NotSupportedException al invocar el flujo de trabajo con un mensaje similar al siguiente: Expression Activity type 'CSharpValue1" requiere compilación para ejecutarse. Asegúrese de que el flujo de trabajo se haya compilado".

Si el flujo de trabajo personalizado basado en código usa DynamicActivity, son necesarios algunos cambios en el método CompileExpressions, tal como se muestra en el ejemplo de código siguiente.

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

Hay varias diferencias en la sobrecarga de CompileExpressions que compila expresiones de C# en una actividad dinámica.

  • El parámetro en CompileExpressions es un objeto DynamicActivity.

  • El nombre de tipo y el espacio de nombres se recuperan mediante la propiedad DynamicActivity.Name.

  • TextExpressionCompilerSettings.ForImplementation se establece en true.

  • Se llama a CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation en lugar de a CompiledExpressionInvoker.SetCompiledExpressionRoot.

Para obtener más información sobre el uso de expresiones en el código, consulte Creación de flujos de trabajo, actividades y expresiones mediante código imperativo.

Usar expresiones de C# en flujos de trabajo de XAML

Las expresiones de C# se admiten en los flujos de trabajo de XAML. Los flujos de trabajo de XAML compilados se compilan en un tipo, mientras que el runtime carga los flujos de trabajo de XAML dinámico que se compilan en un árbol de actividades cuando se ejecuta el flujo de trabajo.

XAML compilado

Las expresiones de C# se admiten en flujos de trabajo de XAML compilado que se compilan en un tipo como parte de un proyecto de flujo de trabajo de C# que tiene como destino .NET Framework 4.6.1. XAML compilado es el tipo predeterminado de creación de flujos de trabajo en Visual Studio y los proyectos de flujo de trabajo de C# creados en Visual Studio que tienen como destino .NET Framework 4.6.1 usan expresiones de C#.

XAML dinámico

Las expresiones de C# se admiten en los flujos de trabajo de XAML dinámico. El programa host del flujo de trabajo que carga e invoca el flujo de trabajo de XAML flexible debe tener como destino .NET Framework 4.6.1 y CompileExpressions debe estar establecido en true (el valor predeterminado es false). Para establecer CompileExpressions en true, cree una instancia de ActivityXamlServicesSettings con la propiedad CompileExpressions establecida en true y pásela como un parámetro a ActivityXamlServices.Load. Si CompileExpressions no se establece en true, se producirá un mensaje NotSupportedException similar al siguiente: Expression Activity type 'CSharpValue1" requiere compilación para ejecutarse. Asegúrese de que el flujo de trabajo se haya compilado".

ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
    CompileExpressions = true
};

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

Para más información sobre cómo trabajar con flujos de trabajo XAML, consulte Serialización de flujos de trabajo y actividades entre XAML.

Usar expresiones de C# en servicios de flujo de trabajo de XAMLX

Las expresiones de C# se admiten en servicios de flujo de trabajo de XAMLX. Cuando un servicio de flujo de trabajo se hospeda en IIS o WAS, no son necesarios pasos adicionales, pero si el servicio de flujo de trabajo de XAML es autohospedado, las expresiones de C# deben compilarse. Para compilar expresiones de C# en un servicio de flujo de trabajo de XAMLX autohospedado, primero cargue el archivo XAMLX en un objeto WorkflowService y, a continuación, transfiera el elemento Body de WorkflowService al método CompileExpressions descrito en la sección anterior, Uso de expresiones de C# en flujos de trabajo de código. En el ejemplo siguiente, hay un servicio de flujo de trabajo de XAMLX cargado, las expresiones de C# están compiladas y el servicio de flujo de trabajo está abierto y espera solicitudes.

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

Si las expresiones de C# no están compiladas, la operación Open se realiza correctamente pero el flujo de trabajo producirá un error al invocarse. El siguiente método CompileExpressions es igual que el método de la sección anterior, Uso de expresiones de C# en flujos de trabajo 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);
}