Friday, September 26, 2008

Debug MOSS Workflow in Visual Studio

This blog is very late to be publishing at this point (given I ran into this a long time ago) as many know how to debug a MOSS workflow. All you need to do is:

  • Make sure that you get a debug build in the gac.
  • Restart IIS or recycle the SharePoint app pool, which ever you prefer.
  • In Visual Studio >> Debug >> Attach to Processes… >> select the w3wp.exe.

Now, do not forget to Attach to only the Workflow Code Type.



Apparently you cannot debug workflow and managed code at the same time. Otherwise, you will find yourself closing Visual Studio, bouncing IIS and re-opening Visual Studio just to debug your workflow… Doh!

Thursday, September 25, 2008

Denver K2 User Group SmartObjects

Update 4/16/2009 - here is a link to the recorded presentation.

I have been asked to speak about SmartObjects at the Denver K2 User Group. I will be doing it virtually. If you like to attend, please tune in. It will be a nice intro into all of the aspects of SmartObjects and if you have any questions, please come with them.

----------------------------

Hello everyone,

The next K2 User Group meeting in Denver is fast approaching and will be held on October 7th from 6pm to 8pm.

The meeting will again be hosted at Baxa located at 14445 Grasslands Dr, Englewood, CO 80112. Here is a map to the Baxa office. There is plenty of parking available around the building and it's fairly close to E470, so please join us in person, if possible. For those of you who cannot attend in person, we will have a Live Meeting session available. This information is located at the bottom of this e-mail.

Please RSVP to me by Friday, October 3rd whether you will be attending in person (so we can plan for food) or via the Live Meeting. If you are interested in receiving these notifications in the future, please register for the Denver User Group on the K2 Underground. If you would prefer to not be contacted again, please let me know so I can remove your name from the list.

Agenda:

6:00 - 6:15 Networking/Refreshments

6:15 - 6:30 Announcements

6:30 - 7:15 Presentation by Jason Apergis from RDA Corporation

7:30 - 7:45 Round Table Discussion

7:45 - 8:00 Meeting Wrap-up


Presentation Summary:
Jason will be talking about the beauty and power of K2 SmartObjects. During this session Jason will be going into the SmartObject basics but will also show you how to create them and their associated ServiceObjects.

Jason Apergis is a software consultant who has lived in the Washington, DC area his entire life. Jason currently works for a Microsoft Gold Partner named RDA Corporation as a technical project manager and has worked for various other consulting firms like BearingPoint and AMS. One of Jason's solutions was nominated as a finalist for a Microsoft solution of the year award in 2006 which integrated K2, BizTalk, SharePoint and InfoPath. Jason completed both his undergraduate and graduate degrees in Information Technology from Virginia Tech. Jason plays ice hockey weekly basis and is both an avid Washington Capitals and Virginia Tech Football fan.

Bring your questions or challenges for a round table discussion after the presentation to discuss with the others in the User Group. Have you used SmartObjects yet? How are you using them? What challenges have you faced?


Live Meeting Information:

Start Time:

Tuesday, Oct 7, 2008 6:00 PM MDT

End Time:

Tuesday, Oct 7, 2008 8:00 PM MDT

Occurrence:

Recurring meeting

Meeting Lobby:

Not enabled

Attendee URL:

https://www.livemeeting.com/cc/scna/join?id=CK6G4W&role=attend&pw=F59%3EKcr

Meeting ID:

CK6G4W

Attendee Entry Code:

F59>Kcr

Location:

Audio:

Two way computer audio conferencing: Off

One way computer audio broadcasting: Off

Telephone conferencing: On

Audio conferencing provider: Other

Toll free: +1 (913) 227-1219Toll: +1 (888) 269-6500

Participant code: 994729

Note: please keep your phone on mute until you are ready to speak.

Wednesday, September 24, 2008

K2 blackpearl 0807 Released

Important news 0807 build of K2 blackpearl, that I previewed here, has been released. There are major changes and bug fixes that have been addressed. Below is the announcement.

K2 blackpearl™ 0807 (4.8210.1.0) is now available!

Greetings K2 Customers!

K2 blackpearl™, the centerpiece of the new K2 platform, has the K2 blackpearl 0807 (4.8210.1.0) release now available.

The 0807 release focused on greater stability and better performance than ever before. It includes various enhancements and performance increases for the K2 Workspace, the K2 Designer for Microsoft Visual Studio 2005, and the K2 blackpearl Workflow Server to name but a few. It also includes post-0803 patches and bug fixes. New exciting features included in this release are Out of Office functionality and K2 Interop!

OUT OF OFFICE

