Search code examples
entity-frameworkdependency-injectionrepository-patternservice-locatorworkflowservice

Workflow: Creating Dependency Chain with Service Locator Pattern


I'm trying to get dependencies set up correctly in my Workflow application. It seems the best way to do this is using the Service Locator pattern that is provided by Workflow's WorkflowExtensions.

My workflow uses two repositories: IAssetRepository and ISenderRepository. Both have implementations using Entity Framework: EFAssetRepository, and EFSenderRepository, but I'd like both to use the same DbContext.

I'm having trouble getting both to use the same DbContext. I'm used to using IoC for dependency injection, so I thought I'd have to inject the DbContext into the EF repositories via their constructor, but this seems like it would be mixing the service locator and IoC pattern, and I couldn't find an easy way to achieve it, so I don't think this is the way forward.

I guess I need to chain the service locator calls? So that the constructor of my EF repositories do something like this:

public class EFAssetRepository
{
    private MyEntities entities;

    public EFAssetRepository()
    {
        this.entities = ActivityContext.GetExtension<MyEntities>();
    }
}

Obviously the above won't work because the reference to ActivityContext is made up.

How can I achieve some form of dependency chain using the service locator pattern provided for WF?

Thanks, Nick


EDIT

I've posted a workaround for my issue below, but I'm still not happy with it. I want the code activity to be able to call metadata.Require<>(), because it should be ignorant of how extensions are loaded, it should just expect that they are. As it is, my metadata.Require<> call will stop the workflow because the extension appears to not be loaded.


Solution

  • It seems one way to do this is by implementing IWorkflowInstanceExtension on an extension class, to turn it into a sort of composite extension. Using this method, I can solve my problem thus:

    public class UnitOfWorkExtension : IWorkflowInstanceExtension, IUnitOfWork
    {
        private MyEntities entities = new MyEntities();
    
        IEnumerable<object> IWorkflowInstanceExtension.GetAdditionalExtensions()
        {
            return new object[] { new EFAssetRepository(this.entities), new EFSenderRepository(this.entities) };
        }
    
        void IWorkflowInstanceExtension.SetInstance(WorkflowInstanceProxy instance) { }
    
        public void SaveChanges()
        {
            this.entities.SaveChanges();
        }
    }
    

    The biggest downside to doing it this way is that you can't call metadata.RequireExtension<IAssetRepository>() or metadata.RequireExtension<ISenderRepository>() in the CacheMetadata method of a CodeActivity, which is common practice. Instead, you must call metadata.RequireExtension<IUnitOfWork>(), but it is still fine to do context.GetExtension<IAssetRepository>() in the Execute() method of the CodeActivity. I imagine this is because the CacheMetadata method is called before any workflow instances are created, and if no workflow instances are created, the extension factory won't have been called, and therefore the additional extensions won't have been loaded into the WorkflowInstanceExtensionManager, so essentially, it won't know about the additional extensions until a workflow instance is created.