-
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


-
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!

Related Links
Blog posts
Online documentation
Videos


-
Our profiler uses WCF in order to communicate with the client application. One of the benefits of the XPOProfiler is that it supports 2 binding types. Both binding types have their advantages however in this situation only one allows us the flexibility we require.
NetTcpBinding
This option provides a secure and reliable binding environment for .Net to .Net cross machine communication. By default it creates a communication stack using WS-ReliableMessaging protocol, TCP for message delivery and windows security for message and authentication at run time.

One drawback is that in shared host environments there is no way to open a port neither to use Mutexes. Our second option allows us to overcome this by creating a WCF service and hosting it with our web application.
WSHttpBinding
-
Defines a secure, reliable, interoperable binding suitable for non-duplex service contracts,
-
Supports WS-* functionality and distributed transactions with reliable and secure sessions using SOAP security,
-
Uses HTTP and HTTPS transport for communication,
-
Reliable sessions are disabled by default

We have already identified limitations when profiling medium trust web applications, now we must look at modifying our application in order to overcome them.
The first step is to add the new service using VS add new Item context menu.
This action will add the following items to the project;
-
IMyLogService.cs - the communication interface description,
-
MyLogService.svc.cs - the communication service class declaration
Furthermore the Web.config file will be modified by adding <system.serviceModel> section. I am using .NET3.5 right now, be aware that this step may vary according to the .NET version you are using.
The next step is to remove the IMyLogService.cs from the project.

Then we are going to modify the MyLogService.svc.cs as follows;
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MyLogService : LogService {
public MyLogService()
: base(Logger) {
if (Logger != null) LogManager.SetTransport(Logger);
}
private static LoggerBase Logger {
get { return new LoggerBase(); }
}
}
The final step is to change the name of the communication contract in <system.serviceModel> section of Web.config file from WebApplicationToProfile.IMyLogService to DevExpress.Xpo.Logger.Transport.ILogSource.

After that we are ready to run the XPOProfiler and create a new connection to our medium hosted XPO web application.

Note: Version 11.2 will allow us to carry out all the above processes.
Happy profiling!
Related Links
Blog posts
Videos


-
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


-
Let me describe for a moment how we at DX work. We build and sell software which means that we only sell and provide support for products that have been built and tested by us! However I am here as a framework evangelist and huge XAF fan. This makes it my duty to spread the word as much as I can and make XAF even bigger. To this end through collaboration within the XAF community, we have been building and supporting eXpand. This framework follows XAF to the letter and takes things even further. eXpand gets its inspiration from real life situations and bases itself on examples from DX SUPPORT CENTER. eXpand is the first open source project based on the DevExpress eXpressApp Framework (XAF). More info is available at www.expandframework.com and our very existence relies on your efforts! Anyone is welcome to contribute and enjoy the rewards. It is not necessary to be a XAF guru, we can all manage to create a behavior taken from DevExpress code central. Let’s work together to enhance our beloved XAF!
Today we are going to deal with creating a reusable module for eXpandFramework. This new module will host State Machine logic therefore we are going to name it Xpand.ExpressApp.StateMachine. A Security logic similar to this is going to be the first resident however we are going to use permissions rather than manually writing the custom function criteria operator to the TargetObjectCriteria. This module can be used as a standalone without the need of eXpandFramwork for the moment. However keeping it under the eXpand umbrella will help us to share features/processes within the framework in the future.
First we need to create a new VS project that shares characteristics with the rest of the eXpand modules. Currently eXpand only provides a new Solution VS template. This means that we need to do things the old fashioned way, i.e. copying and pasting an existing module. Our behavior is platform independent thus it’s a good idea to choose a module that is also platform independent such as Xpand.ExpressApp.ViewVariants.
The next step is to open the cloned project, rename it and set its references to those shown in the pic below. It is important to leave all Xpand core references as they are.

It is advisable to register the original StateMachine module to avoid having to register it later.

XpandSystemModule is already registered since we used the Xpand.ExpressApp.ViewVarians project as a template.
We want (with the help of the existing XAF Security permissions system) to be able to assign special types of permissions to a role. We can then use these permissions to control the transition to certain states. The State Machine module uses XpoStateMachine and XpoState persistent classes. These classes can be linked to a permission by name. As a result a permission having 2 properties StateMachineName and StateName would be sufficient.
[NonPersistent]
public class StateMachineTransitionPermission : PermissionBase {
public override IPermission Copy() {
return new StateMachineTransitionPermission(Modifier, StateCaption, StateMachineName);
}
public StateMachineTransitionPermission() {
}
public override SecurityElement ToXml() {
SecurityElement result = base.ToXml();
result.AddAttribute("modifier", Modifier.ToString());
result.AddAttribute("stateMachineName", StateMachineName);
result.AddAttribute("stateCaption", StateCaption);
return result;
}
public override void FromXml(SecurityElement e) {
base.FromXml(e);
Modifier =
(StateMachineTransitionModifier)
Enum.Parse(typeof(StateMachineTransitionModifier), e.Attributes["modifier"].ToString());
StateCaption = e.Attributes["stateCaption"].ToString();
StateMachineName = e.Attributes["stateMachineName"].ToString();
}
public StateMachineTransitionPermission(StateMachineTransitionModifier modifier, string stateCaption, string stateMachineName) {
Modifier = modifier;
StateCaption = stateCaption;
StateMachineName = stateMachineName;
}
public override bool IsSubsetOf(IPermission target) {
var isSubsetOf = base.IsSubsetOf(target);
if (isSubsetOf) {
var stateMachineTransitionPermission = ((StateMachineTransitionPermission)target);
return stateMachineTransitionPermission.StateCaption == StateCaption &&
stateMachineTransitionPermission.StateMachineName == StateMachineName;
}
return false;
}
public StateMachineTransitionModifier Modifier { get; set; }
public string StateMachineName { get; set; }
public string StateCaption { get; set; }
}
A Modifier property can be used to disable our permission, moreover the SecuritySystem is going to grant the permission by calling the IsSubsetOf method.
Finally, we are going to create a controller and check if a permission with the same state and statemachine name has been granted to our system. If not we are going to throw an
exception
public class StatePermissionController : ViewController, IModelExtender {
void IModelExtender.ExtendModelInterfaces(ModelInterfaceExtenders extenders) {
extenders.Add<IModelOptions, IModelOptionsStateMachine>();
}
protected override void OnActivated() {
base.OnActivated();
var stateMachineController = Frame.GetController<StateMachineController>();
stateMachineController.TransitionExecuting += OnTransitionExecuting;
}
void OnTransitionExecuting(object sender, ExecuteTransitionEventArgs executeTransitionEventArgs) {
var states = executeTransitionEventArgs.StateMachine.States.OfType<XpoState>();
foreach (var state in states) {
if (IsNotGranted(state))
throw new UserFriendlyException("Permissions are not granted for transitioning to the " + state.Caption);
}
}
bool IsNotGranted(XpoState state) {
return IsNotGranted(new StateMachineTransitionPermission(StateMachineTransitionModifier.Deny, state.Caption, state.StateMachine.Name));
}
static bool IsNotGranted(IPermission permission) {
var securityComplex = ((SecurityBase)SecuritySystem.Instance);
bool isGrantedForNonExistentPermission = securityComplex.IsGrantedForNonExistentPermission;
securityComplex.IsGrantedForNonExistentPermission = true;
bool granted = SecuritySystem.IsGranted(permission);
securityComplex.IsGrantedForNonExistentPermission = isGrantedForNonExistentPermission;
return granted;
}
}
During the writing of this post M. Brekhof asked if it is possible to hide the transition in the UI if there are no permissions. This is a certainly a useful feature to include in the new module. To implement it we can subscribe to the ItemsChanged event of the ChangeStateAction and use the IsGranted method there.

