Friday, June 12, 2009

Help Wanted – Core .NET, SharePoint Technologies, and WCF

Company Description

A long-time Microsoft Partner, forProject Technology has a suite of companion products for Microsoft Project Server and Project Professional.   The company is breaking ground on a new product suite targeting large businesses and government agencies concerned with earned value management and integrating data from disparate systems into their Microsoft Project implementation.  But that’s just the beginning…  Our team of visionaries have more than 50 years combined experience in the enterprise project management market with a pipeline of future product ideas.

Headquartered in Texas, but with virtual offices across the United States and India, forProject Technology offers a unique work experience with a team of some of the best minds in the project management software industry.  In addition to our India-based development center, forProject Technology is building a distributed development team here in the United States.  We need very smart people with great technical skills who want to work hard to ship a 1.0 product that will likely shakeup our little corner of the enterprise project management market.

Job Descriptions

Senior Software Design Engineer - SharePoint Technologies + 2 (TWO JOB OPENINGS)

Senior design engineer responsible for designing and implementing a shared library of reusable SharePoint components including application pages, web parts, SharePoint-ready controls, and a custom view rendering framework.   Reporting to the Chief Architect.

· Shared requirements below

· 8+ years of .NET software development experience (.NET Base Class Libraries & C#)

· 5+ years of SharePoint development experience

· 8+ years of ASP.NET development experience

· 5+ years of database design and T-SQL experience

Senior Software Design Engineer - Windows Communication Foundation

Senior design engineer responsible for designing and implementing a services framework to support a specialized extract, transfer, and load system as well as other present and future service-oriented products.  Work closely with a group of design engineers who will help implement your vision and designs.  Reporting to the Chief Architect.

· Shared requirements below

· 8+ years of .NET software development experience (.NET Base Class Libraries & C#)

· 5+ years of web services development

· 3+ years of WCF development

Senior Software Design Engineer - .NET Core

Senior software design engineer responsible for implementing core system APIs and libraries to support a services-oriented, mathematically-heavy enterprise earned value management system.  Reporting to the Chief Architect.

· Shared requirements below

· 8+ years of .NET software development experience (.NET Base Class Libraries & C#)

· Expert API design skills

· Strong service-oriented design skills

· Mathematical or financial background

Technical Writer

Technical writer responsible for designing and writing product help for online and other media for a suite of enterprise project management products.  As technical writer, you will work closely with product development during all aspects of the development cycle to ensure help and system user interfaces are linguistically correct and conceptually absorbable by our end users and  customer IT staff.

· Shared requirements below

· 5+ years experience as a technical writer in a software development or IT environment

· 5+ years experience designing online help, system help, wikis, FAQs, and knowledge bases

· Expert Microsoft Word skills

· Strong understanding of the software development lifecycle

· Excellent illustration\diagramming skills using tools such as Illustrator and Visio 

Computer-Based Training/Web-Based Training Content Developer

Training content developer responsible for building primarily web-based training materials for a suite of enterprise project management products.   As content developer, you will be responsible for the entire content development lifecycle, from requirements to delivery, to ensure the company’s training materials are professional, appealing, enterprise-ready, and properly instruct our customers.   You will be responsible for evaluating CBT/WBT/e-Learning tools and technologies and making recommendations regarding our learning platform.

· Shared requirements below

· 3+ years experience as an e-Learning content developer (CBT/WBT/Others)

· Expert writing and presentation skills

· Expert in CBT/WBT/e-Learning tools and technologies

Shared Requirements

· Able to self-manage

· Excellent written and verbal skills

· Able to work from home with occasional travel

· Excellent code craftsmanship, attention to detail, and precision thinking

· Bachelors degree or equivalent work experience

· Familiar with Scrum and other agile methodologies

· Experience with Microsoft Project Server a plus

Benefits

· Medical, Dental, Eye, and Life insurance

· 401K Investment Program

· Paid Vacation

Contact me here: http://www.colbyafrica.com/forms/contact.htm

Thank you!

Monday, June 08, 2009

forProject Technology, Inc. – Help Wanted!

About a month ago I left Microsoft to take on the role of chief architect at forProject Technology, Inc (FTI).   FTI has an existing suite of products for Microsoft Project that provide advanced earned value management capabilities.   FTI is moving its product suite from the desktop to the enterprise and I am helping them move to a modern, service-oriented architecture.

The company is distributed across two continents, with management and professional services located in the US and a development center in India.  Unlike many companies that outsource to India, FTI owns the development center, which offers a great deal more stability, efficiency, and frankly quality than other arrangements.  That being said, the scope of what needs to be done has made it very apparent that we need to increase development capacity in both India and here in the US.

So, I am putting out the word to my small but loyal readership that I am looking for a couple of Ninjas, star developers, hotshots (minus the attitude), and gurus to help me.  In particular, I am looking for expert SharePoint and Windows Communication Foundation developers.  I have a team of developers in India who are very smart but they need guidance and mentoring. 

If you are interested in being a senior technical lead on my team, let me know ASAP.  You will work from home some weeks and travel to our temporary development center in Overland Park, Kansas.  More information available upon request!

Contact me here: http://www.colbyafrica.com/forms/contact.htm

Colby

Friday, May 08, 2009

Returning to BlogSpot

This blog will become my primary blog for the time being.

Thursday, January 29, 2009

MpFx Walkthrough: Creating the ProjectServer Object & Enumerating Project Information

I am taking a little break from my current project.  Whew. Only such much you can cram into your brain about SharePoint in one unbroken period of time.

Today’s topic starts at the beginning of mpFx:  How to create a ProjectServer object.  I will also demonstrate enumerating project information.  The code sample and an update to the core mpFx library are available on Code Gallery.  Here is the updated mpFx core library and here is the walkthrough.

The following references are required to build and execute:

  • Mcs.Epm.MicrosoftProject.mpFx
  • System
  • System.Core
  • System.Data
  • System.Xml

NOTE: mpFx 1.0 PREVIEW is intended to be a learning aide.  There are problems with parts of the code related to resource acquisition and clean up, performance, and generally some design decisions I made well before I had a full understanding of what the PSI was up to.  I am slowly purging those areas that need purging…

NOTE II: Parts of the PREVIEW edition implement IDisposable where it isn’t really a benefit.  DataSets implement IDispose as part of MarshalByValueComponent, but the source of IDisposable in this case is IComponent!    It is recommended that you call Dispose on any object the implements it, but in this case I can’t see any reason to.  No resources are freed as part of the call, as far as I can tell.

Okay, on to the sample!

Walkthrough

Let’s begin with the sample:

   1: using System;
   2: using System.IO;
   3: using Mcs.Epm.MicrosoftProject.mpFx;
   4: using Mcs.Epm.MicrosoftProject.mpFx.ProjectsWebService;
   5:  
   6: namespace CreateProjectServerObject
   7: {
   8:     class Program
   9:     {
  10:         static void Main()
  11:         {
  12:             /*  There are four constructors for the ProjectServer object.  This demo describes 
  13:              *  the constructor most commonly used in my projects: 
  14:              *  
  15:              *  ProjectServer(string projectServerUrl, DataStoreEnum store, ILog log)
  16:              *  
  17:              *  The constructor takes three parameters:
  18:              *  
  19:              *  1.) projectServerUrl: The full path to the Project Server PWA site
  20:              *  2.) DataStorEnum: The store on which operations will be performed
  21:              *  3.) ILog: An object implementing the ILog interface, which 
  22:              *      performs logging operations.
  23:              *      
  24:              *  At the time of writing, a single class implements ILog and implements
  25:              *  logging to the file system.  Other classes might implement ILog and 
  26:              *  persist events to the Event Log or a database.            
  27:              */
  28:  
  29:             string logDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MpFx");
  30:  
  31:             try
  32:             {
  33:                 using (Log log = new Log(logDirectory, "Demo", LogPeriod.Hourly, true))
  34:                 using (ProjectServer projectServer = new ProjectServer("http://projectserver/pwa", DataStoreEnum.WorkingStore, log))
  35:                 {
  36:                     ProjectCollection projects = projectServer.Projects;
  37:  
  38:                     foreach (EnterpriseProject project in projects)
  39:                     {
  40:                         string projectInformation = string.Format("Project Name: {0} - Start Date: {1}",
  41:                                                                   project.Name,
  42:                                                                   EnterpriseProject.StandardInfo(project).PROJ_INFO_START_DATE);
  43:                                                 
  44:                         Console.WriteLine(projectInformation);                     
  45:                     }
  46:                 }
  47:             }
  48:             catch (MpFxException exception)
  49:             {
  50:                 Console.Write(Errors.ProcessMpFxException(exception));
  51:             }
  52:             catch (Exception exception)
  53:             {
  54:                 Console.Write(exception.Message);
  55:             }
  56:  
  57:             Console.WriteLine("Projects enumerated  Press any key to close.");
  58:             Console.ReadKey();
  59:         }
  60:     }
  61: }

We will skip the details on logging for this post.  To begin, let’s look at one of the four constructors implemented on ProjectServer (I chose the one I most commonly use).  In the sample above, the constructor is called on line 34.  Here is the constructor source:

   1: public ProjectServer(string projectServerUrl, DataStoreEnum store, ILog log)
   2: {            
   3:     try
   4:     {
   5:         Log = log;
   6:  
   7:         if (!Utilities.IsValidUrl(projectServerUrl))
   8:         {
   9:             throw new ArgumentException(LibraryResources.InvalidProjectServerUrl);
  10:         }
  11:  
  12:         Settings = new ProjectServerSettings();
  13:         WebServices = new WebServices(this);
  14:         Site = new Uri(projectServerUrl);
  15:         Store = store;
  16:  
  17:         NetworkCredential = CredentialCache.DefaultNetworkCredentials;
  18:         AuthenticationType = AuthenticationType.Windows;
  19:  
  20:         WebServices.LoginWindows = new LoginWindows();
  21:  
  22:         WebServices.LoginWindows.Url = WebServices.AppendPath(projectServerUrl, ServicePaths.WindowsLoginService);
  23:         WebServices.LoginWindows.UseDefaultCredentials = true;
  24:  
  25:         WriteLogEntry(LogArea.Constructor, 
  26:                       LogEntryType.Information, 
  27:                       string.Format(LibraryResources.LogConstructor, AuthenticationType, projectServerUrl));
  28:  
  29:         WebServices.LoginWindows.Login();
  30:  
  31:         Settings.ListSeparator = WebServices.Projects.ReadServerListSeparator();
  32:  
  33:     }
  34:     catch (SoapException exception)
  35:     {
  36:         throw MpFxException.Create(exception, Log, LogArea.Constructor, LogEntryType.Error);
  37:     }
  38:     catch (ArgumentException exception)
  39:     {
  40:         throw MpFxException.Create(exception, Log, LogArea.Constructor, LogEntryType.Error);
  41:     }
  42:     catch (Exception exception)
  43:     {
  44:         throw MpFxException.Create(exception, Log, LogArea.Constructor, LogEntryType.Error);
  45:     }
  46: }

Here are a few elaborations and considerations:

  • Lines 12-15:  mpFx attempts to build an object model from the disparate web services and data of the PSI.  From the ProjectServer object, you can directly access the underlying web services, by accessing the WebServices property (projectServer.WebServices.[webServiceName]) or you can access the encapsulation objects and use the helper methods I have created.  For example, to create a project you can populate a ProjectDataSet object, directly access the project web service, call QueueCreateProject, wait on the job, and deal with the potential exceptions yourself, or you can call projectServer.Projects.Create, which looks like this:
   1: /// <summary>
   2: /// Create a project in Project Server.
   3: /// </summary>
   4: /// <param name="project">Project</param>
   5: /// <param name="validateOnly">Indicates whether the operation should validate only or perform the creation</param>
   6: /// <param name="wait">Indicates whether the call should wait on the queued job.</param>
   7: /// <returns>Queue job GUID</returns>
   8: public Guid Create(ProjectDataSet project, bool validateOnly, bool wait)
   9: {
  10:    try
  11:    {
  12:        Guid jobGuid = Guid.NewGuid();
  13:  
  14:        Parent.WebServices.Projects.QueueCreateProject(jobGuid, project, validateOnly);                
  15:  
  16:        if (wait)
  17:        {
  18:            string errorMessage;
  19:  
  20:            Parent.Queue.WaitOnJobStatus(jobGuid,
  21:                                         JobState.Success,
  22:                                         Parent.Settings.QueueStatusRetryCount,
  23:                                         Parent.Settings.QueueStatusSleepDuration,
  24:                                         out errorMessage);
  25:  
  26:            MpFxException.ThrowIfError(errorMessage, Parent.Log, LogArea.CreateProject, LogEntryType.Error);                    
  27:            
  28:        }
  29:  
  30:        return jobGuid;
  31:  
  32:    }
  33:    catch (SoapException exception)
  34:    {
  35:        throw MpFxException.Create(exception, Parent.Log, LogArea.CreateProject, LogEntryType.Error);
  36:    }
  37:    catch (Exception exception)
  38:    {
  39:        throw MpFxException.Create(exception, Parent.Log, LogArea.CreateProject, LogEntryType.Error);
  40:    }
  41: }

  • The particular constructor we used in the sample uses Windows authentication when communicating with the PSI.  NOTE: Forms authentication has NOT been tested with mpFx 1.0 PREVIEW

Returning to the sample:

  36:                     ProjectCollection projects = projectServer.Projects;

The Projects property returns a ProjectCollection object. Looking at the property implementation uncovers another design decision:

   1: public ProjectCollection Projects
   2: {
   3:     get
   4:     {
   5:         if (_ProjectCollection == null)
   6:         {
   7:             _ProjectCollection = new ProjectCollection(this);
   8:         }
   9:  
  10:         return _ProjectCollection;
  11:     }
  12: }

The lazy load pattern is widely employed (with various degrees of success and cleanliness!) throughout mpFx.   Examining the internals of the next line of code further illustrates this point:

  38:                     foreach (EnterpriseProject project in projects)

Let’s take a look at the enumerator implementation.  First, the ProjectCollection implements two IEnumerables:

   1: public class ProjectCollection : IEnumerable<EnterpriseProject>, IEnumerable<Guid>

The first enumerates EnterpriseProjects (we will get to this later) and the second enumerates Guids.  Let’s take a look at the EnterpriseProject enumerator, which is the one used in the sample:

   1: public IEnumerator<EnterpriseProject> GetEnumerator()
   2: {
   3:     return ((IEnumerable<EnterpriseProject>)this).GetEnumerator();
   4: }

And:

   1: IEnumerator<EnterpriseProject> IEnumerable<EnterpriseProject>.GetEnumerator()
   2: {
   3:     if (_projectsCollection == null)
   4:     {
   5:         LoadProjectCollection();
   6:     }
   7:  
   8:     ThrowLoadCollectionLoadException();
   9:  
  10:     foreach (KeyValuePair<Guid, EnterpriseProject> pair in _projectsCollection)
  11:     {
  12:         yield return pair.Value;
  13:     }
  14: }

Note that the backing field is first checked for nullness.  If it is null, the project collection is loaded:

   1: internal void LoadProjectCollection()
   2: {
   3:     if (_projectsCollection == null)
   4:     {
   5:         _projectsCollection = new Dictionary<Guid, EnterpriseProject>();
   6:     }
   7:     else
   8:     {
   9:         _projectsCollection.Clear();
  10:     }
  11:  
  12:     using (ProjectDataSet projectDataSet = Parent.WebServices.Projects.ReadProjectStatus(Guid.Empty, Parent.Store, string.Empty, (int)Project.ProjectType.Project))
  13:     {
  14:         foreach (ProjectDataSet.ProjectRow project in projectDataSet.Project.Rows)
  15:         {
  16:             if (project.PROJ_UID != Guid.Empty)
  17:             {
  18:                 _projectsCollection.Add(project.PROJ_UID, new EnterpriseProject(this, project.PROJ_UID, project.PROJ_NAME, Parent.Store));
  19:             }
  20:         }
  21:     }
  22: }

imagePretty straightforward so far.  The ProjectCollection contains EnterpriseProjects.  Rather than build a full model of all of the ProjectDataSet.ProjectRow members, it exposes data and helper methods.  See the class diagram to the left.

The properties and methods that are currently implement reflect the learning requirements I experienced while developing mpFx in the early days.  Essentially, every time I needed to read or act on project data in a new way, I would implement a property or method appropriate to the requirement. 

Returning to the sample:

  40:                         string projectInformation = string.Format("Project Name: {0} - Start Date: {1}",
  41:                                                                   project.Name,
  42:                                                                   EnterpriseProject.StandardInfo(project).PROJ_INFO_START_DATE);
  43:                                                 

The project name is one of a few properties that wrap elements of the ProjectDataSet.ProjectRow data row class.  The helper method EnterpriseProject.StandardInfo is short hand for project.StandardInformation.Project[0].  Accessing the StandardInformation property causes a call to be made to Project Server:

   1: public ProjectDataSet StandardInformation
   2: {
   3:     get
   4:     {
   5:         return Parent.Parent.WebServices.Projects.ReadProjectEntities(ProjectGuid, (int)ProjectEntityType.Project, Parent.Parent.Store);
   6:     }
   7: }

That’s pretty much it!

More later.  Back to SharePoint.

Wednesday, January 28, 2009

mpFx: Simon Demo

Simon from Australia needed an example of how to use mpFx to create projects, resources, tasks, and assignments.  Here it is:

http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=mpFx&DownloadId=4636.

Here is the source:

   1: using System;
   2: using System.Web.Services.Protocols;
   3: using Mcs.Epm.MicrosoftProject.mpFx;
   4: using Mcs.Epm.MicrosoftProject.mpFx.ProjectsWebService;
   5: using Mcs.Epm.MicrosoftProject.mpFx.ResourcesWebService;
   6: using Microsoft.Office.Project.Server.Library;
   7: using DataStoreEnum=Mcs.Epm.MicrosoftProject.mpFx.ProjectsWebService.DataStoreEnum;
   8: using Resource = Microsoft.Office.Project.Server.Library.Resource;
   9:  
  10: namespace EntityCreationDemo
  11: {
  12:     class Program
  13:     {
  14:         static void Main(string[] args)
  15:         {
  16:             try
  17:             {
  18:                 using (ProjectServer projectServer = new ProjectServer("http://projectserver/pwa", DataStoreEnum.WorkingStore, null))
  19:                 {
  20:                     string errorMessage;
  21:  
  22:                     Guid projectGuid = Guid.NewGuid();
  23:                     
  24:                     //  Create the project
  25:                     using (ProjectDataSet projectDataSet = EntityFactory.NewProject("Simon Test", projectGuid))
  26:                     {
  27:                         projectServer.Projects.Create(projectDataSet, false, true);
  28:                     }
  29:  
  30:                     // This is actually a bug of omission.  I should be updating the underlying project collection on creation.  
  31:                     // In order to use the current tech preview of mpFx, we brute force to refresh the projects collection 
  32:                     projectServer.Projects.Refresh();
  33:  
  34:                     //  Grab the project object
  35:                     EnterpriseProject project = projectServer.Projects[projectGuid];
  36:  
  37:                     Guid taskGuid;
  38:  
  39:                     //  Create the task
  40:                     project.CreateTask("Test Task",
  41:                                        DateTime.Now,
  42:                                        1000,
  43:                                        true,
  44:                                        true,
  45:                                        Guid.NewGuid(),
  46:                                        out taskGuid,
  47:                                        out errorMessage);
  48:  
  49:                     if (IsPsClientError(errorMessage))
  50:                     {
  51:                         return;
  52:                     }
  53:  
  54:                     Guid resourceGuid = Guid.NewGuid();
  55:  
  56:                     //  Create the resource
  57:                     using (ResourceDataSet resourceDataSet = EntityFactory.NewResource("Simon", Resource.Type.WorkResource, resourceGuid))
  58:                     {
  59:                         projectServer.Resources.Create(resourceDataSet, false, true);
  60:                     }
  61:  
  62:                     Guid assignmentGuid = Guid.NewGuid();
  63:  
  64:                     //  Create the assignment
  65:                     project.CreateAssignment(assignmentGuid, taskGuid, resourceGuid, true, true, true, Guid.NewGuid(), out errorMessage);
  66:  
  67:                     if (IsPsClientError(errorMessage))
  68:                     {
  69:                         return;
  70:                     }
  71:                 }
  72:             }
  73:             catch (MpFxException exception)
  74:             {
  75:                 Console.Write(Errors.ProcessMpFxException(exception));
  76:             }
  77:             catch (SoapException exception)
  78:             {
  79:                 Console.Write(Errors.ProcessMSProjectErrors(exception));
  80:             }
  81:             catch (Exception exception)
  82:             {
  83:                 Console.Write(exception.Message);
  84:             }
  85:  
  86:             Console.ReadKey();
  87:         }
  88:  
  89:         /// <summary>
  90:         /// Check the string returned by waitable calls to see if it is a a PSClientError
  91:         /// </summary>
  92:         /// <param name="errorMessage">The error message xml</param>
  93:         /// <returns></returns>
  94:         private static bool IsPsClientError(string errorMessage)
  95:         {
  96:             if (string.IsNullOrEmpty(errorMessage))
  97:             {
  98:                 return false;
  99:             }
 100:  
 101:             PSClientError clientError = new PSClientError(errorMessage);
 102:  
 103:             if (clientError.Count == 0)
 104:             {
 105:                 return false;
 106:             }
 107:  
 108:             Console.Write(Errors.FormatProjectError(clientError.GetAllErrors(), errorMessage));
 109:  
 110:             return true;
 111:         }
 112:     }
 113: }

Monday, January 26, 2009

mpFx 1.0 Preview Released to MSDN Code Gallery

The preview of mpFx 1.0 has been released to MSDN Code Gallery.  The tool I built to showcase some of the library’s functionality, mpFxClient, is installed along with the source.  Get it here.

I want to be clear that this is a preview release.  None of the tools, plugins, or source code is supported by Microsoft.  It is my hobby project (one of like twelve, lol).  MpFx is intended to be a learning aide for those tasked with building solutions with Microsoft’s EPM stack.  A bunch of stuff isn’t done yet, but I committed to releasing it and like any good hobby project, it will never really be finished!   The list of known issues will come out this week, so wait before flooding me with bug reports!  The exception is please let me know if you are having problems downloading plugins.

The plugins work but haven’t been tested much beyond my own use of them.

That said, I hope this is useful!

Getting Started with mpFx

After installation, check your Start menu for Microsoft Consulting Services\mpFx.  There are three shortcuts: mpFxClient, mpFx 1.0 Preview Release - Source.zip, and User Guide.  The user guide opens to a blank document.  That will be complete by the final release of 1.0.

The source shortcut opens to a zip archive of the source files.   Open the solution file in the source root directory to get started.

Auto-documentation of limited value, can be found here: http://mpfx.colbyafrica.com/docs/index.html.  The final release will include more documentation.

Running the mpFxClient Application

imageClick the mpFxClient shortcut to begin.  The main window is displayed, as shown to the right.  Click the image button to open the profiles dialog, which reads Microsoft Project Pro profile information from the registry.  If you don’t have Pro installed, you can create custom profiles and store them for future use.

MPFX 1.0 HAS NOT BEEN TESTED WITH FORMS AUTHENTICATION.

Finish inputting credential information and click the Log On button to begin.  Plugins are not installed by default, so the first thing to do is open the Plugin Gallery Browser to download plugins.

Click the image  menu item to open the browser.   NOTE: I switched hosting providers last night.  While I have tested this quite a bit, I am still getting to know this new provider.  Be patient if there are download problems but let me know immediately.

Continued below…

 

 

 

 

 

 

 

 

image The plugin gallery, as shown to the right, provides browsing and downloading functionality for plugins.  Select the plugin you want to download and click the image button to download and install it.  Once installed, you will see addition menu items in the main window’s toolstrip.

I will follow up this week with additional documentation, code samples, and tips and tricks.  For now, if you want to get started with the source, the easiest thing to do is look at the source code for one of the plugins.

I am frankly just a little beat and I still have work to do on an internal project today!

A few credits here:

Zip library for .NET is SharpZipLib.

Icons by FamFamFam.

Mike Shughrue for the event handler installation basics.

Brian Smith, Chris Boyd, Christophe Fiessinger, and Michael Jordan for helping me over the humps.

The International Institute of Learning for assigning me to a project that required in the PSI in the first place!

heart And, most important, my wife for letting me work all those long hours at night and on the weekends.   Now we have to do it all over again with the book!

 

 

ENJOY!

Sunday, January 25, 2009

Failed Economy

So, for those of you who haven’t figured it out, living on credit is death.  Really.   It is better to pack up, move (to your parents’ house, your best friend’s garage, etc), get a job doing something possibly below your (perceived) pay grade, and regroup.  I, unlike many people who are “suffering” from the economic situation, have really felt not having anything.  Just to put into perspective, I graduated from a high school that is mostly scorned by the academic community (long story), went to an itty-bitty college, bailed—largely because said high school didn’t really prepare me for anything—and ended up living on people’s couches and worse.  I took an 18 hour bus ride from Los Angles to an uncertain future in Seattle.  From that bus stop, I walked into nothing.   My mother was getting ready to take on a job as cook on a ship that was bound for a trek around the world (that sounds fancy, but she made about $4.50 an hour).  I couldn’t run home to mommy. I had just three months to find a way to survive.  So, I started selling “insurance” to people.  It was such a scam I couldn’t stomach it anymore so I went to a temp agency and ended up at Microsoft.  There is tons of irony there, because at the time, my goal was to be a software designer engineer (SDE) at Microsoft.  The year was 1994.  I was so far from being an SDE.  I was answering phones in Product Support Services.  I wasn’t even handling the cases, I was simply a dispatch to the engineers.  I bombed every interview I had beyond that clerical job of answering phones.  I wasn’t ready, and I knew it.  So I worked harder.

Things happened (because I made them happen), and my life changed.  I worked from the very bottom to something that I loved.  I didn’t stay at Microsoft, either.  I left, did my thing, and then came back.  In between, I lived in the streets of Seattle for many nights, just wandering around.  That was scary.  At times, I didn’t know where I was going to spend the night.  I kept pushing.  I read every book I could find about software development.  I hooked up with a tiny company in Redmond that put me on a contract as a developer at Microsoft.  I worked *hard*.  I work hard every day, even weekends—and I hate holidays, just ask my wife.  People need to work harder and smarter.  Don’t expect anything out of entitlement.   You aren’t entitled to anything except what you create. 

The only easy day was yesterday.  Character is not developed by wishing in one and and, well, pooping in the other.  See which one fills up faster.

So, those of you out there that are living on credit cards and hoping for the best, get yourself together and focus.  This isn’t a free ride.  We are lucky to live here and now.  If anything, we live in interesting times.  I voted for Barak Obama, but I have no more faith in him than I have had in any politician.  We need hope, but we are still responsible for our own condition.  Obama might be a catalyst that sparks a change in this country, but he cannot do it if we are collectively acting irresponsibly and simply wishing for a “better day.”

Rant.

Sunday, January 18, 2009

MpFx Preview: To Be Released to MSDN Code Gallery Next Weekend!

This a post long in the making.  Over a year ago, I started working on a Microsoft Project 2003 to Microsoft Project Server 2007 migration.  The learning curve was steep.  I had taken a hiatus from developing solutions with Microsoft Project—a long hiatus, really.  The last time I seriously did any development with Microsoft Project, aside from the occasional macro or add-in, was 2001.  I think I was blessed to come back on the scene when I did because Microsoft Project Server 2007 is truly a giant step forward for developers tasked with building solutions on the Microsoft EPM stack.  During the years between 2001 and 2006, I had become a consumer of Microsoft Project and a practitioner of project management, but all of my development efforts were focused on solving other problems in the project management space.  The work I did between 2006 and today taught me many things and I have tried very hard to share that learning with the community. 

The moniker under which this work has progressed is “mpFx”.  The term “Fx” is a short for “functon”, and has been a suffix on a few Microsoft technologies, such as .NET FX, WinFx, and others.  I chose the suffix because it rolls off the tongue nicely, and because mpFx started out as a collection of functions that blended data and functionally from several PSI web services.

The work also, by the way, changed my life completely.  I was blogging on the topic almost daily as I pushed through the SDK.  At a time when I was doing Microsoft Project development during my downtime while working on a another product, I found myself slightly obsessed with the new technology. Many things that had proven difficult or even impossible were now accessible via the PSI.  The platform is amazing.  It will see improvement but that is a fact that accompanies any major redesign or first offering of an API.  Good API design is a wicked problem and the Project PG did a great job. 

As my interest grew, I started to reach out to old friends.  Guys like Brian Kennemer, for example.  Because of the work I was doing and folding into mpFx, I was able to react to Brian’s recommendation to come on board at Microsoft a second time.  So, for that I thank Brian, Jeff, and the whole MCS EPM team.

What is mpFx?

image

Developing solutions with PSI is about two technologies: .NET 2.0 XML web services and strongly typed datasets.  First and foremost it is about XML web services.  Once a developer pushes through understanding the basic makeup of PSI, it is relatively easy to develop against the stack, but it is often not very pretty.  To create a resource, add the resource to a project team, and wait for the operation to complete, you must instantiate three web services (Resource.asmx, Project.asmx, and QueueSystem.asmx), rig all three web services, and make several web method calls.  There is absolutely nothing wrong with this and it is appropriate to the design of the PSI, but it is cumbersome and difficult to understand for the novice.

And that is exactly what I was in the early days, a novice.

So, my intention with mpFx was to create a technology bridge for the developer accustomed to dealing with object models, to the world of XML web services and strongly typed datasets.  What I was looking for was semantics such as this:

   1: using(ProjectServer projectServer = new ProjectServer("http://projectserver/pwa", "colby.africa", "yoohoo"))
   2: {
   3:     projectServer.Projects.Create("New Project");
   4: }

As you know or will come to find out, this involves many, many lines of code to rig the web services and make the calls, assuming that the .Projects.Create call waits for the operation to complete.

At its heart, mpFx is a set of libraries that coalesces PSI web services and data into an object model, which eases development and provides additional services to the developer.  The larger picture is that mPfx is a collection of tools and technologies designed to enable developers and service the broader Microsoft Project community as a whole.

Introducing mpFxClient

imageThe core of mpFx is two .NET 2.0 class libraries written in C#.  The class libraries are Mcs.Epm.MicrosoftProject.mpFx and Mcs.Epm.MicrosoftProject.mpFx.WinForms.  Together, the two libraries provide services and user interfaces designed to make developing solutions with Microsoft Project a bit easier.  As I was writing the libraries, I started building a test harness application to test the various services and user interface elements.  A few weeks ago, I decided that given the volume of services provided that a new approach to testing was needed.  The test harness code became so dense and so lacking in basic aesthetics that I chucked it all and start over.  About the same time, I release PSI Extension Generator 1.0 to MSDN Code Gallery and created an internal tool for managing Project Server event handlers.  It occurred to me that I could build a containment application which would not only serve to test mpFx, but would also be a delivery vehicle for all of my tools and utilities.

Thus mpFxClient was born.  MpFxClient is a bare-bones container application built around a plugin architecture.    Here are some of the highlights of mpFxClient:

  1. Provide a basic container application which does little besides solicit login information from the user (Project Server url, credentials, impersonation options, etc) and create a mpFx ProjectServer object
  2. Implement an interface-based plugin architecture so that I could write plugins which would consume the ProjectServer object mentioned above to do interesting things
  3. Provide a mechanism by which plugins could add or extend the container application’s user interface.  This achieved primarily but injecting a toolstrip object into the plugin so the plugin can add menu items
  4. Provide an online gallery where users can browse for plugins, select new plugins for immediate installation or update existing plugins

imageThe mpFxClient user interface is quite simple, as you can see in the image to the left.  Again, the purpose of the container application is to create a ProjectServer object and host plugins.

A couple of interesting points are that the Microsoft Project Professional profiles are read from the registry, so the login experience is similar and that impersonation is supported so you can specify credentials and log into any Project Server implementation you might have access to on the network. 

After successful login, mpFxClient looks to a sub directory beneath the installation directory for plugins.  Plugins implement the IMpFxClientPlugin interface.   Again, for more information, read this.  The plugins are loaded, injected with dependencies such as the ProjectServer object and the toolbar, as well as the parent form so MDI child relationships can be established. 

Here is a screenshot of the toolstrip after the plugins have been loaded:

image

Note that in the screen to the left, the vast majority of the dropdown buttons don’t exist.  These were added by the either the plugins themselves or the plugin host (PluginHost), which is a separate object that ties the mpFxClient application and the plugins together.

 

The PluginHost object also knows about the online gallery, which is hosted on colbyafrica.com.  The gallery is built around a file called catalog.xml.  Here is a scaled down version of the file:

   1: <plugins>
   2:   <plugin guid="6187ac29-c0ce-42b9-a5b9-7aab52dadb5e" 
   3:           name="PSI Extension Generator Plugin" 
   4:           description="A plugin for generating PSI Extension Web Services" 
   5:           author="Colby Africa" 
   6:           version="1.0.0.0" 
   7:           preview="http://code.msdn.microsoft.com/PsiXGen" 
   8:           tag="Development Tools" 
   9:           zip="PSIExtensionGeneratorPlugin.zip" /> 
  10: </plugins>

Here is a screenshot of the Plugin Gallery Browser, which will give context to the discussion:

image 

The catalog.xmls is a collection of <plugin/> elements, each describing a plugin.  The three attributes that may need some explanation are:

  1. Preview – The URL of the online resource that is displayed when a plugin in selected in the browser
  2. Tag – The tag of the plugin directs which group the plugin is arranged under.  This provides a visual cue to the user as to what kind of plugin it is
  3. Zip – The actual plugin binaries are stored in a zip file.  The zip attribute directs the plugin browser to the file which must be downloaded to install or update a given plugin

One of the first plugins I wrote was a plugin which enabled publishing plugins the gallery.  The tool helped write the tool.

Plugin Publisher Plugin

By default, the Plugin Publisher Plugin (P3) is not installed, mainly because I need to control what is published to the gallery.  If it is installed, a button is added to the Plugin Gallery Browser: image.  Clicking the button brings a login dialog up:

image

Once successfully logged in, a new button is added to the Plugin Gallery Browser toolstrip: image.  Clicking the button displays the  Add Plugin dialog:

image

Clicking the image button allows the user to select a plugin assembly.  The assembly is loaded, dependencies are detected and added to the Files list at the bottom of the dialog:

image

Clicking OK does several things.  The files listed in the Files list are zipped up, the zipped file is uploaded to the online gallery, and catalog.xml is updated with the new plugin information.  Once the upload is complete, the Plugin Gallery Browser is updated to reflect the newly available plugin:

image

Note the image button, which indicates that the plugin exists on the server but doesn’t exist locally.  Let’s take a step back and look at the Plugin Manager feature

Plugin Manager

All plugins installed locally can be managed through a single user interface, called the Plugin Manager.  The Plugin Manager allows for the loading, unloading, installation (in the case that a plugin was not distributed via the online gallery), and management of plugin options.  Here is a screenshot of the Plugin Manager dialog:

image

Note that the Event Handler plugin, which we recently added to the online gallery is not present.  We will get back to installing a plugin from the gallery in moment.  The buttons along the top of the dialog allow for loading, unloading, and installing plugins.  Also, note the Options tree node in the tree control on the left.  Each plugin exposes an Options user control, which is dynamically loaded into the Plugin Manager dialog’s tab control:

image

The options control you see above is the options control for the PSI Extension Generator Plugin.  Note that a tab exists for each installed plugin.

Installing a Plugin From The Online Gallery

Returning to the Plugin Gallery Browser and clicking the image button will install the Event Handler Plugin to the local gallery.  Several things happen when a plugin is published from the online gallery to the local gallery. First, the catalog.xml file is read to determine which zip file should be downloaded.  After the download is complete, the files are unzipped into a temporary directory, a new sub directory beneath the plugins sub directory is created, the files are moved over, and the plugin in registered with the PluginHost, which in turn loads the plugin into the application. The following screens are before and after this process occurs:

image

image

The green plus is replaced by the lightening image to indicate that the installation is complete.

The mpFxClient menu has been modified to include the Event Handler menu items:

image 

Event Handler Plugin Sneak Peek

I am not going to spend much time on any particular plugin, but I am going to give you a sneak peek at the Event Handler Plugin.  I designed the plugin after doing several engagements which used event handlers and found the SharePoint-based administration tool to be cumbersome.  Also, I took Mike Shughrue’s good work on installing event handlers and incorporated it here.   Here is a screenshot of the primary user interface:

image

The tree to the left are all of the available event handler receivers.  The Details View to the right shows the event handler specifics.  In this case, there are more than one event handler registered for the TimesheetUpdated post-event.  Thus, there are two tabs in the details view, one for each.

Clicking the image  button brings up the Add Event Handler panel:

image

From here, it is easy to add event handlers, although this is still a work in progress.

Navigating the Solution

image The mpFx project is organized by Applications and Libraries, as seen to the right.  The main application is mpFxClient, followed by the individual plugins.  The two main libraries, and the previously not mentioned Mcs.Epm.MicrosoftProject.mpFx.Client.Shared library live in the Library solution folder.

Mcs.Epm.MicrosoftProject.mpFx.Client.Shared is code shared by the plugins and mpFxClient, including the IMpFxClientPlugin interface, the PluginHost object, and a variety of shared functions.

The rest of the solution elements are dedicated to testing and demonstrations.

Mcs.Epm.MicrosoftProject.mpFx Library

I have written quite a bit on mpFx internals, so I am not going to repeat too much of that.  Suffice it to say, mpFx is designed for learning and for specific scenarios.  When I use mpFx in client engagements, I tend to trim it down just to the pieces I need.  In a high-volume, data-intensive, server-side application scenario, care should be taken in considering whether or not to use mpFx as it encapsulates whole swaths of the PSI.

At the time of writing, mpFx is over 150,000 lines of code.

Please read this post for more information.

A Simple Example

Let’s review a simple example:

   1: using System;
   2: using Mcs.Epm.MicrosoftProject.mpFx;
   3: using Mcs.Epm.MicrosoftProject.mpFx.ProjectsWebService;
   4:  
   5: namespace ConsoleTest
   6: {
   7:     class Program
   8:     {
   9:         static void Main(string[] args)
  10:         {
  11:             try
  12:             {
  13:                 using (Log log = new Log(@"C:\temp", LogPeriod.Hourly, "Test Log", true))
  14:                 using (ProjectServer projectServer = new ProjectServer("http://epm2007demo/pwa", DataStoreEnum.WorkingStore, log))
  15:                 using (ProjectDataSet projectDataSet = EntityFactory.NewProject("Demo6"))
  16:                 {                    
  17:                     projectServer.Projects.Create(projectDataSet, false, true);
  18:                 }
  19:             }
  20:             catch (MpFxException exception)
  21:             {
  22:                 Console.WriteLine(Errors.ProcessMpFxException(exception));                
  23:             }
  24:  
  25:             Console.ReadLine();
  26:         }
  27:     }
  28: }

The above example does all of the work to rig the web services, plus it adds a factory object for creating project datasets (among others) and it uses a log object which is injected to capture information about operations.

More throughout the week as I get closer to publishing the mpFx Preview to MSDN Code Gallery.

Have a great week.

Saturday, January 17, 2009

ASP.NET Session Monitor 1.0 Released to MSDN Code Gallery

A couple of years ago I was dealing with an ASP.NET application with session state issues.  This happens, if you can believe it.  I was flummoxed by the problem and the routine checks and configuration options didn’t seem to fix the problem (although there was one little thing that was causing me problems and I didn’t catch it before I went down the path I am about to write about).  I decided I needed to find a way to peer into ASP.NET session state.  Given that the web application was storing state in Microsoft SQL Server, the challenge was to figure out how to make sense of the state information stored in the database.  For background information, check out this tag from my old blog.

If you have read some of those older posts, you might note that I wrote a session monitoring application a while back.  That code is somewhere in the mists of time and I wouldn’t use it as it was anyway because it had some issues, so I decided to take another swag at it.  Also, Philippe sent me an email asking for the tool, so I thought I would oblige and deliver it to everyone.

Here it is on MSDN Code Gallery.

ASP.NET Session Monitor 1.0

I had to read my own blog posts again and figure out how this works and it is a little rough, so consider this a starter project for somebody with more time who might improve it.  The basic requirements are as follows:

  1. Create a class that monitors ASP.NET session data (if Microsoft SQL Server is backing session; otherwise, you are on your own!)
    1. Detect new sessions and provide basic information about the session
    2. Detect the removal of a session
    3. Detect new session items and provide basic information about the value (primitives and strings are directly supported)
    4. Detect session item value changes and maintain a history of the values as they change
    5. Detect session item removal
  2. Create a simple user interface to display session information
    1. Enable impersonation so session state stored in a database residing outside the current authenticating domain can be monitored
    2. Display sessions, session values, and session value history in a comprehensible manner
    3. Support direct inspection of session values of primitive types and strings
    4. Provide a property browser for non-primitive\string types (this is an area for improvement if somebody wants to build an extension that can unpack non-primitive types, but I warn you that because the session monitor does not necessarily run on the same machine where the web application is running, you might have a problem with type resolution!)

Session Monitor User Interface

image

The key feature of the user interface shown above are:

  1. Collected credentials and use Win32 impersonation if specified by the user (use default credentials by checking the “Use Current” checkbox
  2. Provide a drop-down of all available Microsoft SQL Server instances
  3. Once an instance is selected, provide a dropdown of databases (in my case, the state database is the SharePoint shared services DB for my Project Server implementation)
  4. Allow for the user to specify a polling interval.  The default is zero, so think of this as a debugging aide and not something you would point to a production server
  5. Click the image button to start monitoring and image  to stop monitoring

I will leave it to you to peruse the source code and read this deep dive to understand how this works.  

Simple Demonstration

I created a very simple web application to demonstrate the session monitor:

image

Let’s start with a screenshot of the session monitor as it monitors, with no sessions currently in play:

image

In the test application, I add primitive (which is also the name of the value) value “14” and here is the result:

image

In the test application, I add a value to the list and here is the result:

image

In the test application, I update the primitive value to 23 and here is the result:

image

You can see that the session monitor picked up the new value and displayed it below the original value.

For non-primitive values or strings, you can click the View link and see basic properties:

image

That’s it folks!  Hope this is useful to somebody :-)

Colby

Thursday, January 15, 2009

C# Code Header Designer 1.0 Released to MSDN Code Gallery

Recently I finished an engagement where the customer received the source code.  When MCS does this, we typically add a header to each source file, which contains information related to the engagement.    About two years ago, I wrote up a little application to apply a standard code header to a source tree.  I dug it up, gave it a quick polish and now I am giving it to you.  Visit the resource page on MSDN Code Gallery or click here to install now.

Here is a summary of the tool.  Check out the user guide for more information.


The C# Code Header Designer (the Designer) gives developers the ability to design code headers, save the headers for future use, and apply headers to existing source trees.

Getting Started

The Designer provides the following features to enable code header design, storage, and use:

1.) A simple editor that emulates the look of a source editor

2.) A means to add fields to a header, which are replaced by values specified by the user when the header is applied

3.) Auto fields, which are field whose values are automatically inserted into the header by the system

4.) Header save and open features, which allow for the header to be persisted as a .headerx file for later use

5.) The ability to choose a source directory and selected which files should have the header applied

6.) The ability to apply the header, which includes the ability to remove existing headers prior to applying the new header

7.) Maintain a log of actions taken on the source files

After installation, click Start\Program Files\Colby Africa\C# Code Header Designer to begin.

image

The Main Dialog

image

The File Selection Dialog

image

Enjoy!

 
Blog Directory - Blogged