Setzen der Persistenz Service ID für Workflow Services

Gestern habe ich einen kleinen, aber nützlichen Hack gezeigt, mit welchem man die Service ID des Persistenzdienstes setzen kann. Dies funktioniert auch ganz prima, solange man nicht mit Workflow Services arbeitet.

Wie man es auch versucht, man erhält keine gültige Referenz auf den Persistenz Service.

Also was tun? Reflector zeigt uns die Interna von WorkflowService.ApplyDispatchBehavior():

static class WorkflowExtensions
...
WorkflowPersistenceService service = item.WorkflowRuntime.GetService<WorkflowPersistenceService>();
if (service != null)
{
    bool isStarted = item.WorkflowRuntime.IsStarted;
    if (isStarted)
    {
        item.WorkflowRuntime.StopRuntime();
    }
    item.WorkflowRuntime.RemoveService(service);
    item.WorkflowRuntime.AddService(new SkipUnloadOnFirstIdleWorkflowPersistenceService(service));
    if (isStarted)
    {
        item.WorkflowRuntime.StartRuntime();
    }
}
...

Was geschieht hier genau?
Wenn via Config oder Code ein Persistenz Service hinzugefügt wurde, wird die Runtime gestoppt. Der Standard Persistenz Service wird entfernt, und ein neuer Service mit dem handlichen Namen SkipUnloadOnFirstIdleWorkflowPersistenceService wird hinzugefügt. Danach wird die Runtime wieder gestartet.
Die Workflow Runtime eines Workflow Services verwendet also intern nicht den SqlWorkflowPersistenceService, sondern einen speziellen, öffentlich nicht zugänglichen und gekapselten Dienst.

Wie also kann nun in einem solchen Kontext die Service ID gesetzt werden?

Die gestern gezeigte Erweiterungsmethode kann wie folgt ergänzt werden:

static class WorkflowExtensions
{
    public static void SetServiceInstanceId(this WorkflowPersistenceService workflowPersistenceService, Guid serviceId)
    {
        Type persistenceServiceType = workflowPersistenceService.GetType();
        FieldInfo instanceIdField = persistenceServiceType.GetField("_serviceInstanceId", BindingFlags.NonPublic | BindingFlags.Instance);
        instanceIdField.SetValue(workflowPersistenceService, serviceId);
    }

    public static WorkflowRuntime GetRuntime(this WorkflowServiceHost host)
    {
        return host.Description.Behaviors.Find<WorkflowRuntimeBehavior>().WorkflowRuntime;
    }

    public static T GetWFService<T>(this WorkflowRuntime runtime) where T : class
    {
        if (typeof(T) != typeof(SqlWorkflowPersistenceService))
        {
            return runtime.GetService<T>();
        }
        else
        {
            WorkflowPersistenceService service = runtime.GetService<WorkflowPersistenceService>();
            Type sqps = service.GetType();

            FieldInfo field = (from fieldinfo in sqps.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
                               where fieldinfo.FieldType == typeof (WorkflowPersistenceService)
                               select fieldinfo).First();
            return field.GetValue(service) as T;
        }
    }
}

Mit der Erweiterungsmethode GetRuntime() erhält man die WF Runtime vom WorkflowServiceHost.
Mittels wfRuntime.GetWFService<SqlWorkflowPersistenceService>() kann danach durch den Zugriff via Reflection auf den intern als Feld gekapselten Persistenzdienst zugegriffen werden.

Tags:

Kommentare sind nicht erlaubt.