The K2 Out of Office feature enables a user to share specific worklist items or their entire worklist with other users. The user, the out of office participant, will be able to share current and future work items with designated colleagues, and redirect all remaining items to others by creating exception rules. The additional column on the K2 Worklist allows users to differentiate between their own and the absent colleague's worklist items. This functionality ensures that important and urgent work items receive prompt attention, regardless of someone being away from their desk.

K2 INTEROP (Moving Process Definitions from K2.net 2003 to K2 blackpearl – CLIENT SIDE)
K2 Interop (short for interoperability) is the ability for K2.net 2003 process (.kpr) files to be opened in the K2 Designer for Visual Studio that ships with K2 blackpearl. It is a client-side, design-time functionality that allows converted K2.net 2003 and hybrid (K2.net 2003 and blackpearl) processes to be deployed to K2 blackpearl. With the release of K2 blackpearl, organizations with an investment in K2.net 2003 may want to leverage the advantages of the new platform and migrate their existing business processes from the old platform to the new. From a technology perspective, K2.net 2003 is distinctively different to K2 blackpearl, and K2 blackpearl offers a whole new feature set however still supporting the same K2.net 2003 features moving forward. As a result the divide between K2 blackpearl and K2.net 2003 is bridged by supporting K2.net 2003 features within the K2 blackpearl environment using K2 Interop

MIGRATION (Moving existing K2.net 2003 Process Instances to K2 blackpearl – SERVER SIDE)
K2 blackpearl 0807 is now migration-ready. The migration utility will be released in the next few weeks and is NOT available immediately. It is important to note that you will require K2 blackpearl 0807 to use the Migration Utility. The Migration capability should not be confused with K2 Interop. Migration is the physical migration of data from K2.net 2003 to K2 blackpearl. This is a server-side, runtime functionality enabled by a separate utility that migrates database records from K2.net 2003 to K2 blackpearl.

NEW ACTIVE DIRECTORY SERVICE
The New Active Directory Service provides new objects and methods to access information from Active Directory for easy access and use of information to help create business processes and workflows in K2 blackpearl. This service surfaces information from AD and has a great number of performance enhancements and added functionality along with the functionality contained in the original Service, however it does not replace the original one.

PERFORMANCE ENHANCEMENTS
Numerous performance enhancements have been implemented. Some of the highlights are as follows. Workflow Server Performance

> Improved processes execution
> Worklist & Worklist Item performance
> Worklist enhanced paging and filtering capabilities
> More granular selection on Workflow Reporting SmartObjects
> Improved memory usage on K2 Designer for Visual Studio
> Improved memory usage when starting processes and finishing work list items
> Improved batch execution of K2 processes
> SmartObject Performance (runtime execution)
> SmartObject Server data handling enhancements


ENHANCEMENTS ON K2 WIZARDS
Some changes have been made to the functionality which resides behind the scenes of the K2 Wizards – specifically the items used for Microsoft SharePoint Server integration. These wizards now leave a smaller footprint on the process definition (.kprx) files, resulting in smaller file sizes and better memory management in the different designers available for K2 blackpearl.

DOCUMENTATION
Several additions and enhancements have been made to the K2 blackpearl Documentation. The key areas of improvement are as follows.

OVERALL DOCUMENTATION
Certain topics of the K2 blackpearl documentation has been updated and new topics added. New or updated topics are marked with an asterisk image in the Table of Contents

The following sections contain new or updated information:

> K2 Concepts - Content Fields
> K2 Wizards - Chaining wizards
> Mail Event
> IPC Event
> SharePoint Records Management Wizard
> Deploy Project Wizard
> K2 SmartObject Integration - SmartObject Association
> K2 for Visual Studio - K2 Design Canvas
> K2 Interop
> K2 Workspace - Management Console
> Out of Office
> Standard Reports
> User Preference
> K2 Developer Reference - Architecture of K2 Platform
> Users
> Extending the K2 Platform

Please take the time to review the latest K2 blackpearl 0807 (4.8210.1.0) Release Notes and product documentation before installing 0807 to ensure you understand the enhancements, fixed issues, and outstanding known issues.

For installation, we urge you to look at the Release Notes prior to Installation! There are two issues that you should be aware of before installing 0807 that may, depending on your current blackpearl installation, overwrite custom configurations.

DOWNLOAD K2 BLACKPEARL 0807 (4.8210.1.0)
K2 blackpearl 0807 (4.8210.1.0) is available at
https://portal.k2workflow.com/downloads/bp/Default.aspx.
License keys can be obtained at
https://portal.k2workflow.com/licensekey/Default.aspx.

