Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp 2008 And The .NET 3.5 Platform [eng]-1

.pdf
Скачиваний:
34
Добавлен:
16.08.2013
Размер:
22.5 Mб
Скачать

912 CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

This interface defines three methods, one of which returns an array of the (yet-to-be-created)

InventoryRecord type. You may recall that the GetInventory() method of InventoryDAL simply returned a DataTable object, which might make you question why our service’s GetInventory() method does not do the same.

While it would work to return a DataTable from a WCF service method, recall that WCF was built to honor the use of SOA principles, one of which is to program against contracts, not implementations. Therefore, rather than returning the .NET-specific DataTable type to an external caller, we will return a custom data contract (InventoryRecord) that will be correctly expressed in the contained WSDL document in an agnostic manner.

Also note that this interface defines an overloaded method named InsertCar(). The first version takes four incoming parameters, while the second version takes an InventoryRecord type as input. This data contract can be defined as so:

[DataContract]

public class InventoryRecord

{

[DataMember] public int ID; [DataMember] public string Make; [DataMember]

public string Color; [DataMember]

public string PetName;

}

If you were to implement this interface as it now stands, build a host, and attempt to call these methods from a client, you might be surprised to find a runtime exception. The reason has to do with the fact that one of the requirements of a WSDL description is that each method exposed from a given endpoint is uniquely named. Thus, while method overloading works just fine as far as C# is concerned, the current web service specifications do not permit two identically named InsertCar() methods.

Thankfully, the [OperationContract] attribute supports a named property (Name) that allows you to specify how the C# method will be represented within a WSDL description. Given this, update the second version of InsertCar() as so:

public interface IAutoLotService

{

...

[OperationContract(Name = "InsertCarWithDetails")] void InsertCar(InventoryRecord car);

}

Implementing the Service Contract

The AutoLotService type implements this interface as follows (be sure to import the

AutoLotConnectedLayer and System.Data namespaces in this code file):

using AutoLotConnectedLayer; using System.Data;

public class AutoLotService : IAutoLotService

{

private const string ConnString =

@"Data Source=(local)\SQLEXPRESS;Initial Catalog=AutoLot"+ ";Integrated Security=True";

CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

913

public void InsertCar(int id, string make, string color, string petname)

{

InventoryDAL d = new InventoryDAL(); d.OpenConnection(ConnString); d.InsertAuto(id, color, make, petname); d.CloseConnection();

}

public void InsertCar(InventoryRecord car)

{

InventoryDAL d = new InventoryDAL(); d.OpenConnection(ConnString);

d.InsertAuto(car.ID, car.Color, car.Make, car.PetName); d.CloseConnection();

}

public InventoryRecord[] GetInventory()

{

//First, get the DataTable from the database.

InventoryDAL d = new InventoryDAL(); d.OpenConnection(ConnString); DataTable dt = d.GetAllInventory(); d.CloseConnection();

//Now make a List<T> to contain the records.

List<InventoryRecord> records = new List<InventoryRecord>();

//Copy the data table into List<> of custom contracts.

DataTableReader reader = dt.CreateDataReader(); while (reader.Read())

{

InventoryRecord r = new InventoryRecord(); r.ID = (int)reader["CarID"];

r.Color = ((string)reader["Color"]).Trim(); r.Make = ((string)reader["Make"]).Trim(); r.PetName = ((string)reader["PetName"]).Trim(); records.Add(r);

}

//Transform List<T> to array of InventoryRecord types. return (InventoryRecord[])records.ToArray();

}

}

Not too much to say here. For simplicity, we are hard-coding the connection string value (which you may need to adjust based on your machine settings) rather than storing it in our Web.config file. Given that our data access library does all the real work of communicating with the AutoLot database, all we need to do is pass the incoming parameters to the InsertAuto() method of the InventoryDAL class type. The only other point of interest is the act of mapping the DataTable object’s values into a generic list of InventoryRecord types (using a DataTableReader to do so) and then transforming the List<T> into an array of InventoryRecord types.

The Role of the *.svc File

When you create a web-centric WCF service, you will find your project contains a specific file with a *.svc file extension. This particular file is required for any WCF service hosted by IIS; it describes

914CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

the name and location of the service implementation within the install point. Because we have changed the names of our starter files and WCF types, we must now update the contents of the

Service.svc file as so:

<%@ ServiceHost Language="C#" Debug="true"

Service="AutoLotService" CodeBehind="~/App_Code/AutoLotService.cs" %>

Updating the Web.config File