Using the model to control the above allows us more flexibility as we can choose whether to include these improvements or not. In order to do so we need to define and register a model extender.

It should be clear that this is a rapid development! In a few short minutes we have created a module which can be used as often as necessary to enhance the functionality of our applications. This is another fine example of getting the job done the XAF way!
So far we have not discussed how to create lookups for our StateMachineTransitionPermission StateMachineName,StateName properties. Don’t worry all of this will be featured in part 2. In the meantime if any of you need any other information please let me know so that I can cover it too.
Happy eXpanding!
eXpand is the first open source project based on the DevExpress eXpressApp Framework (XAF). More info is available at www.expandframework.com


-
Prerequisites
Manually starting workflows
In the first post of the series we saw how to start a workflow using a WCF service. Furthermore we also discussed doing this using the module’s out of the box mechanisms. So far however, we haven’t had time to analyze the reasons for choosing one method or the other. This post aims to clarify the situation.
WCF Solution
As Dennis likes to say, “There is no need to light a cigar with a nuclear reactor”. This is certainly true when it comes to the WCF solution as it can be somewhat unwieldy. In addition we have to come to terms with more technology as it requires configuration for even the smallest task. For example, the client always needs to know the WCF service address. It is also very difficult to work with WF instances because in order to accomplish common tasks (view, track status, generate reports, measure client service reply time) we need to introduce a system of persistent classes which will help to simulate the out of the box behavior. A third issue arises when modifying the solution because there is often a need for changes to occur in many places at the same time. For example when introducing additional properties we have to change client code and Receive contracts.
This option is only recommended when we have a large system (>10000 WF instances). Only then will we notice a gain in performance. This is because one WCF request can start a workflow which costs nothing compared to multiple Sql Queries. In all other cases we are fortunate that XAF allows us to take the easy road as usual!
Out of the box
Take our usual product ordering task; a client simply creates a new OrderRequest business object and fills the necessary members. A workflow with an AutoStartWhenObjectIsCreated condition will start when the object is saved. After that we can access all the information in the workflow algorithm. This method allows us to,
Note that a similar process is described in Working with CRUD activities – Short Transactions
We would appreciate your feedback on this post. Has it been useful to you? Feel free to contact us with any further questions
Related Links
Blog posts
Online documentation


-
Let me describe for a moment how we at DX work. We build and sell software which means that we only sell and provide support for products that have been built and tested by us! However I am here as a framework evangelist and huge XAF fan. This makes it my duty to spread the word as much as I can and make XAF even bigger. To this end through collaboration within the XAF community, we have been building and supporting eXpand. This framework follows XAF to the letter and takes things even further. eXpand gets its inspiration from real life situations and bases itself on examples from DX SUPPORT CENTER. eXpand is the first open source project based on the DevExpress eXpressApp Framework (XAF). More info is available at www.expandframework.com and our very existence relies on your efforts! Anyone is welcome to contribute and enjoy the rewards. It is not necessary to be a XAF guru, we can all manage to create a behavior taken from DevExpress code central. Let’s work together to enhance our beloved XAF!
Using an eXpand module with an existing XAF application is an issue that is being raised a lot recently and I am sure it deserves a post and a place in Xpand Wiki. The following information applies to any Xpand module. Note that installing an Xpand module is almost as easy as using a XAF one.
Introduction
Firstly, as a base type for our application components it is imperative to use XpandWinApplication or XpandWebApplication. Therefore we need to reference Xpand.ExpressApp.Win or Xpand.ExpressApp.Web assemblies from our application projects and replace their components’ base types accordingly,

Additionally we need to reference Xpand.Persistent.BaseImpl. Having similar architecture with XAF, eXpand modules use interfaces and their implementations are hosted in the BaseImpl assembly. Xpand modules know how to locate the Business Objects they require so this is all we need to do at this point.
Note; it is possible to use a custom BaseImpl assembly.
Registering in Toolbox
Now, unless we are a XAF guru, we have to register the assemblies in the VS toolbox. I suggest creating the toolbox items within a new Xpand Tab as shown. At this point we don’t have the resources to create our own ToolBoxCreator utility. If any VS IDE guru wants to contribute such a tool, we owe him one!
Registration
Registering and using a module is as easy as dragging and dropping it into the Application Designer. The designer will then add all required references and modules for us.

Give us your feedback
Was this post useful to you? Do you want to learn more about Xpand framework? We would love to hear your feedback!
Happy eXpanding!


-
I am very happy to announce the release of eXpandFramwework v11. Our team has already tested the framework in production. However any feedback or discussions the new version are most welcome. Please you our forums for better collaboration.
In addition we most welcome Martynas Dauciunas as the newest eXpandframework member. Martys joined our team along with his ImportWizard module. The module can map and import Excel files!
You can read more about it and see the demo video in our blog
As always eXpandFramework is available for download here