The page can also be found via the Support > License Key Request > License Key menu on portal. The 0807 installer is slipstreamed, so if K2 blackpearl is not already present, the installer will install K2 blackpearl with the 0807 updates.

Saturday, September 20, 2008

MOSS Workflow History Best Practice

If you are designing a workflow solution in MOSS on of the first things you may think about incorporating into your solution is the Workflow History. The value proposition of using the Workflow History was:

  • When building workflows there is an activity you can drag right onto the process which will set values into the Workflow History table. It is really simple to use.
  • Users will be able to see all of the information immediately right there on the screen in the Workflow State page.

However after doing some more thinking and contacting a few of my colleagues I quickly decided not to use it to any great degree. Here are a couple of the issues that are with it.

  • First, the workflow history table will be cleaned up by SharePoint after a period of time. This time can be configured using the AutoCleanupDays however it will be run at some time. It will not actually delete the logs, but it will delete the log associations to the workflow instance and it will delete any tasks that are associated to the workflow instance.
  • Second, I got concerned with sizing of the workflow history list in general given the 2,000 item rule for a list. If you use it a lot you will quickly eclipse this and there can be performance issues. So the workflow history will have to have data deleted from it on a regular basis.
  • Third, in my opinion there is no real good way to search, query or do business reports of this data.
  • Fourth, since workflows can be removed from a list. Once the workflow is deleted, if you go back to the item the workflow instances will be deleted. This basically means to me that this data is not well managed.

The best practice in my opinion is to use the workflow history if you want to print out some quick information to the user on the workflow page or if you want to use it as a way to do some logging. It should not be used as a solution to satisfy auditing requirements. If you have that as a requirement, I would highly recommend dumping some data out to a custom database which can scale and is easier to query.

References:

Workflow Task Form Data Connections

Error

While I was working with a state workflow in SharePoint I ran into a problem with make secondary data connections my Task forms. I was getting the infamous "5566 An error occurred accessing a data source".

Background

Well Google was giving my tons of hits on the error but nothing helpful at all. I basically had a custom web service that I needed my Task form to use. Here was the situation:

  • I had a data connection a web service which I had converted and placed into a Connection Library in SharePoint.
  • Had changed the form properties to be Full Trust.
  • When I ran the preview of the InfoPath, the data connections were successful.
  • Here is where it gets interesting. I was able to deploy the form through central administration (because the form had full trust) and then associated the content type to a form library. When I opened the form as a web enabled form, it worked!

That last bullet completely confused me. Why would it work if I deployed the form manually but when the same InfoPath form was being used in a workflow it would not work?

Solution

After digging at it for a while this is what I found. Data connections that were executed through a rule would work however and data connections that were executed when the form was opened would not. For instance, I had a dropdown that I wanted to be loaded when the form was opened. So when I created the data connection on the last step of the wizard I set it to "Automatically retrieve data when the form is opened". My solution was to clear that checkbox and then go to Tools >> Form Options >> Open and Save >> and press the Rules button in the Open behavior section. I then added a Rule to query the data connection and then everything worked. Really strange…

Thursday, September 11, 2008

DelayActivity Not Working and Creating Simple Escalation

Scenario
Basically I want to start adding some escalations into my SharePoint state workflows. I want to send emails to people who have been assigned a task.

Error
When I put a Delay Activity into my state workflow, the delay activity would not work. No errors; no nothing. I put in some break points and the event definitely fires however it never wakes up to send the email.



Resolution
I did some searching and found out rather quickly this seems to be a well known error. Specifically this SharePoint team blog lead me to KB 953630 which discusses the issue. To resolve the issue I had to resolution 1 and 4. First I had to install KB 932394 which just required me to bounce both IIS and the SharePoint Timer service. Next I had to modify the Job-workflow timer using the Stsadm. Let's say you have a reminder that must be sent within two hours, if the Job-workflow time job is configured to go off once a day, your reminder will not be sent.

The reason why I had to install this KB is because my client has not installed the Infrastructure Update for Microsoft Office Servers (KB951297). If you have not installed it yet, please do so! There are a ton of good fixes.

Setting up the Escalation
So to implement an escalation into my state workflow, I simply added two Event Driven activities by right clicking the state. The first uses an OnTaskChanged activity which waits for a user action. The second is the escalation.

Now in the EscalationActivity, I simply put in a delay, an email and a log. Done.
Escalations in Thoughts
In K2, I do not even have to think about how to set up escalations but I had to figure out how to do in WF. First of all from what I read the DelayForActivity and DelayUntilActivities activities that you may see in the tool box have some challenges and work well in SharePoint Designer; not in Visual Studio fromw what I read. I knew I really did not need to use them, so I stuck just to DelayActivity.

