Παρουσίαση με Ετικέτες

You have changes? I have Workflow!
01 Δεκεμβρίου 11 05:07 πμ | tolisss | 0 σχόλια   

WF4 uses a service oriented architecture and as a result any problem can be decoupled into smaller, easily solvable and testable services. XAF uses MVC architecture which, in a sense, is very similar to that used by WF4. We can compare XAF’s controllers to WF4 services. Moreover XAF’s Application does the same job as the WF4 server. The upshot of all this is that users should be able to get the feel of WF4 in no time at all. The XAF workflow module introduces a new layer that makes the already decoupled services aware of our business classes.  After this the sky is the limit and over the next few posts I aim to demonstrate some of what can be achieved. For example the next post will focus on creating an event driven workflow initialization engine.

To get back to today’s post, we will discuss an implementation that is very decoupled and as a result it has very limited dependencies on other modules. It is worth noting that all XAF’s features are decoupled, persistent objects take on the role of domain mappers.

Take these requirements;

  • an end user needs to be able to input an object type (and or) a property name,
  • an object change needs to start the workflow either at client or at sever,
  • workflows need to be aware of the object that has changed, its PropertyName and its property OldValue.

The custom workflow definition

We cannot use the default XAF XpoWorkFlowDefinition class in any way.  This is because there are no fields to store the PropertyName and its OldValue. We should not even derive from the default XpoWorkFlowDefinition because we may face difficulties as this class is used by our workflow server. To cope with this issue it is necessary to create a custom ObjectChangedWorkflow definition as shown.

image

While we are doing this we also need to modify the default xaml of the workflow and add the two more arguments (propertyName, oldValue) as per our requirements.

image

Below you can see the UI of this custom workflow definition,

image

Up to here XAF has made things very straightforward for us. We have designed a normal persistent class to store our data and we have used attributes (PropertyEditorType, DataStourceProperty, TypeConverter etc) to configure the UI.

Registration of custom workflow definition

The next step is to register this custom workflow definition. To help with this task, eXpand, provides the WorkflowStartService<T> where T is the type of workflow. Furthermore for ObjectChangeWorkflow definitions the implementation is rather easy since there are no further requirements.

public class ObjectChangedWorkflowStartService : WorkflowStartService<ObjectChangedWorkflow> {

    public ObjectChangedWorkflowStartService()

        : base(TimeSpan.FromMinutes(1)) {

    }

    public ObjectChangedWorkflowStartService(TimeSpan requestsDetectionPeriod) : base(requestsDetectionPeriod) { }

    protected override bool NeedToStartWorkflow(IObjectSpace objectSpace, ObjectChangedWorkflow workflow) {

        return true;

    }

 

    protected override void AfterWorkFlowStarted(IObjectSpace objectSpace, ObjectChangedWorkflow workflow, Guid startWorkflow) {

 

    }

}

 

Start workflow - Track Object Changes

Now, when I have registered workflows on the server, it's time to return to my task: start a workflow when a property has been changed.
In XAF, I can track changes with the help of the ObjectSpace.Committing and ObjectSpace.ObjectChanged events. However because we need to create only one request per object change, it is advisable to collect the changes in an array.

public class StartWorkflowOnObjectChangeController : ViewController<ObjectView> {

    protected override void OnActivated() {

        base.OnActivated();

        if (TypeHasWorkflows()) {

            ObjectSpace.ObjectChanged += PopulateObjectChangedEventArgs;

            ObjectSpace.Committing += StartWorkFlows;

        }

    }

    void PopulateObjectChangedEventArgs(object sender, ObjectChangedEventArgs objectChangedEventArgs) {

        if (!string.IsNullOrEmpty(objectChangedEventArgs.PropertyName)) {

            var changedEventArgs = _objectChangedEventArgses.FirstOrDefault(args => args.Object == objectChangedEventArgs.Object && args.PropertyName == objectChangedEventArgs.PropertyName);

            if (changedEventArgs != null) {

                _objectChangedEventArgses.Remove(changedEventArgs);

                _objectChangedEventArgses.Add(new ObjectChangedEventArgs(changedEventArgs.Object, changedEventArgs.PropertyName, changedEventArgs.OldValue, objectChangedEventArgs.NewValue));

            } else

                _objectChangedEventArgses.Add(objectChangedEventArgs);

        }

    }

 