-
I am very very happy to announce that I have joined DevExpress as technical evangelist for the Frameworks Team. I believe that it’s not a coincidence that my first name “Apostolos” has a very similar meaning to Evangelist!
I promise to myself and to all of you very exciting times to come I will do my best to honor this great opportunity and evangelize XAF through out the universe, because its the greatest framework I ever came across.
I am completely overwhelmed and let this movie clip be an expression of my feelings.
My new responsibilities will include posting to XAF and XPO blogs and publish videos for DevExpress TV. I will be than more happy to discuss any problems, questions, and ideas you have and these can easily end up as blog posts or webcasts. I will also be attending lots of meetings around the world so I hope I am able to shake hands with many of you and soon enough.
You can also be sure that I will continue to lead and support eXpand Framework and this blog will remain devoted to it.
I am already in LA to catch up with The Team,and would be absolutely delighted to have a beer or two with anybody who is around. I am can now be reached at [email protected]
And for those of you that wanted to see a picture of the headquarters. here is a nice one
Happy DXing to all of you!


-
In the past few weeks I have been working on creating a XAF wrapper module for Quartz.Net v2 which is is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems.
For those who are not familiar with concept of job scheduling, here is some background information. Also, there great tutorial for Quartz, which will give you a good and necessary introduction
Quartz default implementation
Quartz supports several datastores (sqlserver,ce,mysql,imemory etc) that you can utilize by providing an appropriate connection string, same as XPO.
It has a decoupled architecture with just 2 basic entities (JobDetails and Triggers) which are related by Group property. For instance you can create a number of JobDetails, set its Group to “LongRunning”, than create a Trigger with the same Group property. When the trigger is executed, all jobs for that group will run.
There is also a JobDataMap which represents a dictionary that holds serialized values and is used for jobs-triggers communication. You fill it with data when you create a trigger or a job and when they are executed you get that values back for further use.
JobDetails holds a reference of the Type that implements the Quartz.IJob interface where you put the code specific to your job. Quartz instantiates that type using reflection and runs its Execute method using the context parameter that passes all available information (Scheduler instance, JobDataMaps, etc.)
namespace Quartz {
public interface IJob {
void Execute(IJobExecutionContext context);
}
}
That was a description of how Quartz works by default. Now we would want to put a nice-looking and functional UI on top, and we have XAF (+Xpand) to come for the rescue. To provide as much decoupling as possible from the original Quartz implementation I resorted to creating persistent classes with the same set of properties as Quartz ones. I also needed to have a set of controllers to call Quartz API when the Xpand classes are changed. This allows me to be able to design any XAF domain and use the API to interface with Quartz datastore.
Xpand JobScheduler Domain description
First of all we have the same basic entities XpandJobDetail, XpandJobTrigger. A JobSchedulerGroup was introduced to achieve the default quartz relationship as shown below:
Additionally a many to many relationship between XpandJobDetail and XpandJobTrigger has been added allowing you to associate triggers, and jobs (even if they not belong to any group).
A new type of trigger the JobListenerTrigger was introduced in order to be able to trigger a job after an event of another job was executed
Now our XpandJobDetail stores a reference to the Quartz.IJob interface using the Persistent XpandJob object (the same way Quarz does). However, one would have to decorate this class with JobDetailDataMapType or JobDataMapType attribute in order to tell XAF which XpandJobDataMap to associate with the XpandJobDetail or XpandJob And, as you already guessed, JobDataMap, that is dictionary instance for Quartz, is populated from XAF persistent classes.
Bellow you see the sample ThresholdCalculationJob provided by Xpand.ExpressApp.JobScheduler.Jobs assembly:
[System.ComponentModel.DisplayName("ThresholdJob")]
[JobDetailDataMapType(typeof(ThresholdJobDetailDataMap))]
public class ThresholdCalculationJob : IJob {
public const string ThresholdCalcCount = "ThresholdCalcCount";
readonly ILog log = LogManager.GetLogger(typeof(ThresholdCalculationJob));
public void Execute(IJobExecutionContext context) {
log.Info("EXECUTING:ThresholdCalculationJob");
var application = ((IXpandScheduler)context.Scheduler).Application;
var objectSpaceProvider = application.ObjectSpaceProvider;
var jobDataMap = context.JobDetail.JobDataMap;
var typeInfo = objectSpaceProvider.TypesInfo.FindTypeInfo((Type)jobDataMap.Get<ThresholdJobDetailDataMap>(map => map.DataType));
object count;
using (var unitOfWork = new UnitOfWork(((ObjectSpaceProvider)objectSpaceProvider).WorkingDataLayer)) {
count = unitOfWork.GetCount(typeInfo.Type, CriteriaOperator.Parse(jobDataMap.GetString<ThresholdJobDetailDataMap>(map => map.Criteria)));
}
jobDataMap.Put<ThresholdJobDetailDataMap>(ThresholdCalcCount, count);
}
}
Viola! We get a XafApplication instance and off we go using our usual XAF routines!
Apart from the XafApplication instance, the above code has some other interesting details, e.g.
var typeInfo = objectSpaceProvider.TypesInfo.FindTypeInfo((Type)jobDataMap.Get<ThresholdJobDetailDataMap>(map => map.DataType));
Here we are requesting the DataType property value from ThresholdJobDetailDataMap object (didn’t I already tell you that our XpandJobDetails have JobDataMaps are simply XAF persistent objects?).
The server
Xpand provides a very easy way to configure Xpand.Quartz.Server assembly. Server is built with the TopShelf framework, that means you can either run it as a console app or as a windows service (using –install as parameter) . Server is responsible of executig all jobs (classes that implement that Quartz.IJob interface).
One thing you have to remember is that, since Server instantiates XafApplication, the configuration file on the server has to be synchronized with the one on the client.
In order for the server to know which XafAppliation to create the following change is needed:
The Demo
Xpand FeatureCenter has a special assembly (Xpand.ExpressApp.JobScheduler.Jobs) that provides 2 sample job, the ThresholdCalculationJob that you saw already and SendThresholdCalculationEmailJob. Also there is a new EmailTemplateEngine that has been build in order to support the SendThresholdCalculationEmailJob which is naturally based on persistent objects and that can be configured runtime.
The scripts that create a Quartz database can be found in eXpand sources under Resource/Quartz folder. Quartz database has to be created in order to run the demo from FeatureCenter.
To give you a better idea about above mentioned functionality and to show what the sample application (Feature Center in this case) that uses JobScheduler module may look like, here is a short screen cast:


-
For the past two years we concentrated on working with real world projects. We also worked with SC and forum issues from DevExpress. All this we used to gather requirements for eXpand Framework. It was a good starting point and we need your active input to move further. In order to build a proper road map for eXpand we have created several ways which we can use to work together.
The voting system
First off is a voting system based on UserVoice that has been in operation for some time now. Follow the link and you can see the most popular suggestions and you don’t even need to login to cast your votes.
For some time now a voting system based on UserVoice is functional for our site.There you see the most popular suggestions.
But how it works? You just click on Cast your votes

- Searching for an idea is always preferred before posting a new suggestion :)
- Remember that you only have 10 votes for all the suggestions in the list. But you get those back if the suggestion you voted for gets implemented. So, vote carefully. Top right frame gives you the summary of the suggestions you voted for.
- To stay on top of the changes to the suggestions you can subscribe to the General activity feed
- The most important feature is the discussion that we would ideally have about each suggestion. And this is in your own benefit. You have to convince others of the importance of any given suggestion and have then cast their precious votes.
You can also see top 3 suggestions straight from eXpand’s home page:
The project management system
In the first picture (top of this page) at the right frame the first suggestion is “Provide what's new, changed, fixed”. We agree with you that this one should be taken very seriously - users should have a good reason to download latest revision. This suggestion is already assigned to our web master and CI expert Dima Janzen and as far as I know he is considering http://www.redmine.org/ pas a project management system for our web site and he might speak about it when the time comes. We think that Redmine is an excellent and mature system, but do have a look at their web site (which is built on… Redmine!) and contribute to the discussion if you have a comment or an alternative to suggest.
The blog
Big thanks go to our irreplaceable Dima again who takes care of the web site and who just announced that our blog can republish YOUR blog (it is already republishing mine).
Do you have a blog?
Do you blog about XAF / eXpandFramework ?
Do you want us to host your article on our blog?
Please email [email protected], and we are more than happy to republish your articles on our site.


-
jCarousel is a well known carousel build using JQuery by Jan Sorgalla. I have port the Carousel with dynamic content loading via JavaScript to .Net and created NCarousel which is basically a classic asp.net server control that a developer can configure using c# .
A list editor and added to eXpand (NCarouselListEdior) and all NCarousel control properties are also in the model so you can configure it from there.
A special interface IPictureItem should be implemented by your BO object in order editor to work.
public interface IPictureItem
{
string ID { get; }
Image Image { get; }
string ImagePath { get; }
}
At least one of Image, ImagePath peoperties should have values
To make the design of NCarousel skin design fast without having to go back to the model many times I have added the AllowOverride attribute at model, which basically does not inject any specific to view css, thus allowing the skin to be overridden by adding a ling to a css file in your default.aspx.
You can get the default skin css either from eXpand feature center application or from JCarousel original files
That way you modify only the css with dimensions, colors what ever you fill like and then set the attribute back to false and copy your css modifications back to the model so they can be inject as specific to view css.
A normal thumbnail list editor that will display all images in one go similar to the one available at DevExpress Extend demo has been added as well
The image bellow demo the use of multiple editors as nested listview editors,
ps:as text bellow image will concatenate all visible properties of the listview. Samples of those editors can be found at Xpand Feacturecenter web application
http://expandframework.com/downloads/download.html


