Peut être certains d’entre vous ont tenté de créer une solution à base de Master Data Services 2012 (MDS) couplé avec un Workflow de type SharePoint 2010.

Normalement, tout ceci est “by design” intégré dans MDS, l’appel du Workflow SharePoint étant déclenché par un service Windows MDS (que vous avez au préalable installé)

Dans votre éditeur Business Rules, vous n’avez normalement qu’à renseigner l’adresse de votre site SharePoint et MDS doit (enfin devrait) s’occuper du reste :

image

Manque de bol, la version 2012 de ce fameux service subit une régression de son fonctionnement (sic !)

En bref, pour la faire court, en 2008 R2 le lancement du Workflow SharePoint fonctionne bien, en 2012, le lancement du workflow SharePoint … n’existe plus !

Du coup, si vous tentez de lancer votre WorkFlow, voici ce qui risque de vous arrivez (en Debug via le lancement du service windows en mode –Console) :

70012

“Could Not Dispatch Due To Missing Extender : SPWF”

Ah ben ça marche de suite moins bien dis donc !

J’ai donc décider de réflecter le code du service Windows Microsoft.MasterDataServices.Workflow.Exe (version 2008 R2 et 2012) et on peut voir ça :

Version 2008

private static bool DispatchExternalActions()
    {
        // Nothing interesting here

while (!WorkflowService.stopEvent.WaitOne(0))

{

SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();

while (sqlDataReader.Read())

{

SqlXml sqlXml = sqlDataReader.GetSqlXml(0);

@value.InnerXml = sqlXml.Value;

foreach (XmlNode childNode in @value.ChildNodes)

{

string upperInvariant = childNode["Type"].InnerText.ToUpperInvariant();

string innerText = childNode["Server_URL"].InnerText;

string str = childNode["Action_ID"].InnerText;

object[] objArray = new object[3];

DateTime now = DateTime.Now;

objArray[0] = now.ToString();

objArray[1] = str;

objArray[2] = innerText;

Program.WriteLine(string.Format(CultureInfo.InvariantCulture, « [{0}] Workflow {1} on {2} », objArray));

try

{

if (upperInvariant == « SPWF »)

{

WorkflowService.DispatchSPWorkflow(innerText, str, childNode, strs);

}

else

{

IWorkflowTypeExtender workflowTypeExtender = null;

if (WorkflowService.extenders != null)

{

WorkflowService.extenders.TryGetValue(upperInvariant, out workflowTypeExtender);

}

if (workflowTypeExtender == null)

{

Program.WriteLine(string.Concat(« Could not dispatch due to missing extender: « , upperInvariant));

}

else

{

Program.WriteLine(string.Concat(« Dispatching using extender: « , upperInvariant));

workflowTypeExtender.StartWorkflow(upperInvariant, (XmlElement)childNode);

}

}

}

catch (Exception exception1)

{

Exception exception = exception1;

object[] str1 = new object[1];

str1[0] = exception.ToString();

Program.WriteLine(string.Format(CultureInfo.InvariantCulture, « Error starting workflow:\n{0}\n », str1));

ExceptionInspector.RethrowCriticalException(exception);

}

}

}

sqlDataReader.Close();

sqlDataReader.Dispose();

}

return false;

 

Version 2012

try
  {
      IWorkflowTypeExtender workflowTypeExtender = null;
      if (WorkflowService.ExtenderCache != null)
      {
          WorkflowService.ExtenderCache.TryGetValue(upperInvariant, out workflowTypeExtender);
      }
      if (workflowTypeExtender == null)
      {
          object[] objArray = new object[1];
          objArray[0] = upperInvariant;
          Program.WriteLine(string.Format(CultureInfo.InvariantCulture, "Could not dispatch due to missing extender: {0}", objArray), EventLogEntryType.Error);
      }
      else
      {
          object[] objArray1 = new object[1];
          objArray1[0] = upperInvariant;
          Program.WriteLine(string.Format(CultureInfo.InvariantCulture, "Dispatching using extender: {0}", objArray1), EventLogEntryType.Information);
          workflowTypeExtender.StartWorkflow(upperInvariant, (XmlElement)childNode);
      }
  }
  catch (Exception exception1)
  {
      Exception exception = exception1;
      object[] objArray2 = new object[1];
      objArray2[0] = exception;
      Program.WriteLine(string.Format(CultureInfo.InvariantCulture, "Error starting workflow:\n{0}\n", objArray2), EventLogEntryType.Error);
      WorkflowService.RethrowCriticalException(exception);

 

Remarquez comme le version 2008 fait bien appel à un Dispatcher particulier si le paramètre est SPWF, alors que la version 2012, non.

if (upperInvariant == "SPWF")
{
    WorkflowService.DispatchSPWorkflow(innerText, str, childNode, strs);
}

 

Alors de deux choses l’une : Soit Microsoft s’est un peu fourvoyé sur ce coup là Surpris Soit ils ont décidé que tous les appels externes de MDS doivent être fait par le système d’extender (why not hein)

Du coup dans notre cas, j’ai développé l’extender qui va bien pour résoudre la solution. (Code source complet en attachement du post)

Voici la classe, qui implémente IWorkflowTypeExtender et qui, via ses réféences vers SharePoint, lance un Workflow avec les paramètres qui vont bien :

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
usingMicrosoft.MasterDataServices.WorkflowTypeExtender;

namespace Sharepoint.WorkflowExtender

{

public class SPWorkflow : IWorkflowTypeExtender

{

public void StartWorkflow(string workflowType, System.Xml.XmlElement dataElement)

{

var doc = XDocument.Parse(dataElement.OuterXml);

Dictionary<string, object> strs = new Dictionary<string, object>();

try

{

var externalAction =

(

from e in doc.Elements()

select new

{

Type = (string)e.Element(« Type »),

SendData = (string)e.Element(« SendData »),

Action_ID = (string)e.Element(« Action_ID »),

Server = (string)e.Element(« Server_URL »),

Model_ID = (int)e.Element(« Model_ID »),

Model_Name = (string)e.Element(« Model_Name »),

Entity_ID = (int)e.Element(« Entity_ID »),

Entity_Name = (string)e.Element(« Entity_Name »),

Version_ID = (int)e.Element(« Version_ID »),

Member_ID = (int)e.Element(« Member_ID »),

MemberData = new

{

Name = (string)e.Element(« MemberData »).Element(« Name »),

Code = (string)e.Element(« MemberData »).Element(« Code »),

Attributes = e.Element(« MemberData »).Elements().ToDictionary(ae => ae.Name, ae => ae.Value)

}

}

).Single();

var member = externalAction.MemberData;

DispatchSPWorkflow(externalAction.Server, externalAction.Action_ID, dataElement, strs);

}

catch (Exception)

{

throw;

}

}

private static void DispatchSPWorkflow(string serverUrl, string workflowName, XmlNode dataElement,

Dictionary<string, object> sites)

{

SPSite sPSite;

SPWorkflowAssociation sPWorkflowAssociation;

object obj = null;

if (sites.TryGetValue(serverUrl, out obj))

{

sPSite = (SPSite)obj;

}

else

{

sPSite = new SPSite(serverUrl);

sites[serverUrl] = sPSite;

}

SPWeb sPWeb = sPSite.OpenWeb();

SPWorkflowManager workflowManager = sPSite.WorkflowManager;

sPWeb.WorkflowAssociations.UpdateAssociationsToLatestVersion();

foreach (SPWorkflowAssociation workflowAssociation in sPWeb.WorkflowAssociations)

{

if (!(workflowAssociation.Name == workflowName) || !workflowAssociation.AllowManual)

{

continue;

}

sPWorkflowAssociation = workflowAssociation;

break;

}

if (sPWorkflowAssociation != null)

{

workflowManager.StartWorkflow(null, sPWorkflowAssociation, dataElement.OuterXml, 1);

}

}

}

}

 

Cette assembly est à déployer dans le répertoire Program Files\Microsoft SQL Server\110\Master Data Services\WebApplication\bin au coté de l’assembly WorkflowTypeExtender, comme le montre la copie d’écran suivante :

image

A partir de là, votre Workflow SharePoint devrait se lancer sans trop de problèmes !

Le projet C# :

SharePoint.WorkflowExtender.zip