I found these two blogs sending a reminder in a sequential workflow and using nested StateActivity to send regular reminders in Visual Studio SharePoint State Machine Workflow. Really they were overkill from what I found and the second article basically was hacking at the workflow to achieve a result where a user will continue to receive reminders until the person takes action. LET ME IMPLORE YOU; I would challenge any business user who claims this is a requirement for an everyday business process. I have seen way to many scenarios using workflow engines (at different client) where set up complex business rules and had expectations that users would be doing stuff in rapid fashion. Trust me, you are building a spam machine and the business users will hate you for it, especially if escalations are going up to the CEO. I HIGHLY recommend that you put in simple escalation rules or none at all until you can do some analysis of the workflow once it has been automated. Once you start collecting rich data about the process, you will identify bottlenecks and that is when you want to start introducing escalations.

Wednesday, September 10, 2008

WF Error Correlation Token Already Initialized

Error


This is another error I ran into which I found really interesting.


System.InvalidOperationException: Correlation value on declaration "managerTaskToken" is already initialized. at System.Workflow.Runtime.CorrelationToken.Initialize(Activity activity, ICollection`1 propertyValues) at System.Workflow.Activities.CorrelationService.InvalidateCorrelationToken(Activity activity, Type interfaceType, String methodName, Object[] messageArgs) at System.Workflow.Activities.CallExternalMethodActivity.Execute(ActivityExecutionContext executionContext) at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(T activity, ActivityExecutionContext executionContext) at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(Activity activity, ActivityExecutionContext executionContext) at System.Workflow.ComponentModel.ActivityExecutorOperation Run(IWorkflowCoreRuntime workflowCoreRuntime) at System.Workflow.Runtime.Scheduler.Run()


I did some and I found this rather quickly - http://social.msdn.microsoft.com/Forums/en-US/sharepointworkflow/thread/347537b5-7710-4576-98e9-fcd7b023ff0e/


Resolution


This error was really interesting to me and had to do with my lack of knowledge of the inner workings of WF. Here is a screenshot of an early iteration of sequential workflow I am creating.




In the workflow, the manager could reject back to the requestor and then the requestor will submit back to the manager for approval. Simple enough right? This error would occur after all of the resubmission events had completed and the state had gone back to the ManagerApproval state. What is happening is that I have different tasks created for each state which have their own tokens. To resolve the error make sure that on the CreateTask properties window that you select the name of the "state" for the OwnerActivityName and not the name of the "workflow" (in this case I select ManagerApproval). When the correlation token is set to the "state" it will be re-initialized when the state is created again (i.e. when the requestor resubmits back to the manager). The one thing to take note of is that once you do this the task cannot be reused in other states.

WF Error with Separate Association and Initiation Forms

Error

I came across another interesting error to today when building some workflows in Visual Studio for SharePoint.

Critical The form template failed to load. (User: SHAREPOINT\administrator, Form Name: InitiationForm, IP: , Request: http://mossserver:28921/_layouts/IniWrkflIP.aspx?List=0ffc0061-eb35-4533-80e8-2b488c4c8371&ID=1&TemplateID={611ca686-af64-497e-b3c4-debbb8421a56}&Source=http://mossserver:28921/createuer/Lists/aaa/AllItems.aspx, Form ID: urn:schemas-microsoft-com:office:infopath:InitiationForm:-myXSD-2008-09-09T17-43-22, Type: SchemaValidationException, Exception Message: Could not find schema information for the element 'http://schemas.microsoft.com/office/infopath/2003/myXSD/2008-09-09T15:06:55:myFields'.) 2b07a15f-6e26-440f-9ed2-351042351288

I did some searching and came up with this link which gave me a lead - http://social.msdn.microsoft.com/forums/en-US/sharepointworkflow/thread/83264f93-ebe3-49ec-bd6b-95ee02df4d8a/

Resolution

Well after re-reading my recommended book by Scot Hillier I noticed he had an obscure step where he took the Association InfoPath form he had created and then created the Initiation Form from it. Why he did that; well he did not explain other than saying the forms needed to be the same. Once I did this problem resolved. I then stumbled across this in my other readings which basically says the schemas of both of the forms must be the same.

I tried to do some more searching on why this is the case but I could not find anything. Really this dependency makes no sense what so ever. I really look at the association form as the form that is used to capture information related to the association of the workflow to the list while the initiation form would capture information that is only related to the workflow instance that is about to be created. An example would be that the association form must take a configuration to a database, web service address, or even a designed guid to another list that will be used by the workflow. While the Initiation form would capture such information as the manager's name that must approve. Right now, I have to combine all this into a single form.

WF Error: event receiver context for Workflow is invalid

Error

I ran into an interesting error when building up some WF State Machine Workflows for SharePoint. I had no problems building some sequential workflows and then I started building a fairly complex State Machine Workflow and I got a really useless error message that gave me no insight at all:

Value cannot be null

System.InvalidOperationException: The event receiver context for Workflow is invalid. at Microsoft.SharePoint.SPEventReceiverDefinition.ValidContext() at Microsoft.SharePoint.SPEventReceiverDefinition.ValidReceiverFields() at Microsoft.SharePoint.SPEventReceiverDefinition.GetSqlCommandToAddEventReceivers(IList`1 erds) at Microsoft.SharePoint.Workflow.SPWinOESubscriptionService.CommitNewSubscriptions(Transaction txn, IList`1 erds)

Error in commiting pending workflow batch items: System.InvalidOperationException: The event receiver context for Workflow is invalid. at Microsoft.SharePoint.SPEventReceiverDefinition.ValidContext() at Microsoft.SharePoint.SPEventReceiverDefinition.ValidReceiverFields() at Microsoft.SharePoint.SPEventReceiverDefinition.GetSqlCommandToAddEventReceivers(IList`1 erds) at Microsoft.SharePoint.Workflow.SPWinOESubscriptionService.CommitNewSubscriptions(Transaction txn, IList`1 erds) at Microsoft.SharePoint.Workflow.SPPendingWorkBatch.ProcessWorkItemBatch(Transaction transaction, Work method, IList`1 workItemBatch) at Microsoft.SharePoint.Workflow.SPPendingWorkBatch.Commit(Transaction transaction, ICollection items)

