Corporate Purchase Process
This sample shows how to create a very basic Request for Proposals (RFP) based purchase process with automatic best proposal selection. It combines Parallel, ParallelForEach<T>, and ForEach<T> and a custom activity to create a workflow that represents the process.
This sample contains an ASP.NET client application that allows interacting with the process as different participants (as the original requester or a particular vendor).
Visual Studio 2012.
.NET Framework 4.6.1.
Composition of activities.
Hosting WF in different clients (ASP.NET Web applications and WinForms applications).
The samples may already be installed on your machine. Check for the following (default) directory before continuing.
If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WF samples. This sample is located in the following directory.
This sample shows an implementation of a Windows Workflow Foundation (WF) program to gather proposals from vendors for a generic company.
An employee of Company X creates a Request for Proposal (RFP).
The employee types in the RFP title and description.
The employee selects the vendors that he wants to invite to submit proposals.
The employee submits the proposal.
An instance of the workflow is created.
The workflow is waiting for all vendors to submit their proposals.
After all proposals are received, the workflow iterates through all the received proposals and selects the best one.
Each vendor has a reputation (this sample stores the reputation list in VendorRepository.cs).
The total value of the proposal is determined by (The value typed in by the vendor) * (The vendor's recorded reputation) / 100.
The original requester can see all the submitted proposals. The best proposal is presented in a special section in the report.
The core logic of the sample uses a ParallelForEach<T> activity that waits for the offers from each vendor (using a custom activity that creates a bookmark), and registers the vendor proposal as an RFP (using an InvokeMethod activity).
The sample then iterates through all of the received proposals stored in the
RfpRepository, calculating the adjusted value (using an Assign activity and System.Activities.Expressions activities), and if the adjusted value is better than the previous best offer, assigns the new value as the best offer (using If and Assign activities).
This sample contains the following projects.
|Common||The entity objects used within the process (Request for Proposal, Vendor, and Vendor Proposal).|
|WfDefinition||The definition of the process (as a WF program) and host (|
|WebClient||An ASP.NET client application that allows the users to create and participate in instances of the purchase process. It uses a custom-created host to interact with the workflow engine.|
|WinFormsClient||A Windows Forms client application that allows the users to create and participate in instances of the purchase process. It uses a custom-created host to interact with the workflow engine.|
The following table contains a description of the most important files in the WfDefinition project.
|IPurchaseProcessHost.cs||Interface for the host of the workflow.|
|PurchaseProcessHost.cs||Implementation of a host for the workflow. The host abstracts the details of the workflow runtime and is used in all the client applications to load, run, and interact with |
|PurchaseProcessWorkflow.cs||An activity that contains the definition of the Purchase Process workflow (derives from Activity).|
Activities that derive from Activity compose functionality by assembling existing custom activities and activities from the .NET Framework 4.6.1 activity library. Assembling these activities is the most basic way to create custom functionality.
|WaitForVendorProposal.cs||This custom activity derives from NativeActivity and creates a named bookmark that must be resumed later by a vendor when submitting the proposal.|
Activities that derive from NativeActivity, like those that derive from CodeActivity, create imperative functionality by overriding Execute, but also have access to all of the functionality of the workflow runtime through the ActivityContext that gets passed into the
|TrackingParticipant.cs||A TrackingParticipant that receives all tracking events and saves them to a text file.|
Tracking participants are added to workflow instance as Extensions.
|XmlWorkflowInstanceStore.cs||A custom InstanceStore that saves workflow applications to XML files.|
|XmlPersistenceParticipant.cs||A custom PersistenceParticipant that saves an instance of request for proposal to an XML file.|
|AsyncResult.cs / CompletedAsyncResult.cs||Helper classes for implementing the asynchronous pattern in the persistence components.|
The following table contains a description of the most important classes in the Common project.
|Vendor||A vendor that submits proposals in a Request for Proposals.|
|RequestForProposal||A request for proposals (RFP) is an invitation for vendors to submit proposals on a specific commodity or service.|
|VendorProposal||A proposal submitted by a vendor to a concrete RFP.|
|VendorRepository||The repository of Vendors. This implementation contains an in-memory collection of instances of Vendor and methods for exposing those instances.|
|RfpRepository||The repository of Requests for Proposals. This implementation contains uses Linq to XML to query the XML file of Requests for Proposal generated by the schematized persistence. This class implements T:System.Runtime.Persistence.IDataViewMapper.|
|IOHelper||This class handles all I/O-related issues (folders, paths, and so on.)|
The following table contains a description of the most important Web pages in the Web Client project.
|CreateRfp.aspx||Creates and submits a new Request for Proposals.|
|Default.aspx||Shows all active and completed Requests for Proposals.|
|GetVendorProposal.aspx||Gets a proposal from a vendor in a concrete Request for Proposals. This page is used only by vendors.|
|ShowRfp.aspx||Show all the information about a Request for Proposals (received proposals, dates, values, and other information). This page is only used by the creator of the Request for Proposal.|
The following table contains a description of the most important forms in the Win Forms project.
|NewRfp||Creates and submits a new Request for Proposals.|
|ShowProposals||Show all active and finished Requests for Proposals. Note: You may need to click the Refresh button in the UI to see changes in that screen after you create or modify a Request for Proposal.|
|SubmitProposal||Get a proposal from a vendor in a concrete Request for Proposals. This window is used only by vendors.|
|ViewRfp||Show all the information about a Request for Proposals (received proposals, dates, values, and other information). This window is only used by the creator of the Request for Proposals.|
The following table shows the files generated by the persistence provider (
XmlPersistenceProvider) are located in the path of the current system's temporary folder (using GetTempPath). The tracing file is created in the current execution path.
|rfps.xml||The XML file with all the active and finished Requests for Proposals.||GetTempPath|
|[instanceid]||This file contains all the information about a workflow instance.|
This file is generated by the schematized persistence implementation (PersistenceParticipant in XmlPersistenceProvider).
|[instanceId].tracking||A text file with all the events that occurred within a concrete instance.|
This file is generated by TrackingParticipant.
|PurchaseProcess.Tracing.TraceLog.txt||The tracing file generated by the workflow based on the configuration parameters in the App.config or Web.config files.||Current execution path|
Using Visual Studio 2010, open the PurchaseProcess.sln solution file.
To execute the Web Client project, open Solution Explorer and right-click the Web Client project. Select Set as Startup Project.
To execute the WinForms Client project, open Solution Explorer and right-click the WinForms Client project. Select Set as Startup Project.
To build the solution, press CTRL+SHIFT+B.
To run the solution, press CTRL+F5.
Create a new RFP: Creates a new Request for Proposals (RFP) and starts a Purchase Process workflow.
Refresh: Refreshes the list of Active and Finished RFPs in the main window.
View: Shows the content of an existing RFP. Vendors can submit their proposals (if invited or the RFP is not finished).
View As: The user can access the RFP using different identities by selecting the desired participant in the View as combo box in the active RFPs grid.
Create RFP: Creates a new Request for Proposals (RFP) and starts a Purchase Process workflow.
Refresh: Refreshes the list of Active and Finished RFPs in the main window.
View RFP: Shows the content of an existing RFP. Vendors can submit their proposals (if invited or the RFP is not finished)
Connect As: The user can access the RFP using different identities by selecting the desired participant in the View as combo box in the active RFPs grid.