Before we can take this service out for a test drive, the final task is to update the <system. serviceModel> section of the Web.config file. As described in more detail during our examination of ASP.NET later in this book, the Web.config file serves a similar purpose to an executable’s *.config file; however, it also controls a number of web-specific settings. For this example, all we need to do is update the WCF-specific section of the file as follows:

<system.serviceModel>

<services>

<service name="AutoLotService" behaviorConfiguration="ServiceBehavior"> <endpoint address="" binding="wsHttpBinding" contract="IAutoLotService"/>

<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>

</service>

</services>

<behaviors>

<serviceBehaviors>

<behavior name="ServiceBehavior"> <serviceMetadata httpGetEnabled="true"/>

<serviceDebug includeExceptionDetailInFaults="false"/> </behavior>

</serviceBehaviors>

</behaviors>

</system.serviceModel>

Testing the Service

Now you are free to build any sort of client to test your service, including passing in the endpoint of the *.svc file to the WcfTestClient.exe application:

WcfTestClient http://localhost/AutoLotWCFService/Service.svc

Consider Figure 25-21, which illustrates the result of invoking GetInventory().

Source Code The AutoLotService project is located under the Chapter 25 subdirectory.

CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

915

Figure 25-21. Creating a web-centric WCF service

If you wish to build a custom client application, simply use the Add Service Reference dialog box as you did for the MagicEightBallServiceClient and MathClient project examples earlier in this chapter.

Summary

This chapter introduced you to the Windows Communication Foundation (WCF) API, which has been part of the base class libraries since .NET 3.0. As explained, the major motivation behind WCF is to provide a unified object model that exposes a number of (previously unrelated) distributed computing APIs under a single umbrella. Furthermore, as you saw at the onset of this chapter, a WCF service is represented by specified addresses, bindings, and contracts (easily remembered by the friendly abbreviation ABC).

As you have seen, a typical WCF application involves the use of three interrelated assemblies. The first assembly defines the service contracts and service types that represent the services functionality. This assembly is then hosted by a custom executable, an IIS virtual directory, or a Windows service. Finally, the client assembly makes use of a generated code file defining a proxy type (and settings within the application configuration file) to communicate with the remote type.

The chapter also examined using a number of WCF programming tools such as

SvcConfigEditor.exe (which allows you to modify *.config files), the WcfTestClient.exe application (to quickly test a WCF service), and various Visual Studio 2008 WCF project templates.

C H A P T E R 2 6

Introducing Windows Workflow

Foundation

.NET 3.0 shipped with a particular programming framework named Windows Workflow Foundation (WF). This API allows you to model, configure, monitor, and execute the workflows (which, for the time being, can simply be regarded as a collection of related tasks) used internally by a given application. The out-of-the-box solution provided by WF is a huge benefit when building software, as we are no longer required to manually develop complex infrastructure to support workflowenabled applications.

This chapter begins by defining the role of business processes and describes how they relate to the WF API. As well, you will be exposed to the concept of a WF “activity,” the two major flavors of workflows (sequential and state machine), and various WF assemblies, project templates, and programming tools. Once we’ve covered the basics, we’ll build several example programs that illustrate how to leverage the WF programming model to establish business processes that execute under the watchful eye of the WF runtime engine.

Note The entirety of WF cannot be covered in a single introductory chapter. If you require a deeper treatment of the topic than presented here, check out Pro WF: Windows Workflow in .NET 3.0 by Bruce Bukovics (Apress, 2007).

Defining a Business Process

Any real-world application must be able to model various business processes. Simply put, a business process is a conceptual grouping of tasks that logically work as a collective whole. For example, assume you are building an application that allows a user to purchase an automobile online. Once the user submits the order, a large number of activities are set in motion. We might begin by performing a credit check. If the user passes our credit verification, we might start a database transaction in order to remove the entry from an Inventory table, add a new entry to an Orders table, and update the customer account information. After the database transaction has completed, we still might need to send a confirmation e-mail to the buyer, and then invoke a remote XML web service to place the order at the dealership. Collectively, all of these tasks could represent a single business process.

Historically speaking, modeling a business process was yet another detail that programmers had to account for, often by authoring custom code to ensure that a business process was not only modeled correctly, but also executed correctly within the application itself. For example, you may need to author code to account for points of failure, tracing, and logging support (to see what a given business process is up to); persistence support (to save the state of long-running processes);

917

918CHAPTER 26 INTRODUCING WINDOWS WORKFLOW FOUNDATION

