Cascade Skyline - with Microsoft Logo and Project Support header - author Brian Smith

  • Brian Smith's Microsoft Project Support Blog

    Project Conference - What do you want to hear about Administration?

    • 9 Comments

    I have been asked to present a session at the Project Conference titled "Administration of an EPM Solution" and wanted to check that I would be addressing the right issues. 

    The session will cover administrative functions both in Windows SharePoint Services and Project Server.  The SharePoint 3.0 Central Administration features of Diagnostic Logging, Shared Services (including provisioning new PWA sites), Timer jobs, Backup/Restore and administrative account management will be covered.  In Project Server I will be looking at the administrative side of Cube Building, Custom Fields, Active Directory Synchronization, User Management, Administrative Backup/Restore,  Project Workspaces and last and definitely not least Queue Management.  The focus will mainly be on new and changed administrative features of Project Server 2007 so not too much time will be spent on features that have changed little since 2003.  This session will also not be covering the administration of Project Portfolio Server.

    Am I missing anything?

    Technorati Tags: Project Server 2007

  • Brian Smith's Microsoft Project Support Blog

    When are server-side events supposed to fire?

    • 14 Comments

    We have had a few questions on events, and the fact that sometimes, or more correctly, in some circumstances, they don't fire.  So if you are using the PSI to make things happen then all should work and usually the description gives you a good idea when they will fire.  However, you might also expect that doing a similar thing through Project Professional or Project Web Access will fire the same event.  In many cases this is true - a publish is a publish is a publish and wherever you publish a project from will fire the "publish" events.  However in saving from Project Professional after deleting some entity in the project you will not see the "EntitiesDeleted" event fire - that would get triggered by a PSI action doing the same thing.  Project Professional and PWA make use of the same PSI web services in many cases, but also have a private winproj web service  and pwa web service.  It is when using these "private" web services that event behavior may be different. 

    Give us feedback if you are missing some events that as an ISV would make your integration to Project Server easier. 

    The SDK should be going through another revision later this month and we will be updating the event area to give a little more information..

    Technorati Tags: Project Server 2007

  • Brian Smith's Microsoft Project Support Blog

    Creating Issues and Linking to Tasks. Better than task notes?

    • 6 Comments

    There are some limitations with the task notes field and the PSI which are discussed elsewhere on the blogs with a potential workaround of using VBA to access the RTF information.

    Another workaround you might consider is to use SharePoint and the Project Workspaces and instead of having a task note use the issues or risks lists or document library associated with the project.

    In the attached sample I have modified the LoginDemo sample with an additional button that creates a new issue list item in the project workspace. If you don't create a workspace - or click the button before the workspace has been created by the queue then you will discover how much error handling I have coded in (none!). As well as creating an issue I have modified the project creation piece to also create a task in the project - and then finally I create a link between the new issue and this single task using the PSI ObjectLinkProvider web service. I've included the code below in the posting for the main part of the application which is the only .cs file I manually changed from the original LoginDemo. I've commented my changes so you can either make just the mods or use the attached zip of the full modified LoginDemo. If you just modify then note you will also need a reference to Microsoft.SharePoint and a web reference to the Object Link Provider web service.

    Once you have a link then this is visible in the Project Center view both at the task level - you could also link at the project level. The extra bonuses of using a WSS list include alerts, RSS feed and the ability to attach other files - and if you really want to get clever then you can implement some of the WSS workflow on the inserted issue. Much better than tasks notes! One possible extension of this could the the inclusion of content in a workspace template and then linking up the documents to projects/tasks based on an event.

    using System;
    using System.Data;
    using System.Drawing;
    using System.Net;
    using System.Text;
    using System.Windows.Forms;
    using System.Web;
    using System.Web.Services.Protocols;
    using System.Threading;
    using Microsoft.SharePoint;
    using PSLibrary = Microsoft.Office.Project.Server.Library;
    
    /* NOTE: 
     * Delete the Admin, LoginForms, LoginWindows, and Project Web References, and 
     * re-add them for your own Project Server. Use the following URLs 
     * (substitute your values for ServerName and ProjectServerName):
     *
     *   http://ServerName/ProjectServerName/_vti_bin/psi/admin.asmx
     *   http://ServerName/ProjectServerName/_vti_bin/psi/project.asmx
     *   http://ServerName/ProjectServerName/_vti_bin/psi/LoginForms.asmx
     *   http://ServerName/ProjectServerName/_vti_bin/psi/loginwindows.asmx
     */
    
    /* NOTE:
     * For the Object Link Provider example you need to add a reference and using statement for Microsoft.SharePoint 
     * and a Web Reference for the PSI Object Link Provider web service and
     */
    
    namespace LoginDemo
    {
        public partial class LogonProjectServer : Form
        {
            private const string URLPREFIX = "http://";
            private const string PROJECTWEBSERVICE = "_vti_bin/PSI/Project.asmx";
            private const string ADMINWEBSERVICE = "_vti_bin/PSI/Admin.asmx";
            // Constant for Object Link Provider URL
            private const string OLPWEBSERVICE = "_vti_bin/PSI/ObjectLinkProvider.asmx";
            private string baseUrl; // Example: http://ServerName/ProjectServerName/
            private string userName;
            private string password;
            private bool winLogon = true;
            private bool containsUrl = false;
            private bool loggedOn = false;
            private string serverName;
            // Extra definitions for OLP
            private PSLibrary.WebObjectType webObjectType;
            private string linkedItems; 
    
            public static LoginDemo.WebSvcProject.Project project = 
                new LoginDemo.WebSvcProject.Project();
            public static WebSvcAdmin.Admin admin =
                new LoginDemo.WebSvcAdmin.Admin();
            // Added for Object Link Provider
            public static LoginDemo.WebSvcObjectLinkProvider.ObjectLinkProvider objectLinkProvider = 
                new LoginDemo.WebSvcObjectLinkProvider.ObjectLinkProvider();
    
    
            private static LoginUtils loginUtils = new LoginUtils();
            private static AdminUtils adminUtils = new AdminUtils();
            // Defined here so they can be used in the OLP section
            private Guid projectGuid;
            private Guid taskGuid;
            
    
            public LogonProjectServer()
            {
                InitializeComponent();
                lblUserName.Enabled = false;
                lblPassword.Enabled = false;
                txtUserName.Enabled = false;
                txtPassword.Enabled = false;
                radFormsAuthentication.Enabled = false;
                radWindowsAuthentication.Enabled = false;
                btnLogon.Enabled = false;
                btnLogOff.Enabled = false;
                lblProjectCreated.Visible = false;
                txtWorkspaceSubSite.Enabled = false;
                lblVersion.Text = "";
    
                //Get the user.config or the default for the ProjectServerUrl property setting
                txtProjectServerUrl.Text = Properties.Settings.Default.ProjectServerUrl;
                if (txtProjectServerUrl.Text != "http://ServerName/ProjectServer/")
                {
                    radFormsAuthentication.Enabled = true;
                    radWindowsAuthentication.Enabled = true;
                    btnLogon.Enabled = true;
                    btnLogon.Select();
                }
            }
    
            private void radFormsAuthentication_CheckedChanged(object sender, EventArgs e)
            {
                if (radFormsAuthentication.Checked)
                {
                    lblUserName.Enabled = true;
                    lblPassword.Enabled = true;
                    txtUserName.Enabled = true;
                    txtPassword.Enabled = true;
                    winLogon = false;
                    loggedOn = false;
                    lblLoggedOn.Visible = false;
                    btnLogOff.Enabled = false;
                    lblProjectCreated.Text = "";
                    lblWorkspaceUrl.Text = "";
                }
            }
    
            private void radWindowsAuthentication_CheckedChanged(object sender, EventArgs e)
            {
                if (radWindowsAuthentication.Checked)
                {
                    lblUserName.Enabled = false;
                    lblPassword.Enabled = false;
                    txtUserName.Enabled = false;
                    txtPassword.Enabled = false;
                    winLogon = true;
                    loggedOn = false;
                    lblLoggedOn.Visible = false;
                    btnLogOff.Enabled = false;
                    lblProjectCreated.Text = "";
                    lblWorkspaceUrl.Text = "";
                }
            }
    
            private void txtProjectServerUrl_TextChanged(object sender, EventArgs e)
            {
                string url = txtProjectServerUrl.Text.Trim();
    
                if (url.StartsWith(URLPREFIX) && url.Length > 10)
                    containsUrl = true;
                else
                {
                    containsUrl = false;
                }
            }
    
            private void txtProjectServerUrl_Leave(object sender, EventArgs e)
            {
                if (containsUrl)
                {
                    if (!txtProjectServerUrl.Text.EndsWith("/"))
                        txtProjectServerUrl.Text += "/";
                    radFormsAuthentication.Enabled = true;
                    radWindowsAuthentication.Enabled = true;
                    btnLogon.Enabled = true;
                }
                else
                {
                    btnLogon.Enabled = false;
                    MessageBox.Show("Invalid Project Server URL", "Invalid URL",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                    radFormsAuthentication.Enabled = false;
                    radWindowsAuthentication.Enabled = false;
                    btnLogon.Enabled = false;
                }
            }
    
            private void txtUserName_TextChanged(object sender, EventArgs e)
            {
                userName = txtUserName.Text;
            }
    
            private void txtPassword_TextChanged(object sender, EventArgs e)
            {
                password = txtPassword.Text;
            }
    
            private void btnLogon_Click(object sender, EventArgs e)
            {
                baseUrl = txtProjectServerUrl.Text;
                lblLoggedOn.Visible = false;
                string errMess = "";
                serverName = loginUtils.GetServerName(baseUrl);
    
                this.Cursor = Cursors.WaitCursor;
                try
                {
                    loggedOn = loginUtils.LogonPS(winLogon, baseUrl, userName, password);
                }
                catch (SoapException ex)
                {
                    errMess = ex.Message.ToString();
                }
                catch (WebException ex)
                {
                    errMess = ex.Message.ToString();
                }
                this.Cursor = Cursors.Default;
    
                if (loggedOn)
                {
                    AddContextInfo();
                    lblLoggedOn.Text = "Logon Succeeded!";
                    lblLoggedOn.ForeColor = Color.Green;
                    btnLogOff.Enabled = true;
    
                    string version = adminUtils.ProjectServerVersion(admin);
                    if (version.StartsWith("Error"))
                    {
                        MessageBox.Show(version, "Admin Web service error",
                            MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else
                        lblVersion.Text = "Project Server Version: " + version;
                }
                else
                {
                    MessageBox.Show(errMess, "Logon Error", MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    lblLoggedOn.Text = "Logon Failed!";
                    lblLoggedOn.ForeColor = Color.Red;
                }
    
                lblLoggedOn.Visible = true;
            }
    
            private void btnLogOff_Click(object sender, EventArgs e)
            {
                bool loggedOff = false;
                string errMess = "";
                try
                {
                    loggedOff = loginUtils.LogoffPS(winLogon);
                }
                catch (SoapException ex)
                {
                    errMess = ex.Message.ToString();
                }
                catch (WebException ex)
                {
                    errMess = ex.Message.ToString();
                }
                if (loggedOff)
                {
                    RemoveContextInfo();
                    lblLoggedOn.Text = "Logged Off!";
                    lblLoggedOn.ForeColor = Color.Black;
                    lblLoggedOn.Visible = true;
                    lblProjectCreated.Text = "";
                    lblVersion.Text = "";
                    lblWorkspaceLabel.Visible = false;
                    lblWorkspaceUrl.Text = "";
                }
                else
                {
                    MessageBox.Show(errMess, "Logoff Error", MessageBoxButtons.OK, 
                        MessageBoxIcon.Error);
                }
            }
    
            private void btnSaveUrl_Click(object sender, EventArgs e)
            {
                //Save the ProjectServerUrl property to the user.config file.
                Properties.Settings.Default.ProjectServerUrl = txtProjectServerUrl.Text;
                Properties.Settings.Default.Save();
            }
    
            /// <summary>
            /// Add the URL, and the user credentials or logon cookie, to each 
            /// PSI Web service object in the application.
            /// </summary>
            public void AddContextInfo()
            {
                // Add the Url property first.
                admin.Url = loginUtils.BaseUrl + ADMINWEBSERVICE;
                project.Url = loginUtils.BaseUrl + PROJECTWEBSERVICE;
                // Url property for OLP
                objectLinkProvider.Url = loginUtils.BaseUrl + OLPWEBSERVICE;
    
    
                if (winLogon)   // Add Windows credentials
                {
                    admin.Credentials = CredentialCache.DefaultCredentials;
                    project.Credentials = CredentialCache.DefaultCredentials;
                    // Windows credentials for OLP
                    objectLinkProvider.Credentials = CredentialCache.DefaultCredentials;
                }
                else            // Add Project Server logon cookie for Forms logon
                {
                    admin.CookieContainer = loginUtils.Cookies;
                    project.CookieContainer = loginUtils.Cookies;
                    // Forms logon cookie for OLP
                    objectLinkProvider.CookieContainer = loginUtils.Cookies;
                }
            }
    
            /// <summary>
            /// Remove the user credentials or logon cookie from each PSI Web service object.
            /// </summary>
            public void RemoveContextInfo()
            {
                if (winLogon)
                {
                    admin.Credentials = null;
                    project.Credentials = null;
                    // Added for OLP
                    objectLinkProvider.Credentials = null;
                }
                else
                {
                    admin.CookieContainer = null;
                    project.CookieContainer = null;
                    // Adde for OLP
                    objectLinkProvider.CookieContainer = null;
                }
            }
    
            /// <summary>
            /// Test the application with a PSI call. For example, create a project. 
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnCreateProject_Click(object sender, EventArgs e)
            {
                string projectCreatedLabel = "Project created!";
                string wssUrl;
                string projectWorkspace = ResetWorkspaceUrl();
                bool created = false;
                lblProjectCreated.Text = "";
                lblWorkspaceUrl.Text = projectWorkspace;
                this.Cursor = Cursors.WaitCursor;
    
                try
                {
                    WebSvcProject.ProjectDataSet dsProject = 
                        new WebSvcProject.ProjectDataSet();
                    WebSvcProject.ProjectDataSet.ProjectRow projectRow = 
                        dsProject.Project.NewProjectRow();
    
                    projectGuid = Guid.NewGuid();
                    projectRow.PROJ_UID = projectGuid;
                    projectRow.PROJ_NAME = this.txtProjectName.Text;
                    projectRow.PROJ_TYPE = 
                        Convert.ToInt32(PSLibrary.Project.ProjectType.Project);
                    
                    dsProject.Project.AddProjectRow(projectRow);
                    
                    // Adding a task to the project so we can have something to link to
                    WebSvcProject.ProjectDataSet.TaskRow taskRow =
                        dsProject.Task.NewTaskRow();
                    taskGuid = Guid.NewGuid();
                    taskRow.PROJ_UID = projectGuid;
                    taskRow.TASK_UID = taskGuid;
                    taskRow.TASK_NAME = "Task 1";
                    taskRow.TASK_DUR = 480000;
                    dsProject.Task.AddTaskRow(taskRow);
                    
    
                    Guid jobGuid = Guid.NewGuid();
                    bool validateOnly = false;
                    // Create and save project to the Draft db
                    project.QueueCreateProject(jobGuid, dsProject, validateOnly);
    
                    // Wait 3 seconds (more or less) for Queue job to complete.
                    // Or, add a routine that checks the QueueSystem for job completion.
                    System.Threading.Thread.Sleep(3000);
    
                    WebSvcProject.ProjectRelationsDataSet dsProjectRelations =
                        new WebSvcProject.ProjectRelationsDataSet();
                    jobGuid = Guid.NewGuid();
    
                    // Set wssUrl = "" to have default WSS project workspace, or null to have no workspace.
                    if (chkDefaultWorkspace.Checked)
                        wssUrl = "";
                    else if (projectWorkspace == "")
                        wssUrl = null;
                    else
                        wssUrl = projectWorkspace;
    
                    bool fullPublish = true;
                    // Publishes project to the Published db
                    dsProjectRelations = project.QueuePublish(jobGuid, projectGuid, fullPublish, wssUrl);
                    created = true;
                }
                catch (SoapException ex)
                {
                    string errMess = "";
                    // Pass the exception to the PSClientError constructor to get 
                    // all error information.
                    PSLibrary.PSClientError error = new PSLibrary.PSClientError(ex);
                    PSLibrary.PSErrorInfo[] errors = error.GetAllErrors();
    
                    for (int j = 0; j < errors.Length; j++)
                    {
                        errMess += errors[j].ErrId.ToString() + "\n";
                    }
                    errMess += "\n" + ex.Message.ToString();
    
                    MessageBox.Show(errMess, "Error", MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                }
                catch (WebException ex)
                {
                    string message = ex.Message.ToString() +
                        "\n\nLog on, or check the Project Server Queuing Service";
                    MessageBox.Show(message, "Project Creation Error", 
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                this.Cursor = Cursors.Default;
               
                if (created)
                {
                    lblProjectCreated.ForeColor = Color.Green;
                    lblWorkspaceUrl.Visible = true;
                    lblWorkspaceLabel.Visible = true;
                }
                else
                {
                    projectCreatedLabel = "Project not created";
                    lblProjectCreated.ForeColor = Color.Red;
                    lblWorkspaceUrl.Visible = false;
                    lblWorkspaceLabel.Visible = true;
                }
                lblProjectCreated.Text = projectCreatedLabel;
                lblProjectCreated.Visible = true;
            }
    
            private string ResetWorkspaceUrl()
            {
                string workspace;
                if (chkDefaultWorkspace.Checked)
                {
                    workspace = "http://" + serverName + "/" + txtProjectName.Text;
                }
                else
                {
                    workspace = "http://" + serverName + "/" + txtWorkspaceSubSite.Text;
                }
                return workspace;
            }
    
            private void txtProjectName_TextChanged(object sender, EventArgs e)
            {
                lblProjectCreated.Text = "";
                if (chkDefaultWorkspace.Checked)
                    txtWorkspaceSubSite.Text = txtProjectName.Text;
                lblWorkspaceLabel.Visible = false;
                lblWorkspaceUrl.Visible = false;
            }
    
            private void chkDefaultWorkspace_CheckedChanged(object sender, EventArgs e)
            {
                if (chkDefaultWorkspace.Checked)
                {
                    txtWorkspaceSubSite.Enabled = false;
                }
                else
                {
                    txtWorkspaceSubSite.Enabled = true;
                }
            }
    
            private void btnExit_Click(object sender, EventArgs e)
            {
                Application.Exit();
            }
    
            private void btnAddIssue_Click(object sender, EventArgs e)
            {
                //First find the Issues list in the site collection for the new project
                // siteCollection will fidn the collection for http://servename/pwa
                SPSite siteCollection = new SPSite(txtProjectServerUrl.Text + txtProjectName.Text);
                
                // sites will hold collection of sites under pwa - which will be he workspaces
                SPWebCollection sites = siteCollection.AllWebs;
    
                // lists will be the collection of lists in the specific site with the project name we just created
                SPListCollection lists = sites[txtProjectName.Text].Lists;
                
                // and finally list will be the Issues list for this site
                SPList list = lists["Issues"];
    
                // create a list collection
                SPListItemCollection listItems = list.Items;
    
                // add a new item
                SPListItem item = listItems.Add();
    
                // set some properties - in this sample I just set Title
                // but you can set any of the properties of the Project Workspace Issues list
                item["Title"] = "New Issue for Project " + txtProjectName.Text;
                
                // and update - we now have a new issue
                item.Update();
                
                // get some properties of the new item we will need later
                int itemTPID = item.ID;
                Guid listGuid = list.ID;
                string listName = list.Title.ToString();
    
                //  This code is basicall from the SDK OLP example but using the properties 
                // of the project task and items created earlier in this sample
                Guid taskWebObjectUid = Guid.Empty;
                WebSvcObjectLinkProvider.ObjectLinkProviderDataSet dsLinkedObjects =
                    objectLinkProvider.ReadTaskWebObject(taskGuid);
                int numTaskWebObjects = dsLinkedObjects.WebObjects.Count;
                // In this sample there will be no existing objects - but would be useful in other scenarios
                if (numTaskWebObjects > 0)
                    taskWebObjectUid = dsLinkedObjects.WebObjects[0].WOBJ_UID;
                
    
                WebSvcObjectLinkProvider.ObjectLinkProviderDataSet dsTask =
        new WebSvcObjectLinkProvider.ObjectLinkProviderDataSet();
                WebSvcObjectLinkProvider.ObjectLinkProviderDataSet.WebObjectsRow taskRow =
                    dsTask.WebObjects.NewWebObjectsRow();
    
                // Provide all known information to the Web object row for the task.  
                // If a task Web object does not exist, AddWebObjects creates
                // a new Web object and updates WOBJ_UID in taskRow.  
                taskRow.WOBJ_UID = taskWebObjectUid;
                taskRow.WOBJ_TASK_UID = taskGuid;
                taskRow.WOBJ_PROJ_UID = projectGuid;
                taskRow.WOBJ_TYPE = (int)PSLibrary.WebObjectDatabaseType.Task;
                dsTask.WebObjects.AddWebObjectsRow(taskRow);
    
                WebSvcObjectLinkProvider.ObjectLinkProviderDataSet dsListItems =
        new WebSvcObjectLinkProvider.ObjectLinkProviderDataSet();
                WebSvcObjectLinkProvider.ObjectLinkProviderDataSet.WebObjectsRow listItemRow =
                    dsListItems.WebObjects.NewWebObjectsRow();
    
                listItemRow.WOBJ_UID = Guid.NewGuid();
                listItemRow.WOBJ_TP_ID = itemTPID;
                listItemRow.WOBJ_LIST_NAME = listGuid;
                listItemRow.WOBJ_PROJ_UID = projectGuid;
                
                // I left the switch in but in this sample it will always be an issue
                switch (listName)
                {
                    case "Issues":
                        listItemRow.WOBJ_TYPE = (int)PSLibrary.WebObjectDatabaseType.Issue;
                        webObjectType = PSLibrary.WebObjectType.Issue;
                        linkedItems = "Issues found for task: " + taskGuid;
                        break;
                    case "Risks":
                        listItemRow.WOBJ_TYPE = (int)PSLibrary.WebObjectDatabaseType.Risk;
                        webObjectType = PSLibrary.WebObjectType.Risk;
                        linkedItems = "Risks found for task: " + taskGuid;
                        break;
                    case "Documents":
                        listItemRow.WOBJ_TYPE = (int)PSLibrary.WebObjectDatabaseType.Document;
                        webObjectType = PSLibrary.WebObjectType.Document;
                        linkedItems = "Documents found for task: " + taskGuid;
                        break;
                    case "Commitments":  // Commitments are now called Deliverables
                        listItemRow.WOBJ_TYPE = (int)PSLibrary.WebObjectDatabaseType.Commitment;
                        webObjectType = PSLibrary.WebObjectType.Commitment;
                        linkedItems = "Deliverables found for task: " + taskGuid;
                        break;
                    default:
                        string errMess = listName +
                            " is not a default SharePoint list type for task links.";
                        throw new SystemException(errMess);
                }
                dsListItems.WebObjects.AddWebObjectsRow(listItemRow);
    
                WebSvcObjectLinkProvider.WebObjectLinkType generalLinkType =
        WebSvcObjectLinkProvider.WebObjectLinkType.General;
                WebSvcObjectLinkProvider.WebObjectLinkType[] wssLinkTypeArray = 
        { generalLinkType };
    
                objectLinkProvider.CreateWebObjectLinks(dsTask, dsListItems, wssLinkTypeArray);
    
    
                
            }
        }
    }
    Technorati Tags: Project Server 2007
  • Brian Smith's Microsoft Project Support Blog

    You can't change the task work breakdown structure (WBS) value from the PSI

    • 0 Comments

    In the dataset for the Project you have read/write access to Task_WBS.  Unfortunately this should be read-only and the documentation is being corrected.  (See community comment already posted at http://msdn2.microsoft.com/en-us/library/websvcproject.projectdataset.taskrow.task_wbs.aspx#CommunityContent - and look out for the updated downloadable SDK next month).  This can appear to work and you can see any changes written to the dataset and even persisted to the database and published to PWA views.  However if the project is then opened from Project Professional 2007 any values perviously set via the PSI are ignored and reset (or just blanked out) according to the settings in Project Professional. 

    One potential workaround if you need to control the Task WBS values externally would be to use another custom field that you could control fully through the PSI.

    This is also another reminder that you too can post community content to the SDK on MSDN. 

    Technorati Tags: Project Server 2007

  • Brian Smith's Microsoft Project Support Blog

    Queue Etiquette: 4 items or less? Join the queue!

    • 2 Comments

    The queue is one of the new features that is taking some getting used to by our customers.  It is probably time for a fresh "queue problems" posting - that will be my next post, but the following came out of a discussion with Patrick Conlan yesterday.  The main job of the queue is to even out the flow of data to the database ensuring consistency of data and avoiding overload on the servers.

    The queue process has some great logic built in to control the flow of data and ensure reliable data consistency - such as not pushing data into the reporting database while a cube build is under way, and not allowing a check-in if a save is incomplete.  There is also some optimization to avoid doing the same job more than once.  You may have seen "skipped for optimization" against some jobs.  These are jobs that do not have their own payload (unlike Save from Professional where the project changes are part of the message in the queue)  but are really just instructions to do something.  An example would be a publish - which has no payload of data but is just an instruction to take what is in the draft db for a particular project and push it into the published db (in very simple terms).  Obviously if there are several publish requests for the same project and no intermediate saves then you only need to do this job once - so you can skip all other occurrences. 

    But beyond this (again in simple terms) it is really first come first served.

    You need to consider this if you are developing an application that may push data into the Project Server queues.  If you submit thousands of jobs to the queue that may take a while to process then anyone doing normal routine stuff will be in the queue behind you and therefore delayed.  A better approach (assuming your stuff isn't time critical) is to push a few jobs in to the queue at a time and check for their progress.  Once these have finished perhaps pause for a bit to allow other jobs some time then push in some more.  In this way the normal project saves, check-ins etc will not be right at the back of the queue but will get a chance to join a short queue - much like the express checkouts at the supermarkets.  A good rule of thumb might be pushing in as many jobs as you have threads running - so 4 items or less in a default system.

    Technorati Tags: Project Server 2007

Page 81 of 91 (451 items) «7980818283»