    void StartWorkFlow(ObjectChangedEventArgs objectChangedEventArgs, ObjectChangedWorkflow objectChangedWorkflow) {

        var o = objectChangedEventArgs.Object;

        ITypeInfo typeInfo = XafTypesInfo.Instance.FindTypeInfo(o.GetType());

        object targetObjectKey = typeInfo.KeyMember.GetValue(o);

        if (objectChangedWorkflow.ExecutionDomain == ExecutionDomain.Server) {

            CreateServerRequest(objectChangedEventArgs, objectChangedWorkflow, targetObjectKey, typeInfo);

        } else {

            InvokeOnClient(objectChangedEventArgs, objectChangedWorkflow, targetObjectKey);

        }

    }

As you will have noticed we have not used the default VS naming for ObjectSpace event handlers. This is because the names that have chosen give a more specific idea of how each method works.

The ObjectChanged event occurs each time a property is changed and the changes are collected in the objectChangedEventArgses array. The Committing event occurs once changes are ready to be sent to the server and workflows start for each entry. We have introduced two options for starting and executing workflows;

  1. Execute synchronously and locally,
  2. Send a request to the server and execute at the server asynchronously

Execute a workflow synchronously on the client

The next stage is to create activities at the client then on ObjectSpace CommitChanges from appropriate WorkflowDefinition and execute them immediatelly

public class StartWorkflowOnObjectChangeController : ViewController<ObjectView> {

 

    void InvokeOnClient(ObjectChangedEventArgs objectChangedEventArgs, ObjectChangedWorkflow objectChangedWorkflow, object targetObjectKey) {

        Activity activity = ActivityXamlServices.Load(new StringReader(objectChangedWorkflow.Xaml));

        var dictionary = ObjectChangedStartWorkflowService.Dictionary(targetObjectKey, objectChangedEventArgs.PropertyName, objectChangedEventArgs.OldValue);

        WorkflowInvoker.Invoke(activity, dictionary);

    }


This is a simple code which can be found in nearly any WF4 example at http://www.microsoft.com/download/en/details.aspx?id=21459.

Send a request to start workflow on the server

The second of our two methods involves starting the workflow at the server. Now we need to notify the server of the values of those arguments as well. In the manually starting workflows post we learnt that XAF does this by using XpoStartWorkflowRequest. This class has a different design however, and may create issues since it is used by XAF default services. Therefore instead of deriving from XpoStartWorkflowRequest we need to design a similar custom class.

public class ObjectChangedXpoStartWorkflowRequest : WFBaseObject, IObjectChangedWorkflowRequest {

 

    [TypeConverter(typeof(StringToTypeConverter))]

    public Type TargetObjectType {

        get { return _targetObjectType; }

        set { SetPropertyValue("TargetObjectType", ref _targetObjectType, value); }

    }

    #region IDCStartWorkflowRequest Members

    public string TargetWorkflowUniqueId {

        get { return GetPropertyValue<string>("TargetWorkflowUniqueId"); }

        set { SetPropertyValue("TargetWorkflowUniqueId", value); }

    }

 

    [ValueConverter(typeof(KeyConverter))]

    public object TargetObjectKey {

        get { return GetPropertyValue<object>("TargetObjectKey"); }

        set { SetPropertyValue<object>("TargetObjectKey", value); }

    }

    #endregion

    #region IObjectChangedWorkflowRequest Members

    public string PropertyName {

        get { return _propertyName; }

        set { SetPropertyValue("PropertyName", ref _propertyName, value); }

    }

 

    [ValueConverter(typeof(SerializableObjectConverter))]

    [Size(SizeAttribute.Unlimited)]

    public object OldValue {

        get { return _oldValue; }

        set { SetPropertyValue("OldValue", ref _oldValue, value); }

    }

This is a very simple class, its only role is to store values in the database. Now instead of invoking workflows locally we only need to create ObjectChangedXpoStartWorkflowRequest objects.

public class StartWorkflowOnObjectChangeController : ViewController<ObjectView> {