I did some searching and came up with some links but none of them helped:

Resolution

If you are getting this error basically you have somehow messed up the TaskID, TaskProperties, AfterProperties, BeforeProperties, or even your CorrelationTokens. When I say messed up, these values have probably not been initialized correctly.

In my case, I had forgot to do TaskId = Guid.NewGuid(); in one of my CreateTask event handlers and bam, I got this error.

Saturday, September 6, 2008

Creating Custom SmartObjects Services Part 2

Introduction


This is a second part to the first article I wrote some time ago on how to create a custom SmartObject Service and how to deploy it. It was completely based off Dynamic SQL Service located in the K2 blackmarket. The thing was the service was geared completely towards generating the interface based on the schema of a SQL Database. So I was lead down the path that I had to follow this pattern to define my SmartObject Service. Well I was playing around the other day and I saw a different project on the K2 blackmarket called the AD Interop. I read the code and I was flabbergasted. Apparently it is possible to create a regular class and then decorate the class, properties and methods with attributes to describe it. I dropped Colin an email saying to check this out and we both agreed this is a much cleaner way of building SmartObject Services which are not being generated dynamically.

I read through the AD Interop project and there were a few things which the project owner did not finish out like how to handle exceptions and how to properly pass configuration values from the ServiceBroker to the SmartObject Service. Below is a proof of concept I created for an employee SmartObject Service. Now it is not the most verbose implementation but should lead you in the right direction when trying to create a SmartObject Service.

Employee Object

First I actually went back to my first article and following my instructions for create a project. Then I created an Employee object. Notice it does not inherit from anything special. Notice the SourceCode.SmartObjects.Services.ServiceSDK.Objects.ServiceObject attribute which has been added to the class. It describes the Employee object.

using System;
using System.Collections.Generic;
using System.Text;
using SourceCode.SmartObjects.Services.ServiceSDK.Attributes;
using SourceCode.SmartObjects.Services.ServiceSDK.Objects;
using SourceCode.SmartObjects.Services.ServiceSDK.Types;

namespace CustomTestService
{

[ServiceObject("EmployeeServiceObject",
"Employee Service Object",
"This is a test Employee Service Object.")]
class Employee
{

}
}

Next I created some attributes and properties for the Employee object. Again nothing special. All of the properties are decorated with SourceCode.SmartObjects.Services.ServiceSDK.Objects.Property which describes name of the property, the type, a display name for the property and the description. The only property of any special interest is the DatabaseConnection property. This will only be used by the service broker. The service broker will set the database connection value that was set in the K2 workspace. Note that properties that are not exposed through a method are not visible in a SmartObject definition and cannot be set. This was a concern of mine because I did not want to expose this property publically.

        #region attributes

private string _databaseConnection = "";

private int _employeeNumber;
private string _department = "";
private string _firstName = "";
private string _lastName = "";
private string _email = "";

