现在的位置: 首页 > 综合 > 正文

Which Class shoulb be derived for Authoring Custom Activity

2013年06月04日 ⁄ 综合 ⁄ 共 11696字 ⁄ 字号 评论关闭

红色字体为个人理解,如有问题,欢迎指正,谢谢! 

The following table lists the features available in the custom activity base classes.

Base activity class Features available

Activity

Composes groups of system-provided and custom activities into a composite activity.

CodeActivity

Implements imperative functionality by providing an Execute method that can be overridden. Also provides access to tracking, variables, and arguments..

NativeActivity

Provides all of the features of CodeActivity, plus aborting activity execution, canceling child activity execution, using bookmarks, and scheduling activities, activity actions, and functions.

DynamicActivity

Provides a DOM-like approach to constructing activities that interfaces with the WF designer and the run-time machinery throughIcustomTypeDescriptor, allowing new activities to be created without defining new types.

Remember, To the workflow you created and based on the wf4 , whatever the way of coding or visual designing . the .net compiler will convert the code or xaml into some Activity Class in assemble.

The Active Anatomy shows below.

 

Authoring Activities using Activity

Activities that derive from Activity compose functionality by assembling other existing activities. These activities can be existing custom activities and activities from the .NET Framework version 4 activity library. Assembling these activities is the most basic way to create custom functionality. This approach is most typically taken when using a visual design environment for authoring workflows.

Besides being base class and providing all basic properties/ methods for all activities, the Activity class could also be used directly to define an activity using composition. When subclassing directly from Activity, a developer describes anActivity‘s Implementation declaratively by creating a tree of existing Activities. For example:

    class Greet : Activity

  {

        public Greet()

        {

            this.Implementation =

                () => new Sequence

                {

                    Activities =

                    {

                        new WriteLine

                        {

                            Text = "Hello"

                        },

                        new WriteLine

                        {

                            Text = "World"

                        },

                    }

                };

        }

}

Code 1. Sample usage of Activity class

This code tells WF Runtime that when Greet is running, it needs to create aSequence Activity with two WriteLine Activities inside, and execute that SequenceActivity as Greet’s body.

When programming using this class, developers could not directly access WF Runtime’s functionalities like bookmark. But it’s very straightforward and fully declarative. We expect this to be the main approach to create new compositeActivities once there are rich Activity libraries built by the community.

  • CodeActivity

CodeActivity allows authoring Activities whose logic could not be built using existingActivities, but simple enough to be implemented by code in one single Executemethod.

Here is an example,

class PrintFile : CodeActivity

{

        protected override void Execute(CodeActivityContext context)

        {

            using (StreamReader file = new StreamReader("foo.txt"))

            {

                string line;

                while ((line = file.ReadLine()) != null)

                {

                    Console.WriteLine(line);

                }

            }

        }

}

Code 2. Sample usage of CodeActivity class

Using CodeActivity is simple, just implement its Execute method and this method will be called when this Activity is executed. CodeActivity is very useful to create simple leaf Activities. 

Authoring Activities using CodeActivity or AsyncCodeActivity

Activities that derive from CodeActivity or AsyncCodeActivity can implement imperative functionality by overriding the Execute method with custom imperative code. The custom code is executed when the activity is executed by the runtime. While activities created in this way have access to custom functionality, they do not have access to all of the features of the runtime, such as full access to the execution environment, the ability to schedule child activities, bookmark creation, or support for a Cancel or Abort method. When a CodeActivity executes, it has access to a reduced version of the execution environment (through the CodeActivityContext orAsyncCodeActivityContext class). Activities created using CodeActivity have access to argument and variable resolution, extensions, and tracking. Asynchronous activity scheduling can be done using AsyncCodeActivity.

Authoring Activities using NativeActivity

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 NativeActivityContext that gets passed into the Execute method. This context has support for scheduling and canceling child activities, executing ActivityAction and ActivityFunc objects, flowing transactions into a workflow, invoking asynchronous processes, canceling and aborting execution, access to execution properties and extensions, and bookmarks (handles for resuming paused workflows).

If an Activity ‘s logic could not be expressed as composition of existing Activities, nor could it be implemented by one method (or one async method), you would need to use NativeActivity. For example, some complicated control flow pattern like State Machine or Peri Net fall into this category. When programming withNativeActivity, developers need to think at the same level of WF Runtime: they need to view Activity execution as scheduling workitems in a queue; and they have direct access to most of Runtime scheduling features like bookmarks, canceling, error progragating, and etc. Using NativeActivity has steeper learning curve and programming is much more complicated. However it's the most powerful activity authoring method and it’s a good window to look into WF Runtime. We will try to build a NativeActivity blow and we will look deeper into it in a series of future blogs about WF Runtime. 

 

Authoring Activities using DynamicActivity

Unlike the other three types of activity, new functionality is not created by deriving new types from DynamicActivity (the class is sealed), but instead, by assembling functionality into the Properties and Implementation properties using an activity document object model (DOM).

Authoring Activities that Return a Result