and whatnot. As you may know firsthand, building this sort of infrastructure from scratch entails a great deal of time and manual labor.

Assuming that a development team did, in fact, build a custom business process framework for their applications, their work was not yet complete. Simply put, a raw C# code base cannot be easily explained to nonprogrammers on the team who also need to understand the business process. The truth of the matter is that subject matter experts (SMEs), managers, salespeople, and members of a graphical design team often do not speak the language of code. Given this, we were required to make use of other modeling tools (such as Microsoft Visio) to graphically represent our processes using skill set–neutral terms. The obvious problem here is we now have two entities to keep in sync: If we change the code, we need to update the diagram. If we change the diagram, we need to update the code.

Furthermore, when building a sophisticated software application using the 100% code approach, the code base has very little trace of the internal “flow” of the application. For example, a typical .NET program might be composed of hundreds of custom types (not to mention the numerous types used by the base class libraries). While programmers may have a feel for which objects are making calls on other objects, the code itself is a far cry from a living document that explains the overall sequence of activity. While the development team may build external documentation and workflow charts, again we run into the problem of multiple representations of the same process.

The Role of WF

Since the release of .NET 3.0, we were provided with the Windows Workflow Foundation API. In essence, WF allows programmers to declaratively design business processes using a prefabricated set of activities. Thus, rather than building a custom set of assemblies to represent a given business activity and the necessary infrastructure, we can make use of the WF designers of Visual Studio 2008 to create our business process at design time. In this respect, WF allows us to build the skeleton of a business process, which can be fleshed out through code.

When programming with the WF API, a single entity can then be used to represent the overall business process as well as the code that defines it. Since a single WF document is used to represent the code driving the process in addition to being a friendly visual representation of the process, we no longer need to worry about multiple documents falling out of sync. Better yet, this WF document will clearly illustrate the process itself.

The Building Blocks of WF

As you build a workflow-enabled application, you will undoubtedly notice that it “feels different” from building a typical .NET application. For example, up until this point in the text, every code example began by creating a new project workspace (most often a Console Application project) and involved authoring code to represent the program at large. A WF application also consists of custom code; however, in addition, you are building directly into the assembly the business process itself. Consider Figure 26-1, which illustrates the initial workflow diagram generated by Visual Studio 2008 when selecting a new Sequential Workflow Console Application project workspace.

Using this designer (and the various WF-centric tools integrated into Visual Studio) you are able to model your process and eventually author code to execute it under the appropriate circumstances. You’ll examine these tools in more detail a bit later in this chapter.

Understand that WF is far more than a pretty designer that allows you to model the activities of a business process. As you are building your WF diagram, the designer tools are authoring code to represent the skeleton of your process. Thus, the first thing to be aware of is that a visual WF diagram is executable code, not just simply a Visio-like static image. As such, WF is represented by a set of .NET assemblies, namespaces, and types, just like any other .NET technology.

CHAPTER 26 INTRODUCING WINDOWS WORKFLOW FOUNDATION

919

Figure 26-1. An empty sequential workflow diagram designer

The WF Runtime

Given the fact that a WF diagram equates to real types and custom code, the next thing to understand is that the WF API also consists of a runtime engine to load, execute, unload, and in other ways manipulate a workflow process. The WF runtime engine can be hosted within any .NET application domain; however, be aware that a single application domain can only have one running instance of the WF engine.

Recall from Chapter 17 that an AppDomain is a partition within a Win32 process that plays host to a .NET application and any external code libraries. As such, the WF engine can be embedded within a simple console program, a GUI desktop application (Windows Forms or Windows Presentation Foundation [WPF]), or exposed from a WCF service or XML web service.

If you are modeling a business process that needs to be used by a wide variety of systems, you also have the option of authoring your WF within a C# Class Library project. In this way, new applications can simply reference your *.dll to reuse a predefined collection of business processes. This is obviously helpful in that you would not want to have to re-create the same WF multiple times.

In any case, at this point understand that the WF API provides a full-blown object model that allows you to programmatically interact with the runtime engine as well as the workflows you have designed.

The Core Services of WF

In addition to designer tools, activities, and a runtime engine, WF provides a set of out-of-the-box services that complete the overall framework of a workflow-enabled application. Using these services we can “inherit” a good deal of commonly required WF infrastructure, rather than having to commit time and resources to build this infrastructure by hand. Table 26-1 documents the intrinsic services baked into the WF API.