#endregion


#region properties

[Property("EmployeeNumber", SoType.Number,

"Employee Number", "Employee Number of employee")]

public int EmployeeNumber {

get {

return _employeeNumber;

}

set {

_employeeNumber = value;

}

}



[Property("Department", SoType.Text,

"Department", "Department of employee")]

public string Department

{

get

{

return _department;

}

set

{

_department = value;

}

}



[Property("FirstName", SoType.Text,

"First Name", "First Name of employee")]

public string FirstName {

get {

return _firstName;

}

set {

_firstName = value;

}

}



[Property("LastName", SoType.Text,

"Last Name", "Last Name of employee")]

public string LastName {

get {

return _lastName;

}

set {

_lastName = value;

}

}



[Property("Email", SoType.Text,

"Email", "Email of employee")]

public string Email {

get {

return _email;

}

set {

_email = value;

}

}



/// <summary>

/// Note this property was created to pass a value from the

/// EmployeeServiceBroker to the Employee object. This property

/// is not exposed through any of the methods.

/// </summary>

[Property("DatabaseConnection", SoType.Text,

"Database Connection", "Connection string to the database.")]

public string DatabaseConnection

{

get

{

return _databaseConnection;

}

set

{

_databaseConnection = value;

}

}

#endregion

Finally I created three methods: GetEmployee, HireEmployee, and GetEmployees. These are stubs and with no real implementation. Notice that each method is decorated with SourceCode.SmartObjects.Services.ServiceSDK.Objects.Method which takes the name of the method, the method type, a display name, and description. As well, there are three arrays which define the required fields, input fields and output fields. Notice the methods always return itself and any property that is defined as an input field to the method will have a value populated into it which can be used to perform operations (like searching for an employee based on their employee number). I was really happy to see I was able to easily return a collection of Employee objects for a List operation.


        #region Methods



/// <summary>

/// This method will get an Employee based on the Employee Number.

/// The Employee number is required.

/// </summary>

/// <returns></returns>

[Method("GetEmployee", MethodType.Read, "Get Employee",

"Method will get Employee based on Employee ID.",

new string[] {"EmployeeNumber"},

new string[] {"EmployeeNumber"},

new string[] {"FirstName", "LastName", "Email", "Department"})]

public Employee GetEmployee()

{

try

{

///Write code here to fill in the object from where ever.

///this.EmployeeNumber; will contain the employee number

///value that was sent by the caller.



///Filling in some test data.

FirstName = "Jason";

LastName = "Apergis";

Department = "Professional Services";

Email = "jason@foobar.com";

}

catch (Exception ex)

{

throw new Exception("Error Getting an Employee >> " + ex.Message);

}



return this; //return the Employee object to the Execute() call

}



/// <summary>

/// This method will create an Employee. First name, last name

/// and department are required. An employee number and email

/// address will be generated as a result of the creation of the employee.

/// These two values will be returned.

/// </summary>

/// <returns></returns>

[Method("HireEmployee", MethodType.Execute, "Hire Employee",

"Method will complete the hire of an email creating a Employee Number and Email Address.",

new string[] {"FirstName", "LastName", "Department"},

new string[] {"FirstName", "LastName", "Department"},

new string[] {"EmployeeNumber", "Email"})]

public Employee HireEmployee()

{

Employee employee = new Employee();



try {

///Write code here to save the object to where ever.

///this.FirstName, etc. to get values provided by the caller.



///returning some test values

EmployeeNumber = 1;

Email = "jason@foobar.com";

}

catch (Exception ex)

{

throw new Exception("Error Hiring an Employee >> " + ex.Message);

}



return this; //return the Employee object to the Execute() call

}



/// <summary>

/// This method will get a list of employees. Providing no value

/// will get all employees. Providing a Department name will get

/// all employees for the department. This is because the Department

/// field is not required.

/// </summary>

/// <returns></returns>

[Method("GetEmployees", MethodType.List, "Get Employees",

"Method will get Employees.",

new string[] { },

new string[] {"Department" },

new string[] {"EmployeeNumber", "FirstName", "LastName", "Email" })]

public List<Employee> GetEmployees()