Many activities must return a result after their execution. Although it is possible to always define a custom OutArgument on an activity for this purpose, it is suggested to instead use Activity, or derive from CodeActivity or NativeActivity. Each of these base classes has an OutArgument named Result that your activity can use for its return value. Activities that return a result should only be used if only one result needs to be returned from an activity; if multiple results need to be returned, separate OutArgument members should be used instead.

 

let 's go through the examples  of Activity models which demo 4 styles to create a custom Activity. 

Today HTTP based RESTful services are getting popular, so it might be useful to perform some basic HTTP operations in workflow. Now let’s try to build one simple Activity to grab some data from a web service using HTTP GET request.

Code it up

First let’s pick a base activity class to implement.

This is a leaf level operation which could not be built by composition of other Activities, aka “leaf activity”. For leaf activities, the simplest approach is to use CodeActivity if the logic could be implemented by one block of imperative code. We know the basic HTTP GET functionality could be implemented by few lines of code using HttpWebRequest API:

            WebRequest request = HttpWebRequest.Create(someUri);

            using (WebResponse response = request.GetResponse())

            {

                //read data out of response.GetResponseStream()

            }

                        Code 1. C# code to perform an HTTP GET operation

So CodeActivity would be a natural choice here. Let’s say we want the activity to “return” the HTTP response content as a string, so we should use CodeActivity<string> as base class.

Next step is to define input/output.

To simplify the case, let’s assume users could only specify URI for the request, so this Activity will have a string-typed input argument Uri. By using CodeActivity<string> as base class, it automatically inherits a string-typed output argument Result. So the class skeleton looks like this:

    public class HttpGet : CodeActivity<string>

    {

        public InArgument<string> Uri

        {

            get;

            set;

        }

 

        //inherited from CodeActivity<string>

        //public OutArgument<string> Result

        //{

        //    get;

        //    set;

        //}

    }

       Code 2. Input/output of HttpGet activity

Final step is to blow in breath of life to the activity.

CodeActivity has one protected abstract method void Execute(CodeActivityContextcontext). Subclasses need to implement this method with activity execution logic. Similarly CodeActivity<T> has an abstract method TExecute(CodeActivityContext context). Subclasses implement this method with concrete logic and returns a result of TCodeActivity<T> will set the return value to the Result output argument automatically. So in our case, we just need to use code 1 to implement Execute method with one change: to access value of an argument, you need to call Argument.Get method, which we will explain in a future blog.

 

    public class HttpGet : CodeActivity<string>

    {

        

        protected override string Execute(CodeActivityContext context)

        {

            WebRequest request = HttpWebRequest.Create(this.Uri.Get(context));

            using (WebResponse response = request.GetResponse())

            {

                //read everything response.GetResponseStream() as one string

                using (StreamReader reader = new StreamReader(response.GetResponseStream()))

                {

                    return reader.ReadToEnd();

                }

            }

        }

    }

        Code 3. Implementation of HttpGet activity

With < 20 lines of code, we create a new activity which could be used in any workflow. That was easy!

To test this activity, the easiest way is to use WorkflowInvoker. Here is a quick example:

     HttpGet fetchMsn = new HttpGet

     {

         Uri = "http://www.msn.com"

     };

     string msnContent = WorkflowInvoker.Invoke<string>(fetchMsn);

     Console.WriteLine(msnContent);

           Code 4. Test code for HttpGet activity

Unblock it

I’ve repeated time after time, workflow is meant for asynchronous services. Whenever possible, we should avoid blocking in activity’s execution logic. In general, any form of IO is potentially slow and we should try to make them non-blocking. The easiest approach to make aCodeActivity to be asynchronous is to use AsyncCodeActivity as long as the logic could be implemented by an asynchronous method call. Looking closer to HttpWebRequest, we found it has Begin/EndGetResponse method pairs, what a godsend! So let’s change HttpGet to AsyncCodeActivity.

AsyncCodeActivity is similar to CodeActivity, except it has aBegin/EndExecute abstract method pair instead of one Execute method. Following .Net Asynchronous Invoke patternBeginExecute starts an asynchronous operation, returns an IAsyncResult without waiting for it to complete; when the operation is done, EndExecute is invoked and returns a result.  So HttpGet would be implemented this way:

    class HttpGet : AsyncCodeActivity<string>

    {

        …

        protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallbackcallback, object state)

        {

            WebRequest request = HttpWebRequest.Create(this.Uri.Get(context));

            context.UserState = request;

            return request.BeginGetResponse(callback, state);           

        }

 

        protected override string EndExecute(AsyncCodeActivityContext context, IAsyncResult result)

        {

            WebRequest request = (WebRequest)context.UserState;

            using (WebResponse response = request.EndGetResponse(result))

            {

                using (StreamReader reader = new StreamReader(response.GetResponseStream()))

                {

                    return reader.ReadToEnd();

                }

            }

        }

    }

        Code 5. Implementation of HttpGet using AsyncCodeActivity

This is great, but you might see more room to improve: reading from a stream is also a blocking operation. We could use BeginRead to make it asynchronous. However now we hit a limitation of AsyncCodeActivity: it only supports execution with one continuation point. So if we want to makeHttpGet 100% non-blocking, we should go with NativeActivity and usebookmark API, which I’m not going to demo in this blog.

How to build a composite activity - World

抱歉!评论已关闭.