Collectively, the four intrinsic services seen in Table 26-1 are termed the core services. The WF APIs provide default implementations of each of these services, two of which are mandatory (scheduling and transactions), while tracking and persistence services are optional and not registered with the runtime by default. While the .NET base class libraries do provide types that support each core service, you are able to exchange them with your own custom implementations. Thus, if you wish to customize the way in which a long-running workflow should be persisted, you can do so. As well, if you wish to extend the basic functionality of a service with new functionality, it is possible to do so.

920 CHAPTER 26 INTRODUCING WINDOWS WORKFLOW FOUNDATION

Table 26-1. Intrinsic Services of WF

Services

Meaning in Life

Persistence services

This feature allows you to save a WF instance to an external source (such

 

as a database). This can be useful if a long-running business process will

 

be idle for some amount of time.

Transaction services

WF instances can be monitored in a transactional context, to ensure that

 

each aspect of your workflow—or a subset of a workflow—completes (or

 

fails) as a singular atomic unit.

Tracking services

This feature is primarily used for debugging and optimizing a WF activity;

 

it allows you to monitor the activities of a given workflow.

Scheduling services

This feature allows you to control how the WF runtime engine manages

 

threads for your workflows.

 

 

When you create an instance of the WF runtime engine in order to execute one of your workflows, you have the option of calling the AddService() method to plug in tracking or persistence service objects (such as SqlWorkflowPersistanceService and SqlTrackingService) as well as any customized service you may have designed. At this point you are able to execute a given WF instance and allow these auxiliary services to further monitor its lifetime.

In this introductory chapter, we will not build custom implementations of the core services, nor will we dive too deeply into the default functionality of them. Here, we will focus on the building blocks of a workflow-enabled application, and we will examine numerous WF activities. Be sure to check out the .NET Framework 3.5 SDK documentation for further details of the core services.

A First Look at WF Activities

Recall that the purpose of WF is to allow you to model a business process in a declarative manner, which is then executed by the WF runtime engine. In the vernacular of WF, a business process is composed of any number of activities. Simply put, a WF activity is an atomic “step” in the overall process. When you create a new WF-enabled application using Visual Studio 2008, you will find a Windows Workflow area of the Toolbox that contains iconic representations of the built-in activities (see Figure 26-2).

Figure 26-2. The Windows Workflow Toolbox

CHAPTER 26 INTRODUCING WINDOWS WORKFLOW FOUNDATION

921

Note Visual Studio 2008 divides the Activities Toolbox into .NET 3.0 and .NET 3.5 activity categories. The activities under the Windows Workflow v3.5 node allow you to interact with Windows Communication Foundation (WCF) services.

.NET 3.5 provides numerous out-of-the-box activities that you can use to model your business process, each of which maps to real types within the System.Workflow.Activities namespace and therefore can be represented by, and driven from, code. You’ll make use of several of these baked-in activities over the course of this chapter. Table 26-2 describes the high-level functionality of some useful activities, grouped by related functionality.

Table 26-2. A Sampling of Intrinsic WF Activities

Activities

Meaning in Life

CodeActivity

This activity represents a unit of custom code to execute

 

within the workflow.

IfElseActivity, WhileActivity

These activities provide basic looping and decision

 

support within a workflow.

InvokeWebServiceActivity,

These activities allow your workflow to interact with

WebServiceInputActivity,

XML web services.

WebServiceOutputActivity,

 

WebServiceFaultActivity

 

SendActivity, ReceiveActivity

These activities allow you to interact with Windows

 

Communication Foundation services. Be aware that these

 

two activities are .NET 3.5 specific.

ConditionedActivity, GroupActivity

These activities allow you to define a group of related

 

activities that execute when a given condition is true.

DelayActivity, SuspendActivity,

These activities allow you to define wait periods as well

TerminateActivity

as pause or terminate a course of action within a

 

workflow.

EventDrivenActivity,

These activities allow you to associate CLR events to a

EventHandlingScopeActivity

given activity within the workflow.

ThrowActivity, FaultHandlerActivity

These activities allow you to raise and handle exceptions

 

within a workflow.

ParallelActivity, SequenceActivity

These activities allow you to execute a set of activities in

 

parallel or in sequence.

 

 

While the current number of intrinsic activities is impressive and provides a solid foundation for many WF-enabled applications, you are also able to create custom activities that seamlessly integrate into the Visual Studio IDE and the WF runtime engine.

Sequential and State Machine Workflows

The WF API provides support for modeling two flavors of business process workflows: sequential workflows and state machine workflows. Ultimately, both categories are constructed by piecing together any number of related activities; however, exactly how they execute is what sets them apart.