{

List<Employee> employees = new List<Employee>();



try

{

///Check of there is a Department value

if (String.IsNullOrEmpty(Department))

{

///Get all Employees

}

else {

///Get Employees based on department id

}



///Filling in some test data.

Employee emp1 = new Employee();

emp1.EmployeeNumber = 0;

emp1.FirstName = "Jason";

emp1.LastName = "Apergis";

emp1.Department = "Professional Services";

emp1.Email = "jason@foobar.com";



employees.Add(emp1);



Employee emp2 = new Employee();

emp2.EmployeeNumber = 1;

emp2.FirstName = "Ethan";

emp2.LastName = "Apergis";

emp2.Department = "Professional Services";

emp2.Email = "ethan@foobar.com";



employees.Add(emp2);

}

catch (Exception ex)

{

throw new Exception("Error Getting Employees >> " + ex.Message);

}



return employees; //return the Employee objects to the Execute() call

}



#endregion

Now below is the EmployeeServiceBroker which inherits from ServiceAssemblyBase. In the GetConfigSection() override is where the DatabaseConnection service configuration is defined. When the service instance is created in the K2 Workspace, the administrator will be required to set a connection string which will be associated to the specific service instance. In the DescribeSchema() method notice that the Employee object is add the Service's SmartObjects list. Unlike my first article, nothing more needs to be done in this method because the definition is on the Employee object. Finally I had to override the Execute() method. The Execute() method actually does not have to be overridden, however I am overriding it so that I can pass the database connection string for the SmartObject service instance to the Employee object. As well, I wanted to properly send exception messages using the ServicePackage.

using System;

using System.Collections.Generic;

using System.Text;

using SourceCode.SmartObjects.Services.ServiceSDK;

using SourceCode.SmartObjects.Services.ServiceSDK.Objects;

using SourceCode.SmartObjects.Services.ServiceSDK.Types;

using System.Data;

using System.Data.SqlClient;



namespace CustomTestService

{

public class EmployeeServiceBroker : ServiceAssemblyBase

{

public EmployeeServiceBroker()

{



}



public override string GetConfigSection()

{

this.Service.ServiceConfiguration.Add("DatabaseConnection", true, "Default Value");



return base.GetConfigSection();

}



public override string DescribeSchema()

{

//Custom service object

Type employeeType = typeof(Employee);

base.Service.ServiceObjects.Add(new ServiceObject(employeeType));



return base.DescribeSchema();

}



/// <summary>

/// This method does not have to be implemented. However it is used to set

/// configuration values that are set in the K2 Workspace. As well, exceptions

/// from the Employee object are handled here.

/// </summary>

public override void Execute()

{

try

{

foreach (ServiceObject so in base.Service.ServiceObjects)

{

if (so.Name == "EmployeeServiceObject")

{

string server = base.Service.ServiceConfiguration["DatabaseConnection"].ToString();

so.Properties["DatabaseConnection"].Value = server;

}

}



base.Execute();

}

catch (Exception ex) {

string errorMsg = Service.MetaData.DisplayName + " Error >> " + ex.Message;

this.ServicePackage.ServiceMessages.Add(errorMsg, MessageSeverity.Error);

this.ServicePackage.IsSuccessful = false;

}

}



public override void Extend()

{

//throw new Exception("The method or operation is not implemented.");

}



}

}

Next all I needed to do was deploy the SmartObject service which I describe how to do here.


Employee SmartObject

Now that I have my Employee SmartObject Service deployed, I needed to create an Employee SmartObject. The following is a screenshot of a quick SmartObject that I threw together.


Test Stub

Finally I created a little command line application to test the SmartObject and Service that I had created. I added references to SourceCode.SmartObjects.Client and SourceCode.Hosting.Client and I was off and running.

using System;

using System.Collections.Generic;

using System.Text;

using SourceCode.SmartObjects.Client;

using SourceCode.Hosting.Client.BaseAPI;



namespace TestSO

