Coder Social home page Coder Social logo

ninject.extensions.wf's Introduction

Dependency injection with Windows Workflow Foundation 4 can be very complex. 

This extension allows to use property injection on Windows Workflow Activities. 

Let us quickly dive into an example how this extension can be useful. Imagine you have a workflow which does the following:

	(1) Observes a given folder for file changes. When a new file arrives in the observed folder the workflow is resumed.
	(2) The new file which is indicated by the file changed event is opened and parsed by an IParser implementation
	(3) The result is passed to the client which invoked the workflow.
	
We have the following key problems now with classical Windows Workflow Foundation:

	(1) How to get IFolderWatcher into the step which observes a given folder for file changes.
	(2) How to get IParser into the step which parses the file
	(3) For event processing we need extensions which might need external dependencies.
	(4) How to unit test WorkflowInvoker and WorkflowApplication events like Completed, Faulted etc.

The workflow	
	
    public class FileInputTransformationWorkflow : Activity
    {
        [RequiredArgument]
        public InArgument<string> Filter { get; set; }

		[RequiredArgument]
        public InArgument<string> Folder { get; set; }

        protected override Func<Activity> Implementation
        {
            get
            {
                return () =>
                    {
                        var path = new Variable<string>();
                        var parsedValues = new Variable<IDictionary<string, double>>();

                        return new Sequence
                            {
                                Variables = { path, parsedValues },
                                Activities =
                                    {
                                        new ObserveFolderStep
                                            {
                                                Folder = new InArgument<string>(env => env.GetValue(this.Folder)),
                                                Filter = new InArgument<string>(env => env.GetValue(this.Filter)),
                                                Path = new OutArgument<string>(path),
                                            },
                                        new ParseDataStep
                                            {
                                                FilePath = new InArgument<string>(path),
                                                ParsedValues = new OutArgument<IDictionary<string, double>>(parsedValues),
                                            },
										...
                                    }
                            };
                    };
            }

            set
            {
                base.Implementation = value;
            }
        }
    }
	
    public sealed class ObserveFolderStep : NativeActivity
    {
        [RequiredArgument]
        public InArgument<string> Filter { get; set; }


        [RequiredArgument]
        public InArgument<string> Folder { get; set; }

        public OutArgument<string> Path { get; set; }

        [Inject]
        public IFolderWatcher FolderWatcher { get; set; }

        protected override bool CanInduceIdle
        {
            get
            {
                return true;
            }
        }

        protected override void CacheMetadata(NativeActivityMetadata metadata)
        {
            // Tell the runtime that we need this extension
            metadata.RequireExtension(typeof(IObserveFolderExtension));

            base.CacheMetadata(metadata);
        }

        protected override void Execute(NativeActivityContext context)
        {
            var observeExtension = context.GetExtension<IObserveFolderExtension>();

            this.FolderWatcher.Folder = this.Folder.Get(context);
            this.FolderWatcher.Filter = this.Filter.Get(context);

            observeExtension.AddFileChangedCallback(this.FolderWatcher);

            this.FolderWatcher.StartObservation();

            context.CreateBookmark(observeExtension.Bookmark, this.OnFileChanged);
        }

        private void OnFileChanged(ActivityContext context, Bookmark bookmark, object value)
        {
            this.FolderWatcher.StopObservation();

            this.Path.Set(context, (string)value);
        }
	}
	
    public sealed class ParseDataStep : CodeActivity
    {
        [RequiredArgument]
        public InArgument<string> Path { get; set; }

        public OutArgument<IDictionary<string, double>> ParsedValues { get; set; }

        [Inject]
        public IParser Parser { get; set; }    

        protected override void Execute(CodeActivityContext context)
        {
            var filePath = this.Path.Get(context);

            var result = this.Parser.Parse(filePath);

            this.ParsedValues.Set(context, result);
        }
    }
	
Workflow Hosting

	var workflow = this.kernel.Get<IWorkflowApplication>();

	// or use dictionary<string,object>
	var inputs = new { Folder = @"C:\temp\", Filter = "*.txt" };

	workflow.Initialize(new FileInputTransformationWorkflow(), inputs);
	
	// The binding for extensions must be transient to profit from the workflow foundation scoping.
	workflow.AddTransientExtension<IObserveFolderExtension>();	
	workflow.AddSingletonExtension<ISomeOtherExtension>();	
	
Conditional bindings?

Activities can be reused. In the example the ParseDataStep could not only be used in FileInputTransformationWorkflow it might also be used in a SlightlyDifferentInputTransformationWorkflow which uses
another IParser implementation. Therefore you would need to define a binding to IParser depending on in which workflow the ParseDataStep would be reused. This is possible with the
BindingWhenSyntaxExtensions!

this.Bind<IParser>().To<Parser>().WhenInjectedIntoActivity(typeof(FileInputTransformationWorkflow));
this.Bind<IParser>().To<SlightlyDifferentParser>().WhenInjectedIntoActivity(typeof(SlightlyDifferentInputTransformationWorkflow));

or even more fancier stuff like

this.Bind<IParser>().To<Parser>().WhenInjectedIntoActivity(root => fancyCondition: bool);
this.Bind<IParser>().To<SlightlyDifferentParser>().WhenInjectedIntoActivity(root => anotherFancyCondition: bool);
	
How to hook into the injection chain?

In some cases you want to perform additional stuff on the activities. For example register certain activities on an Event Broker etc.
This can be achieved by implementing IActivityInjectorExtension. With CanProcess the extension must indicate whether it likes to
process a certain activity or not. In the Process method the actual operation such as registering on event broker etc. can be done.

this.Bind<IActivityInjectorExtension>().To<YourExtension>();

The extensions are collected upon creation of the IActivityInjector. The order of the extension invocation is not predictable.	

Possible side effects?

The default activity resolver uses WorkflowInspectionServices.GetActivities recursively to retrieve all activities under a certain root activity.
This can have the following side effects (extract from MSDN):

To retrieve a specific activity instead of enumerating all of the activities, 
Resolve is used. Both Resolve and GetActivities perform metadata caching if
WorkflowInspectionServices.CacheMetadata has not been previously called. 
If CacheMetadata has been called then GetActivities is based on the existing metadata.
Therefore, if tree changes have been made since the last call to CacheMetadata, 
GetActivities might give unexpected results. If changes have been made to the workflow 
after calling GetActivities, metadata can be re-cached by calling the ActivityValidationServices Validate
method. Caching metadata is discussed in the next section.

You know it better?

If you have a smarter idea how to resolve all activities then you can swap out the internals of the extension. Simply rebind IActivityResolver.

this.Rebind<IActivityResolver>().To<YourSmarterComponent>();

ninject.extensions.wf's People

Contributors

danielmarbach avatar remogloor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ninject.extensions.wf's Issues

License Text Refers to Wrong Extension

From the LICENSE.txt:

Ninject.Extensions.Wcf is intended to be used in both open-source and commercial environments. To allow its use in as many
situations as possible, Ninject.Extensions.Wcf is dual-licensed. You may choose to use Ninject.Extensions.Wcf under either the Apache License,
Version 2.0, or the Microsoft Public License (Ms-PL). These licenses are essentially identical, but you are
encouraged to evaluate both to determine which best fits your intended use.

The bolded text should say "Ninject.Extensions.Wf".

ninject.extensions.wf WebApi and WF4

I use this extension for my project but how to I register an dependency to workflowapplication. I sign it as Inject properties but it is always a null reference. Could you make a real example for this extension.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.