-
I know that many of you were using eXpand modules for your XAF web applications for some time now and I want to thank you on behalf of the team for your feedback. However since now there wasn’t any official release for them from our team. We are happy to announce the release of the web version of our framework today.
- We have publish VS project item templates for web for both c# and vb.net. Also a web version of our featurecenter application comes along.
As you see in the above image there is a Whats New section now for tracking new features.
- expandframework.com modules list has been enhanced with platform images so you can tell what platform each module supports
- 2 more web specific modules have been added (18. NCarousel, 19. Thumbnail), but to keep this blog short i am going to post later on them
- Control navigation menu options through model for both platforms
- Remember me feature for both platforms
- Control nested collections web visibility according to listview ViewEditMode model attribute
- How to auto-select the first item, when changing the group in the navigation control
- How to prompt when the user is about to exit the application.
- Google as default translation provider
- When you have deep object hierarchies (eg you may have a lot of permissions in your app) then you will end up with a new object create menu like the one bellow
there is sure a need for some easy way of grouping those objects. You can decorate your objects like
[NewObjectCreateGroup("Conditional")]
public class AdditionalViewControlsPermission : ConditionalLogicRulePermission {
[NewObjectCreateGroup("Conditional")]
public class MasterDetailPermission : ConditionalLogicRulePermission {
or even better write a controller that will do that automatically in case you forgot
public override void CustomizeTypesInfo(DevExpress.ExpressApp.DC.ITypesInfo typesInfo) {
base.CustomizeTypesInfo(typesInfo);
var typeDescendants = ReflectionHelper.FindTypeDescendants(typesInfo.FindTypeInfo(typeof(ConditionalLogicRulePermission)));
foreach (var typeInfo in typeDescendants) {
typeInfo.AddAttribute(new NewObjectCreateGroupAttribute("Conditional"));
}
}
and the result will look like
You can download expandframework from http://expandframework.com/downloads/download.html explore our featurecenter and sent us your feedback at our forums http://expandframework.com/forum.html


-
What Locking is all about
Transactional isolation is usually implemented by locking whatever is accessed in a transaction. There are two different approaches to transactional locking: Pessimistic locking and optimistic locking.
The disadvantage of pessimistic locking is that a resource is locked from the time it is first accessed in a transaction until the transaction is finished, making it inaccessible to other transactions during that time. If most transactions simply look at the resource and never change it, an exclusive lock may be overkill as it may cause lock contention, and optimistic locking may be a better approach. With pessimistic locking, locks are applied in a fail-safe way. In the banking application example, an account is locked as soon as it is accessed in a transaction. Attempts to use the account in other transactions while it is locked will either result in the other process being delayed until the account lock is released, or that the process transaction will be rolled back. The lock exists until the transaction has either been committed or rolled back.
With optimistic locking, a resource is not actually locked when it is first is accessed by a transaction. Instead, the state of the resource at the time when it would have been locked with the pessimistic locking approach is saved. Other transactions are able to concurrently access to the resource and the possibility of conflicting changes is possible. At commit time, when the resource is about to be updated in persistent storage, the state of the resource is read from storage again and compared to the state that was saved when the resource was first accessed in the transaction. If the two states differ, a conflicting update was made, and the transaction will be rolled back.
In the banking application example, the amount of an account is saved when the account is first accessed in a transaction. If the transaction changes the account amount, the amount is read from the store again just before the amount is about to be updated. If the amount has changed since the transaction began, the transaction will fail itself, otherwise the new amount is written to persistent storage.
XAF Build in locking mechanism
XAF’s datalayer is based on XPO , which already has an optimistic locking implementation on its core. It is enabled by default for all objects that inherit XPBaseObjet and can be disabled by using the OptimisticLocking attribute .
[OptimisticLocking(false)]
public class Client:XPBaseObject {
public Client(Session session) : base(session) {
}
}
For objects that have that attribute XPO is going to create an extra service field, the OptimisticLockingField to store that state of the object.
To see the locking mechanism in action you can perform the following steps
User 1 | User 2 |
1. Run application | 1. Run application |
2. Go to Client object detail view | 2. Go to Client object detail view |
3. XPO reads the value from the optimistic field value (initial value is zero) and stores it to memory | 3. XPO reads the value from the optimistic field value (initial value is zero) and stores it to memory |
4. User is making a change and tries to save the object - XPO queries the the optimisticfield value and compares it with the one in memory
- 2 values are equal so transaction is commited and optimisticlocking fields is raized by one
| 4. User is making a change and tries to save the object - XPO queries the the optimisticfield value (now its one cause user 1 has save the record) and compares it with the one in memory
- 2 values are not equal so a locking exception is thrown by XPO giving information about the locked object
|
| 5. User reloads the object from the database, new optimisticlocking field value is store in memory (one), user makes changes and is able to save the record |
eXpand Pessimistic locking
XPO has no means to determine any information about users, but we are in XAF context and XAF has a build in security system and we know the current user. Lets try to implement a pessimistic locking feature for our XAF applications.
By carefully looking at “3. XPO reads the value from the optimistic field value (initial value is zero) and stores it to memory “ , we can see that there is a place to minimize the locking conflicts. What if we display the detailview in “ViewMode” and add an “Edit” action? And when the Edit action is executed the object will be reloaded from the db, also when is saved the DetailView will return is View mode.
I think we are going to gain something from that even if we use the default Xpo optimistic locking. Lucky us eXpand has already the ViewEditMode attribute that can do that job for us.
Now from the Pessimistic locking definition (when it is first is accessed by a transaction). We have to decide what that means in our case. A good idea would be to lock the object at the time that is changed by a user. Of course we can enhance that in the future upon your requests. We also need a special field that will store the user that locked the record and a special attribute to mark the object for using our pessimistic locking attribute. Finally its a good idea to disable default OptimisticLocking mechanism.
All the above should be transparent to the user of our feature, we need to spent zero time when using it in future projects
Customizing XAF types is very easy as you know , just some lines of code and can do the trick.
public override void CustomizeTypesInfo(DevExpress.ExpressApp.DC.ITypesInfo typesInfo) {
base.CustomizeTypesInfo(typesInfo);
var typeInfos = typesInfo.PersistentTypes.Where(info => info.FindAttribute<PessimisticLockingAttribute>() != null);
foreach (var typeInfo in typeInfos) {
typeInfo.AddAttribute(new OptimisticLockingAttribute(false));
var memberInfo = typeInfo.FindMember(LockedUser);
if (memberInfo == null) {
memberInfo = typeInfo.CreateMember(LockedUser, SecuritySystem.UserType);
memberInfo.AddAttribute(new BrowsableAttribute(false));
}
}
}
Great! Now we need to define our specifications. Remember we are dealing with data now and with a complex feature that may evolve from fellow developers request. For those cases at eXpand we use a BDD approach, and MSpec as our BDD framework.
Here are some specs
PessimisticLockingViewController, When Object Change
» should lock the object
PessimisticLockingViewController, When objectspace rollback
» should unlock object
PessimisticLockingViewController, When ospace commited
» should unlock object
PessimisticLockingViewController, When View CurrentObject Changing
» should unlock object
PessimisticLockingViewController, When View Is closing
» should unlock object
PessimisticLocker, When object is about to be unlocked
» should not unlock if current user does not match locked user
PessimisticLockingViewController, When a locked object is open by a second user
» should not allowedit on view
PessimisticLockingViewController, When 2 users open the same object and both try to change it
» should mark as readonly last user view
PessimisticLockingViewController, When editing a locked detailview
» should allow edit for the pessimistic locking context
PessimisticLocker, When unlocking new object
» should do nothing
PessimisticLocker, When locking new object
» should do nothing
PessimisticLocker, When new object locking state is queried
» should return unlocked
and in this file you can see the implementation of them
https://github.com/expand/eXpand/blob/master/Xpand/Xpand.Tests/Xpand.Tests/Xpand.ExpressApp/PessimisticLockingSpecs.cs
As you see from the specifications when an object is locked the detailview will be read only for a PessimisticLocking context. But real world is strange we have to cover exceptions as well. What will happen if an object was locked and our application was terminated abnormally? We need an action that will force unlock the object.
Maybe there is a need for some timeout implementation there but I do not have strong ideas on this, better wait for some feedback from out there first before spending any more resources. Anyway we are very close now. What we are missing is a message that will tell which user has locked an object when it is locked.
To display the message I think we can utilize our AdditionalViewControlsProvider module. That module allows to conditionally display (When our LockingUser field is different for the current user) a message. Also allows us to conditionalize/localize the message it self. Lets see how
First step will be to use the AdditionalViewControlsRule to display the message as bellow
[PessimisticLocking]
[Custom("ViewEditMode","View")]
[AdditionalViewControlsRule("teee", "LockedUser!='@CurrentUserID' AND LockedUser Is Not Null", "1=0", "Record is locked by user {0}", Position.Top, MessageProperty = "LockedUserMessage")]
public class Client : BaseObject {
public Client(Session session)
: base(session) {
}
private string _lockedUserMessage;
[NonPersistent][Browsable(false)]
public string LockedUserMessage {
get {
var memberValue = GetMemberValue("LockedUser");
if (_lockedUserMessage != null) {
return memberValue != null ? string.Format(_lockedUserMessage, memberValue) : null;
}
return null;
}
set { _lockedUserMessage = value; }
}
}
that will create a rule at our model like the following.
and will display the message
2nd step is to refactor the attribute to something easier to use like
public class PessimisticLockingMessageAttribute : AdditionalViewControlsRuleAttribute {
public PessimisticLockingMessageAttribute(string id)
: base(id, "LockedUser!='@CurrentUserID' AND LockedUser Is Not Null", "1=0", "Record is locked by user {0}", Position.Top) {
MessageProperty = "LockedUserMessage";
}
}
and 3rd step is to refactor the LockedUserMessage property. We have seen already that dynamically adding a property is a piece of cake, but how can we dynamically add a property that has behavior such as the LockedUserMessage property?
Easy as always :), we just have to create our Custom memberinfo like
public class LockedUserMessageXpMemberInfo : XPCustomMemberInfo {
string _theValue;
public LockedUserMessageXpMemberInfo(XPClassInfo owner)
: base(owner, "LockedUserMessage", typeof(string), null, true, false) {
}
public override object GetValue(object theObject) {
var typeInfo = XafTypesInfo.Instance.FindTypeInfo(theObject.GetType());
var memberValue = typeInfo.FindMember("LockedUser").GetValue(theObject);
if (_theValue != null) {
return memberValue != null ? string.Format(_theValue, memberValue) : null;
}
return null;
}
public override void SetValue(object theObject, object theValue) {
_theValue = theValue as string;
base.SetValue(theObject, theValue);
}
}
and register it on the system.
public override void CustomizeTypesInfo(DevExpress.ExpressApp.DC.ITypesInfo typesInfo) {
base.CustomizeTypesInfo(typesInfo);
var typeInfos = typesInfo.PersistentTypes.Where(info => info.FindAttribute<PessimisticLockingMessageAttribute>() != null);
foreach (var typeInfo in typeInfos) {
var memberInfo = typeInfo.FindMember("LockedUserMessage");
if (memberInfo == null) {
var xpClassInfo = XafTypesInfo.XpoTypeInfoSource.XPDictionary.GetClassInfo(typeInfo.Type);
var lockedUserMessageXpMemberInfo = new LockedUserMessageXpMemberInfo(xpClassInfo);
lockedUserMessageXpMemberInfo.AddAttribute(new BrowsableAttribute(false));
XafTypesInfo.Instance.RefreshInfo(typeInfo);
}
}
}
Conclusion
That was a long post but the result i believe is great. All the above are implemented in eXpand framework. Next time you want to use the pessimistic lock approach presented in this blog you only have to decorate your class with 3 attributes
[PessimisticLocking]
[Custom("ViewEditMode","View")]
[PessimisticLockingMessageAttribute("AnId")]
public class Client : BaseObject {
public Client(Session session)
: base(session) {
}
private string _name;
public string Name {
get {
return _name;
}
set {
SetPropertyValue("Name", ref _name, value);
}
}
}
Download expand from http://expandframework.com/downloads/download.html and sent use your feedback or report any problems you find at our forums http://expandframework.com/forum.html
eXpand FeatureCenter implementation contains a demo of this bolg under the Miscallenous/Pessimistic locking navigation menu


-
Where they can be used?? In many places for example invoices, sales orders etc.
One could tell that this is an easy task and maybe that assumption is right but let me write down some real world requirements that can prove the opposite to you.
- Invoices Number Uniqueness should be quarantine in a multi user environment
- Number number should be sequential
- Invoices numbers may have series (eg AB-1001 for invoices coming from store sales , EP-1001 for invoices coming from patient treatment)
- Starting number must be configurable. (eg need to start numbering at 5000 this year, and next year start at 7000)
- There should be a way to reuse numbers of deleted invoices
- Storage (table schema) of invoice numbers should be controlled by the user
- End user API should be very flexible and easy
- Of course whatever you build has to be available in both win and web platforms
Now what you think? How much time do you need to build that?
If you have some XAF experience it will be about only some hrs work to create a reusable code that fellow devs and future projects can utilize. So in this post i am going to go through the process step by step
Step1: Check Devexpress support center
SC has samples for thousand of cases so before building anything it is most advisable to check there.
Searching revealed this one
How to generate and assign a sequential number for a business object within a database transaction, while being a part of a successful saving process (XAF)
That sample is using ExcplicitUnitOfWork (in other words explicit sql transaction) and that can guarantee requirement 1,2
Step2: Refactor the sample to fit your needs
Although the sample can be used as is in real world, has some requirements that are making it less flexible. It requires to inherit from a special BasePersistentObject class.
To overcome that we can introduce an interface instead and push all depended code to our SequenceGenerator class
public interface ISupportSequenceObject {
long Sequence { get; set; }
}
Now we can inherit from any object as you see in the above code and we only have to write the code that is inside the OnSaving method! .
The best part is that we do not even spent one minute to think about the validity of the generating seq numbers code. That is Devexpress job and they are best in doing it. What we could do is just track the issue for changes (maybe bugs will raise from other users in future ) I am happy with this refactoring so lets move on to the other requirements. |  |
Time spent 1.5hrs
Invoices numbers may have series
How about it?? Should be very easy if we understand what are SequenceGenerator does. It saves a number for a specific type in the database. So instead of a specific type we can refactor it to save a specific type and a series string. We can do that by introducing a new Prefix property to our ISupportSequenceObject interface and refactor our SequenceGenerator to save that prefix as well .
public interface ISupportSequenceObject {
long Sequence { get; set; }
string Prefix { get; }
}
our previous front end API was left as simple as it was b4 as you see bellow
Starting number must be configurable. (eg need to start numbering at 5000 this year, and next year start at 7000)
Ok that sounds very similar to our previous req. In essence the year is just a serie, so we could just change our class to
string ISupportSequenceObject.Prefix {
get { return Serie.ToString()+DateTime.Today.Year; }
}
and now our numbers are unique per serier+year!. And how can we control the starting number?
Very easy as everything in XAF!. Sequence numbers are saved using a persistent object, so the thing we need to do is just create a new Sequence number manual and the other numbers will follow since they are sequential.
var unitOfWork = new UnitOfWork();
var sequenceObjects = Enum.GetValues(typeof (SerieEnum)).Cast<SerieEnum>().Select(
serie => SequenceGenerator.CreateSequenceObject(serie.ToString() + 2012, unitOfWork));
foreach (var sequenceObject in sequenceObjects) {
sequenceObject.NextSequence = 7000;
}
unitOfWork.CommitChanges();
There should be a way to reuse numbers of deleted invoices
What does that mean? When an object that supports sequences (ISupportSequence object) is deleted we need to store the deleted sequence number and allow end user through UI to reuse it at a later time.
We already have an SequenceObject that stores the sequence number what we miss is an one to many relation with an object that stores deleted numbers. The following class will do the job
public class SequenceReleasedObject : XpandBaseCustomObject {
public SequenceReleasedObject(Session session)
: base(session) {
}
private SequenceObject _sequenceObject;
public SequenceObject SequenceObject {
get {
return _sequenceObject;
}
set {
SetPropertyValue("SequenceObject", ref _sequenceObject, value);
}
}
private long _sequence;
public long Sequence {
get {
return _sequence;
}
set {
SetPropertyValue("Sequence", ref _sequence, value);
}
}
}
Having the storage object we then need to grad the deleted ISupportSequence object and create a new SequenceReleasedObject
public class Invoice : BaseObject, ISupportSequenceObject {
public Invoice(Session session) : base(session) {
}
protected override void OnDeleted() {
base.OnDeleted();
SequenceGenerator.ReleaseSequence(this);
}
Now that we have our data stored in the database, we are going to use XAF to allow user to restore a deleted number.
I am thinking of a control (PropertyEditor in XAF terms) that could be used to render the sequence property of the ISupportSequence object. Also that control should have a button to the right, that on click is going to display a SequenceReleasedObject list . User will select something from the list and when the transaction is committed the SequenceReleasedObject value will replace ISupportSequenceObject value and will be deleted.
Sounds hard to you? In fact it is not so much. You have to remember to use the tools that XAF provides for you. Here is what I mean . XAF already has a similar property editor to render aggregated object properties
When editor's button is clicked then a detailview of the aggregated object is shown. We could just use the same editor and replace that part. Instead of displaying a detailview we could just display a listview of SequenceReleaseObjects. Exactly the same process we could follow for the web platform.
Now we have our property editors, but lets make dev job even more easier. Lets create a platform independent marker attribute that will handle the property editor type assignment.
First we create a marker attribute like
[AttributeUsage(AttributeTargets.Property)]
public class SequencePropertyAttribute : Attribute {
}
and a marker interface that will be implemented by both our editors
public interface IReleasedSequencePropertyEditor {
}
write a simple controller that will do the assignment as
public class CustomAttibutesController : WindowController {
public override void CustomizeTypesInfo(ITypesInfo typesInfo) {
base.CustomizeTypesInfo(typesInfo);
var memberInfos = typesInfo.PersistentTypes.SelectMany(info => info.OwnMembers);
foreach (var memberInfo in memberInfos) {
HandleSequencePropertyAttribute(memberInfo);
}
}
void HandleSequencePropertyAttribute(IMemberInfo memberInfo) {
var sequencePropertyAttribute = memberInfo.FindAttribute<SequencePropertyAttribute>();
if (sequencePropertyAttribute != null) {
var typeInfo = ReflectionHelper.FindTypeDescendants(XafTypesInfo.Instance.FindTypeInfo(typeof(IReleasedSequencePropertyEditor))).Single();
memberInfo.AddAttribute(new CustomAttribute("PropertyEditorType", typeInfo.FullName));
}
}
}
and of course decorate our property
private long _sequence;
[SequenceProperty]
public long Sequence {
get {
return _sequence;
}
set {
SetPropertyValue("Sequence", ref _sequence, value);
}
}
Time spent 3hr
Storage (table schema) of invoice numbers should be controlled by the user
Does the above remind you anything? It sure does to me. Reminds me the exact same problem we have with the Devexpress support center sample . It was not based on interfaces so if we extract an interface from our Sequence class (the one that store the numbers in the db) like
public interface ISequenceObject {
string TypeName { get; set; }
long NextSequence { get; set; }
}
and refactor again our SequenceGenerator to replace our references to SequenceObject persistent class with the new interface. Now the end user can control the schema of the table cause the only thing he has to do is implement the ISequenceObject to any object he wants.
Time spent 1hr
Conclusion
We have spent almost 6 hrs to implement all requirements but nothing was in vain. Cause the effort produce something very reusable. Bellow is our final, very easy to use and flexible approach.
public class Invoice : BaseObject, ISupportSequenceObject {
public Invoice(Session session) : base(session) {
}
protected override void OnDeleted() {
base.OnDeleted();
SequenceGenerator.ReleaseSequence(this);
}
protected override void OnSaving() {
base.OnSaving();
if (Session.IsNewObject(this))
Sequence = (int)SequenceGenerator.GenerateSequence(this);
}
private long _sequence;
[SequenceProperty]
public long Sequence {
get {
return _sequence;
}
set {
SetPropertyValue("Sequence", ref _sequence, value);
}
}
private SerieEnum _serie;
public SerieEnum Serie {
get {
return _serie;
}
set {
SetPropertyValue("Serie", ref _serie, value);
}
}
string ISupportSequenceObject.Prefix {
get { return Serie.ToString()+DateTime.Today.Year; }
}
}
that is the power of XAF! everything else should live in a reusable framework, like eXpand !


-
Continuing my effort to introduce eXpand framework to the world and with the help of some eXpand framework funs that want to remain anonymous I got invited to write an article for the Second International Scientific-Practical Conference Proceedings hosted at http://objectsystems.ru/ and edited by Pavel P. Oleynik The title of my article is Application frameworks in the real world and in a few words I explain the idea behind eXpand framework, how we have extend Devexpress XAF framework and speak about eXpand architecture. The paper can be very useful to all XAF/eXpand users! The collection can be found at Object Systems – 2010 (Winter session), The Second International Scientific-Practical Conference Proceedings, Russia, Rostov-on-Don, 10-12 November 2010. Edited by Pavel P. Oleynik
Διαβάστε περισσότερα »
-
I ll start this post with a phrase : WE HAVE TO REMEMBER TO NOT REINVENT THE WHEEL . Why I say that? XAF with the help of eXpand is now a very mature framework. Almost all the tools required for build LOB applications are here. XAF has many ruling engines (Validation,EditorState,Appearence) and eXpand also the same ( AdditionalViewControls , ModelArtifactState , MasterDetail , ConditionalDetailViews). All the above engines provide a way to describe behaviors using rules and they operate over certain Contexts. What is a context some of you may ask. The answer is simple, context means the “when” the rule is going to be evaluated. For example Validation rules have 2 predefined contexts , those are Save,Delete. I am going to give an example of how powerful a context is . Let say we have a Customer-Order relationship and we have 2 cases like the following First record of Customer Orders cannot be deleted. Only the last record of Customer Orders can be deleted So what we need to design the above rules? First
Διαβάστε περισσότερα »
-
I was discussing with a friend some days ago about his project requirements. Solution had roles but they where fixed (Like Manager, Receptionist etc) Different UI is needed for each role At runtime there should be a way to change the UI using an action He told me that he was aware of eXpand Role models and he thought that they could help his case. Sure I told him ModelDifference module can help , however the approach would be slightly different from the one you had in your mind and here is why: Although the application have roles since there is no login (wanted to change UI from an action ) we could not use ModelDifference Role models. What I suggested was to use disabled application models and then use eXpand api to just enabled them at runtime. In fact we could create as many application models as the fixed roles are in the updater with a code similar to public class Updater : ModuleUpdater { public Updater ( Session session, Version currentDBVersion)
Διαβάστε περισσότερα »
-
DevExpress XPO as all modern ORM supports object querying using linq and lamda expressions. A sample query can be written as var xpQuery = new XPQuery < Customer >( Session . DefaultSession ); IQueryable < Customer > customers = xpQuery. Where (customer => customer. Age == 25); From the above query we can get a list of customers that are 25 years old . As you can see the code is very simple. The problem When working with modularized applications you may be in context that you have access to the Customer type only at runtime or you may want to apply that query to different object types that all have an Age property. How that can be done? 1st attempt Lets register the customer to our module and use reflection to create the XPQuery. ModuleXXX moduleXxx = new ModuleXXX ();
Διαβάστε περισσότερα »
-
Rachel Hawley from DevExoress interviewed me over expand some days ago. You can read more at her blog eXpand: An open source framework built with XAF
Διαβάστε περισσότερα »
-
eXpand already provided support for various runtime member scenarios such as Calculated properties at runtime—TIMES 4 Dynamic Types with WorldCreator source code is out In the post I am going to speak about our latest addition which is support for runtime orphaned collections. Using Model Editor As you see in the image bellow you only have to create a new IModelRuntimeOrphanedColection set CollectionType attribute (1) and filter the collection by using the criteria (2). You do not have to set the Type attribute cause it is going to be filled automatically. Although the Model Editor approach with a combination of the ModelDifference module can provide great flexibility such as defining Runtime Collections for a set of Roles or Users has a drawback. It does not
Διαβάστε περισσότερα »
-
Keeping our promises eXpand team is happy to announce more control over the DevExpress XtraTreelist. We have use eXpand dynamic model creation feature to map all options of the XtraTreelist control to the model. The process used is very similar to the one used for Controlling DevExpress XtraGrid Control at runtime Controlling DevExpress AspxGridControl at runtime Controlling DevExpress XtraGrid Part-3 (The Columns) and we renew our promise to map more controls in the future versions Bonus Feature Applying the familiar techniques of the Conditional Appearance module is now possible for a TreeListEditor Both features above require to change the Default EditorType to Xpand.ExpressApp.TreeListEditors.Win.ListEditors.XpandTreeListEditor as shown in the imag
Διαβάστε περισσότερα »
-
At November 27-28 one of the biggest IT events ITPro & Dev Connections will take place in Athens. I am invited to speak over Xaf / eXpand and I will speak over the post subject: “Let your business users develop for you” ( 14:15-15:30 ). If you plan to be in Athens in that time and you like to come to the event, I suggest you secure your seat now cause as far as I know they are only few of them left Short description of my speech Definition of Garbage Code can be consider any code that is specific to a project and your company cannot reuse The solution would be to make that code configurable/ declarative and push it back to your reusable framework. You will learn all about Xaf architecture, you will see how much Xaf by default can achieve the previous by describing
Διαβάστε περισσότερα »
-
Our team is happy to announce a stable v10 version for windows platform. In order to help the community adopt expand further we have publish an expand dedicated site at www.expandframework.com . Forums are available also for discussion over various cases at http://expandframework.com/forum.html A big thanks to our sponsors http://expandframework.com/sponsors.html (only 5 for now :) but we expect more), all are happy to provide a pro license to anyone that is interested to help building expandframework. Also our team is building a wiki over our new site , so if you have modules, controllers, ideas , want to be a dedicated wiki writer or you want to actively help expandframework development please write us at [email protected] . You can find the
Διαβάστε περισσότερα »
-
In this post you are going to see how you can use on more of eXpand powerfull features. 4 different ways for creating calculated runtime properties are avaliable. XPO way –>Using Code Take a note at the CreateCalculabeMember extension method at the code bellow public class CreateRuntimeCalculatedFieldController : ViewController { public override void CustomizeTypesInfo ( DevExpress . ExpressApp . DC . ITypesInfo typesInfo) { base . CustomizeTypesInfo (typesInfo); var classInfo = XafTypesInfo . XpoTypeInfoSource . XPDictionary . GetClassInfo ( typeof ( Customer ));
Διαβάστε περισσότερα »