{

class Program

{

static void Main(string[] args)

{

SmartObjectClientServer server = new SmartObjectClientServer();



try

{

SCConnectionStringBuilder cb = new SCConnectionStringBuilder();

cb.Host = "BLACKPEARL";

cb.Port = 5555;

cb.Integrated = true;

cb.IsPrimaryLogin = true;



//Connect to server

server.CreateConnection();

server.Connection.Open(cb.ToString());



//--------------------------

//Hire the Employee

//Get SmartObject Definition

SmartObject newEmployee = server.GetSmartObject("CustomTestEmployee");



//Hire Employee

newEmployee.Properties["FirstName"].Value = "Jason";

newEmployee.Properties["LastName"].Value = "Apergis";

newEmployee.Properties["Department"].Value = "Professional Services";



//Hire the employee

newEmployee.MethodToExecute = "HireEmployee";

server.ExecuteScalar(newEmployee);



//values returned

System.Diagnostics.Trace.WriteLine(

newEmployee.Properties["ID"].Value);



System.Diagnostics.Trace.WriteLine(

newEmployee.Properties["Email"].Value);



//--------------------------

//Get the Employee

//Get SmartObject Definition

SmartObject employee = server.GetSmartObject("CustomTestEmployee");



//Set properties

employee.Properties["ID"].Value = "1";



//Get the record

employee.MethodToExecute = "GetEmployee";

server.ExecuteScalar(employee);



System.Diagnostics.Trace.WriteLine(

employee.Properties["FirstName"].Value);



System.Diagnostics.Trace.WriteLine(

employee.Properties["LastName"].Value);



System.Diagnostics.Trace.WriteLine(

employee.Properties["Email"].Value);



//--------------------------

//Get employees using a Department Name

//Get SmartObject Definition

employee = server.GetSmartObject("CustomTestEmployee");



//Set properties

employee.Properties["Department"].Value = "Professional Services";



//Get the records

employee.MethodToExecute = "GetEmployees";

SmartObjectList employees = server.ExecuteList(employee);



//Loop over the return employee values

foreach (SmartObject so in employees.SmartObjectsList) {

foreach (SmartProperty property in so.Properties) {

System.Diagnostics.Debug.Write(

property.Name + "=" + property.Value);

}

}

}

catch (Exception ex)

{

throw new Exception("Error Creating Request >> " + ex.Message);

}

}

}

}

Conclusions


I was extremely happy with how clean this was. I was able to create an Employee object, decorate it and then it was deployed as a Service which K2 or other applications across my enterprise can use. The implementation is extremely decoupled and I had to put no thought to it at all. Going down this path, it is possible to re-use Domain Layers that may already be written and expose them to the enterprise on the large.

Thursday, September 4, 2008

blackpearl 0807 Build in Beta

Big news, the 0807 build is now been released as a public beta and can be tested if you so choose. It is available in the K2 Portal. Obviously do not install on a production environment but if you have a test area and want to provide some feedback, please do by opening a ticket (select K2 blackpearl – Beta as the system). They indicated the beta cycle will be about two or three weeks.

Here is the summary from the release notes:

  • Enhanced Active Directory Service Object
  • K2 Visual Studio stability
    • References
    • Source Control (support for TFS/VSS)
    • Memory management enhancements
  • Workspace
    • Enhanced paging and filtering
    • Multi-domain support
    • SmartObjects
    • Out-of-memory issue fixes
    • Deployment fixes on associations
    • Run-time performance fixes
    • More granular selection of workflow reporting SmartObjects on process deployment
  • Out of Office functionality
  • Workflow server – worklist "no data" issue fixes
  • K2 blackpearl Designer for Visual Studio 2005
    • Minor Wizards Sweep
    • Memory management enhancements
  • Migration for K2.net 2003 to blackpearl
    • Migration Tools – (SERVER SIDE) this is a separate utility that will be released based on the K2 blackpearl 0807 code base targeted for the week following K2 blackpearl 0807 RTM. This tool is used to migrate an existing K2.net 2003 databases/server to a new K2 blackpearl databases/server. All the details for this process, including prerequisites, are included in the migration tools documents. The latest version is available under Beta components on the customer portal. The Migration Tool is currently NOT compatible with K2 blackpearl 0807, but will be shortly.
    • 2003 Interop – (CLIENT SIDE) this is the ability to open a k2.net 2003 project (kpr) in K2 blackpearl Designer for Visual Studio and deploy that project to K2 blackpearl server. The "interop" code only happens on the client side. Once Finish is clicked on the old K2.net 2003 template, K2 takes all the code that would have been generated in 2003 and uses that in a native K2 blackpearl activity. Server side it is just executing .net code – same as if you used the migration tools above. Interop also provides the ability to build "hybrid‟ processes which include a mixture of K2 blackpearl wizards and K2.net 2003 templates in the same process. The 2003 Interop functionality is available in this release of K2 blackpearl 0807.

As well, patches associated to the 0803 build have been incorporated into the 0807 build.

I also scanned through the list of things that were fixed; there are a ton of them. There were a couple of issues that were resolved that were of personal interest to me:

  • Fixed the no users or groups in a Role error.
  • You can now change the license key using the K2 workspace instead of having to rerun the configuration wizard.
  • Roles can now be used with Mail Events!
  • Escalations and working hour calculations are now working.
  • Several little design time InfoPath integration issues.
  • Lag time when working with custom code events when making modifications the first time.
  • Extender projects will be properly checked into VSS.
  • When modifying an IP form though VS studio sometimes the message stating that the form has been successfully updated would never appear, subsequently locking VS has been fixed.

Fixed the issue with using the process instance data fields (ID and StartDate) with SmartObject event.