    void CreateServerRequest(ObjectChangedEventArgs objectChangedEventArgs, ObjectChangedWorkflow objectChangedWorkflow, object targetObjectKey, ITypeInfo typeInfo) {

        var request = ObjectSpace.CreateObject<ObjectChangedXpoStartWorkflowRequest>();

        request.TargetWorkflowUniqueId = objectChangedWorkflow.GetUniqueId();

        request.TargetObjectType = typeInfo.Type;

        request.TargetObjectKey = targetObjectKey;

        request.PropertyName = objectChangedEventArgs.PropertyName;

        request.OldValue = GetOldValue(objectChangedEventArgs);

    }

In the next step we are going to create a service to consume these values from the server and start a workflow,

public class StartWorkflowOnObjectChangeService : BaseTimerService {

    public override void OnTimer() {

        using (var objectSpace = ObjectSpaceProvider.CreateObjectSpace()) {

            //get all requests from the database

            foreach (var request in objectSpace.GetObjects<ObjectChangedXpoStartWorkflowRequest>()) {

                //find workflow

                var definition = GetService<IWorkflowDefinitionProvider>().FindDefinition(request.TargetWorkflowUniqueId);

                if (definition != null && definition.CanOpenHost) {

                    //Start the workflow passing in PropertyName && OldValue

                    if (GetService<ObjectChangedStartWorkflowService>().StartWorkflow(definition.Name,

                        request.TargetWorkflowUniqueId, request.TargetObjectKey, request.PropertyName, request.OldValue)) {

                        objectSpace.Delete(request);

                        objectSpace.CommitChanges();

                    }

                }

 

            }

        }

    }

At this point our server has all the information it needs to start workflows with arguments taken from persistent ObjectChangeXpoStartWorkFlowRequest objects.

I must admit that I have fully enjoyed preparing this post. The decoupled development experienced offered by the WF service oriented model is something that really appeals to me. At the same time XAF’s workflow module implementation made modeling the requirements a simple and enjoyable process. As usual it was possible to work directly on the problem and leave the hard work to non XAF developers.


Δημοσίευση στην κατηγορία: ,
DevExpress Workflow Instance Store
06 Σεπτεμβρίου 11 10:16 πμ | tolisss | 0 σχόλια   

In version 11.1.7 our team now provides workflow instance support outside XAF borders! With a few lines of code, it is now possible to store our workflows in any of the 14 database systems described in this post.

Durability is a key benefit of the Workflow Foundation and it is based on the ability to store a running workflow instance on the fly at almost any time. 
To this end Microsoft workflow team implemented the SqlWokflowInstanceStore class. Using this class and a few lines is possible to store workflow instances in SQL Server as shown,

// Define SqlWorkflowInstanceStoreBehavior:

//   Set interval to renew instance lock to 5 seconds.

//   Set interval to check for runnable instances to 2 seconds.

//   Instance Store does not keep instances after it is completed.

//   Select exponential back-off algorithm when retrying to load a locked instance.

//   Instance state information is compressed using the GZip compressing algorithm.

SqlWorkflowInstanceStoreBehavior instanceStoreBehavior = new SqlWorkflowInstanceStoreBehavior(connectionString);

instanceStoreBehavior.HostLockRenewalPeriod = new TimeSpan(0, 0, 5);

instanceStoreBehavior.RunnableInstancesDetectionPeriod = new TimeSpan(0, 0, 2);

instanceStoreBehavior.InstanceCompletionAction = InstanceCompletionAction.DeleteAll;

instanceStoreBehavior.InstanceLockedExceptionAction = InstanceLockedExceptionAction.AggressiveRetry;

instanceStoreBehavior.InstanceEncodingOption = InstanceEncodingOption.GZip;

host.Description.Behaviors.Add(instanceStoreBehavior);

The above code was copied from the "BuiltInConfiguration" demo, "InstanceStore1" project. This demo is described at "Built-in Configuration"
(
http://msdn.microsoft.com/en-us/library/ee622978.aspx). You can download full sources of this demo and many others at "WCF and WF Samples for .NET Framework 4"
(
http://www.microsoft.com/download/en/details.aspx?id=21459).

Following the same architecture our team implemented the DX WorkFlow Instance Store. eXpress Persistent Objects (XPO) is used for common objects storage and is fully capable of working transparently with 14 different database systems. For example to provide support for an Oracle database we could write,

//We create or connect to a database by setting the connectionstring

//This code will create 2 tables (XpoWorkflowInstance, XpoInstanceKeyc) in the database

using (var session = new Session()) {

    session.ConnectionString = "Data Source=DevExpressInstanceStore;User Id=myUsername;Password=myPassword";

    session.UpdateSchema(typeof(XpoWorkflowInstance), typeof(XpoInstanceKey));

    session.CreateObjectTypeRecords(typeof(XpoWorkflowInstance), typeof(XpoInstanceKey));

}

 

// Define WorkflowInstanceStoreBehavior:

var dxInstanceStoreBehavior = new WorkflowInstanceStoreBehavior(

    typeof(XpoWorkflowInstance), typeof(XpoInstanceKey), DevExpressConnectionString);

host.Description.Behaviors.Add(dxInstanceStoreBehavior);

dxInstanceStoreBehavior.RunnableInstancesDetectionPeriod = new TimeSpan(0, 0, 2);

dxInstanceStoreBehavior.InstanceCompletionAction = InstanceCompletionAction.DeleteAll;

 

You can download a modified version of the “BuiltInConfiguration” solution here. The console application starts a long running workflow that implements a counting service. Once the service’s start method is invoked, the service counts from 0 to 59. The counter is incremented every 2 seconds. After each count the workflow persists so you can close the application at any time and when you start it next time it will continue. A new one will be started from '0' value in addition to the loaded instances. The second project “InstanceStore2” in the solution provides the same functionality, however it is configured using the app.config file as shown,

<system.serviceModel>

  <extensions>

    <behaviorExtensions>

      <add name="DevExpressWorkflowInstanceStore" type="DevExpress.Workflow.Store.WorkflowInstanceStoreElement, DevExpress.Workflow.Activities.v11.1"/>

    </behaviorExtensions>

  </extensions>

  <services>

    <service name="CountingWorkflow" behaviorConfiguration="">

    </service>

  </services>

  <behaviors>

    <serviceBehaviors>

      <behavior name="">

        <!--<sqlWorkflowInstanceStore

          connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=InstanceStore;Integrated Security=True;Asynchronous Processing=True"

          hostLockRenewalPeriod="00:00:05" runnableInstancesDetectionPeriod="00:00:02" instanceCompletionAction="DeleteAll"

          instanceLockedExceptionAction="AggressiveRetry" instanceEncodingOption="GZip"

          />-->

        <DevExpressWorkflowInstanceStore

          connectionString="Data Source=DevExpressInstanceStore;User Id=myUsername;Password=myPassword"

          runnableInstancesDetectionPeriod="00:00:02" instanceCompletionAction="DeleteAll"/>

      </behavior>

    </serviceBehaviors>

  </behaviors>

</system.serviceModel>

Note; All we need to do to use these code snippets in our code is to reference 'DevExpress.ExpressApp.v11.1.dll' and 'DevExpress.Workflow.Activities.v11.1.dl assemblies. Even though these assemblies are part of our eXpressApp framework they can also be used to support any other type of .NET application!

 

We are waiting to read your feedback about this. Remember that your questions are the best candidates for future posts.

Related Links
Blog posts
Online documentation
Videos

 

 


Δημοσίευση στην κατηγορία: ,
Scheduling workflows in eXpandFrameWork
05 Σεπτεμβρίου 11 04:27 πμ | tolisss | 0 σχόλια   

In this post we are going to extend the functionality of the workflow module. to create a UI that will help us to schedule workflows. Some of you may recall that we looked at using a Delay inside a While activity in Working with CRUD activities – Short Transactions. Recently, DX-Squad member Martin Praxmarer raised an interesting question relating to this topic:

I have the requirement to do a workflow which starts each day on 6 clock - searches for orderdocuments where a specific date is less then X days. I know I will do an do while loop, but workflow definition has 2 options, start when new object, start when criteria, so when do i start this Workflow?

In order to achieve this we will use the WorkFlow demo that ships with our framework. The first thing is to design our custom ScheduledWorkflow persistent object by implementing IWorkflowDefinition. We didn’t derive it from the existing WorkFlowDefinition object because it has properties like TargetObjectType and start up conditions.

public enum StartMode {

    OneTime,

    Daily,

    Weekly

}

 

[DefaultClassOptions]

[Appearance("WeekDays", "StartMode <> 'Weekly'",

    TargetItems = "RecurEveryWeeks;Moday;Tuesday;Wednesday;Thursday;Friday;Saturday;Sunday",

    Visibility = ViewItemVisibility.Hide)]

public class ScheduledWorkflow : BaseObject, IWorkflowDefinition {

    public ScheduledWorkflow(Session session)

        : base(session) {

    }

 

    public bool IsActive {

        get { return GetPropertyValue<bool>("IsActive"); }

        set { SetPropertyValue("IsActive", value); }

    }

 

    public bool RuntASAPIfScheduledStartIsMissed {

        get { return GetPropertyValue<bool>("RuntASAPIfScheduledStartIsMissed"); }

        set { SetPropertyValue("RuntASAPIfScheduledStartIsMissed", value); }

    }

 

    [Association]

    public XPCollection<ScheduledWorkflowLaunchHistory> LaunchHistoryItems {

        get { return GetCollection<ScheduledWorkflowLaunchHistory>("LaunchHistoryItems"); }

    }

 

    [ImmediatePostData]

    public StartMode StartMode {

        get { return GetPropertyValue<StartMode>("StartMode"); }

        set { SetPropertyValue("StartMode", value); }

    }

 

    public TimeSpan StartTime {

        get { return GetPropertyValue<TimeSpan>("StartTime"); }

        set { SetPropertyValue("StartTime", value); }

    }

 

    [Appearance("RecurEveryDays", "StartMode <> 'Daily'", Visibility = ViewItemVisibility.Hide)]

    public int RecurEveryDays {

        get { return GetPropertyValue<int>("RecurEveryDays"); }

        set { SetPropertyValue("RecurEveryDays", value); }

    }

 

    public int RecurEveryWeeks {

        get { return GetPropertyValue<int>("RecurEveryWeeks"); }

        set { SetPropertyValue("RecurEveryWeeks", value); }

    }

 

    public bool Moday {

        get { return GetPropertyValue<bool>("Moday"); }

        set { SetPropertyValue("Moday", value); }

    }

 

    public bool Tuesday {

        get { return GetPropertyValue<bool>("Tuesday"); }

        set { SetPropertyValue("Tuesday", value); }

    }

 

    public bool Wednesday {

        get { return GetPropertyValue<bool>("Wednesday"); }

        set { SetPropertyValue("Wednesday", value); }

    }

 

    public bool Thursday {

        get { return GetPropertyValue<bool>("Thursday"); }

        set { SetPropertyValue("Thursday", value); }

    }

 

    public bool Friday {

        get { return GetPropertyValue<bool>("Friday"); }

        set { SetPropertyValue("Friday", value); }

    }

 

    public bool Saturday {

        get { return GetPropertyValue<bool>("Moday"); }

        set { SetPropertyValue("Saturday", value); }

    }

 

    public bool Sunday {

        get { return GetPropertyValue<bool>("Sunday"); }

        set { SetPropertyValue("Sunday", value); }

    }

    #region IWorkflowDefinition Members

    public string GetActivityTypeName() {

        return GetUniqueId();

    }

 

    public IList<IStartWorkflowCondition> GetConditions() {

        return new IStartWorkflowCondition[0];

    }

 

    public string GetUniqueId() {

        if (Session.IsNewObject(this)) {

            throw new InvalidOperationException();

        }

        return "ScheduledWorkflow" + Oid.ToString().ToUpper().Replace("-", "_");

    }

 

    [Browsable(false)]

    public bool CanCompile {

        get { return false; }

    }

 

    [Browsable(false)]

    public bool CanOpenHost {

        get { return IsActive && !string.IsNullOrEmpty(Name); }

    }

 

    public string Name {

        get { return GetPropertyValue<string>("Name"); }

        set { SetPropertyValue("Name", value); }

    }

 

    [Size(SizeAttribute.Unlimited)]

    public string Xaml {

        get { return GetPropertyValue<string>("Xaml"); }

        set { SetPropertyValue("Xaml", value); }

    }

    #endregion

    public override void AfterConstruction() {

        base.AfterConstruction();

        Xaml = DCWorkflowDefinitionLogic.InitialXaml;

    }

}

 

In the above class we have added some scheduled specific properties such as StartMode, StartTime, RecurEveryDays etc. The class has been decorated with the Appearance attribute to control the visibility of the Day properties. This means when StartMode <> 'Weekly these properties will be hidden.

Moreover there is a collection LaunchHistoryItems of ScheduledWorkflowLaunchHistory objects, which will be used later to check if the workflow has been launched.

public class ScheduledWorkflowLaunchHistory : BaseObject {

    public ScheduledWorkflowLaunchHistory(Session session) : base(session) {}

    public DateTime LaunchedOn {

        get { return GetPropertyValue<DateTime>("LaunchedOn"); }

        set { SetPropertyValue<DateTime>("LaunchedOn", value); }

    }

    [Association]

    public ScheduledWorkflow Workflow {

        get { return GetPropertyValue<ScheduledWorkflow>("Workflow"); }

        set { SetPropertyValue<ScheduledWorkflow>("Workflow", value); }

    }

}

 

After designing these classes, we now have all the required input in order to schedule our workflows.

The next step is to load our custom workflows by extending the workflow provider service as shown,

public class ScheduledWorkflowDefinitionProvider : WorkflowDefinitionProvider {

    public ScheduledWorkflowDefinitionProvider(Type workflowDefinitionType) : base(workflowDefinitionType) { }

    public ScheduledWorkflowDefinitionProvider(Type workflowDefinitionType, IObjectSpaceProvider objectSpaceProvider) : base(workflowDefinitionType, objectSpaceProvider) { }

    public override IList<IWorkflowDefinition> GetDefinitions() {

        IList<IWorkflowDefinition> result = base.GetDefinitions();

        IObjectSpace objectSpace = ObjectSpaceProvider.CreateObjectSpace(); //don't dispose immediately

        foreach(ScheduledWorkflow workflow in objectSpace.GetObjects<ScheduledWorkflow>()) {

            result.Add(workflow);

        }

        return result;

    }

}

 

After this we are ready to implement our final service that will schedule our workflows,

public class ScheduledWorkflowStartService : BaseTimerService {

    private bool NeedToStartWorkflow(IObjectSpace objectSpace, ScheduledWorkflow workflow) {

        if (workflow.StartMode == StartMode.OneTime) {

            if (workflow.LaunchHistoryItems.Count == 0) {

                return true;

            }

        } else if (workflow.StartMode == StartMode.Daily) {

            var historyItem = objectSpace.FindObject<ScheduledWorkflowLaunchHistory>(CriteriaOperator.Parse("GetDate(LaunchedOn) = ?", DateTime.Today));

            if (historyItem == null && DateTime.Now.TimeOfDay > workflow.StartTime) {

                return true;

            }

        } else if (workflow.StartMode == StartMode.Weekly) {

            throw new NotImplementedException();

        }

        return false;

    }

    public ScheduledWorkflowStartService()

        : base(TimeSpan.FromMinutes(1)) {

    }

    public ScheduledWorkflowStartService(TimeSpan requestsDetectionPeriod) : base(requestsDetectionPeriod) { }

    public override void OnTimer() {

        using (IObjectSpace objectSpace = ObjectSpaceProvider.CreateObjectSpace()) {

            foreach (ScheduledWorkflow workflow in objectSpace.GetObjects<ScheduledWorkflow>(new BinaryOperator("IsActive", true))) {

                WorkflowHost host;

                if (HostManager.Hosts.TryGetValue(workflow.GetUniqueId(), out host)) {

                    if (NeedToStartWorkflow(objectSpace, workflow)) {

                        host.StartWorkflow(new Dictionary<string, object>());

                        var historyItem = objectSpace.CreateObject<ScheduledWorkflowLaunchHistory>();

                        historyItem.Workflow = workflow;

                        historyItem.LaunchedOn = DateTime.Now;

                        objectSpace.CommitChanges();

                    }

                }

            }

        }

    }

}

 

Note; the service is not fully implemented, however its very easy to continue from this point. This code will live in the new Xpand.ExpressApp.Workflow module. Now, for the rest of the implementation I would like to ask the help of our community. Anyone that wants to finish it contribute it is most welcome!

Finally we modify the WorkflowServerStarter class and add this service,

private void Start_(string connectionString, string applicationName) {

    ServerApplication serverApplication = new ServerApplication();

    serverApplication.ApplicationName = applicationName;

    serverApplication.Modules.Add(new WorkflowDemoModule());

    serverApplication.ConnectionString = connectionString;

    serverApplication.Security = new SecurityComplex<User, Role>(

        new WorkflowServerAuthentication(new BinaryOperator("UserName", "WorkflowService")));

    serverApplication.Setup();

    serverApplication.Logon();

 

    IObjectSpaceProvider objectSpaceProvider = serverApplication.ObjectSpaceProvider;

 

    server = new WorkflowServer("http://localhost:46232", objectSpaceProvider, objectSpaceProvider);

 

    server.WorkflowDefinitionProvider = new ScheduledWorkflowDefinitionProvider(typeof(XpoWorkflowDefinition));

 

    //Add the service           

    server.ServiceProvider.AddService(new ScheduledWorkflowStartService());

We are now ready to go!

4-9-2011 12-50-05 μμ

Related Links
Blog posts
Online documentation
Videos


Δημοσίευση στην κατηγορία: ,
XAF Workflow persistence storage
25 Αυγούστου 11 07:30 πμ | tolisss | 0 σχόλια   

The .NET Framework 4 ships with the SQL Workflow Instance Store which allows workflows to persist state information about workflow instances in a SQL Server 2005 or SQL Server 2008 database. However its implementation is based on stored procedures which can be a bit scary if you are not familiar with them. In addition our customers may follow different standards (Oracle, Firebird, VistaDB, MySQL, PostgreSQL etc.) and it’s unrealistic to hire more people to support SQL Server infrastructure.

Usually when dealing with these issues the first thing we do is to carry out a Google search for storage solutions. Surprisingly there is no such solution out there! Moreover there is very little useful code or samples available. Luckily XAF provides us with an easier route!

XAF is the perfect workflow modeling environment. It provides a ready made solution for creating and deploying a server that will execute workflows as described here.  In order to start modeling workflows we can use VS design time along with our re-hosted runtime Workflow designer and custom WF4 activities. XAF also gives us increased control over our workflows for example through the ability to manually start workflows. Finally since XAF uses XPO to access data we can easily support 16 different database systems simply by providing a connection string!

XPO Data Store Adapter XPO Data Store Adapter's Assembly Name Database Provider Assembly
AccessConnectionProvider DevExpress.Xpo.vXXX System.Data.dll
AdvantageConnectionProvider DevExpress.Xpo.vXXX.Providers Advantage.Data.Provider.dll 9.10.2.0
AsaConnectionProvider DevExpress.Xpo.vXXX.Providers iAnywhere.Data.SQLAnywhere.dll 11.0.0.12642
AseConnectionProvider DevExpress.Xpo.vXXX.Providers Sybase.Data.AseClient.dll 1.15.50.0
DB2ConnectionProvider DevExpress.Xpo.vXXX.Providers IBM.Data.DB2.dll 9.5.2.2
FirebirdConnectionProvider DevExpress.Xpo.vXXX.Providers FirebirdSql.Data.Firebird.dll 1.7.1.0
FirebirdSql.Data.FirebirdClient.dll 2.5.1.0
MSSqlConnectionProvider DevExpress.Xpo.vXXX System.Data.dll
MSSqlCEConnectionProvider DevExpress.Xpo.vXXX.Providers System.Data.SqlServerCe.dll 3.5.0
System.Data.SqlServerCe.dll 4.0.8482.1
MySqlConnectionProvider DevExpress.Xpo.vXXX.Providers MySql.Data.dll 5.2.5.0
OracleConnectionProvider DevExpress.Xpo.vXXX.Providers System.Data.OracleClient.dll 2.0.0.0
Oracle.DataAccess.dll 9.2.0.700
ODPConnectionProvider DevExpress.Xpo.vXXX.Providers Oracle.DataAccess.dll 10.1.0.200
PervasiveSqlConnectionProvider DevExpress.Xpo.vXXX.Providers Pervasive.Data.SqlClient.dll 2.10.0.15
PostgreSqlConnectionProvider DevExpress.Xpo.vXXX.Providers Npgsql.dll 2.0.11.0
SQLiteConnectionProvider DevExpress.Xpo.vXXX.Providers System.Data.SQLite.dll 1.0.61.0
VistaDBConnectionProvider DevExpress.Xpo.vXXX.Providers VistaDB.4.dll 4.0.0.0

More info @ Database Systems Supported by XPO

This is only one example of what XAF can do for us. XAF provides a comprehensive set of solutions that allow you to outsource all of the mundane programming tasks leaving you to focus purely on your business needs. For more info consult our docs, blogs, code central and support center.

Happy Workflowing!

Related Links
Blog posts
Online documentation

Videos


Δημοσίευση στην κατηγορία: , ,

Search

Go

Το Ιστολόγιο

Ιστορικό Δημοσιεύσεων

Συνδρομές