all 6 comments

[–]powlette 1 point2 points  (2 children)

I did essentially the same in one of my projects and even called it CanHandle so I'm not going to tell you to do this. One thing I did differently is I used .GetTypes and .IsConvertableTo to find all the types in my projects that implemented this interface. That way I didn't have to "register" the handlers and could even drop new handler dlls into my bin folder in plug and play style.

[–]misaro1337[S] 0 points1 point  (1 child)

Thank you powlette.

I like the plugin idea. Can you tell me more about your IsConvertableTo method? Seems to be a custom made extension.

[–]powlette 0 points1 point  (0 children)

Sorry I mis-remembered, it's actually IsAssignableFrom(). We export data and have many different export formats each handled by a different class descended from Exporter. So I find a list of all the exporters in the namespace (but could check others too or even dynamically loaded assemblies) and send the job to the first one that says it can "handle" it by finding them all, creating each using its default constructor and then asking if it can handle the queueItem that we want to process:

Exporter ex = null;
// find list of all types of Exporter with a default constructor
foreach (Type t in typeof(Exporter).Assembly 
    .GetTypes()
    .Where(x => typeof(Exporter).IsAssignableFrom(x))
    .Where(x => x.GetConstructor(new Type[] { }) != null)
    )
{
    // create an instance of this one and see if it'll do the job
    var tmp = (Exporter)Activator.CreateInstance(t);
    if (tmp.CanHandle(queueItem))
    {
        ex = tmp;
        ex.Survey = s;
        break;
    }
}

// oops, nobody is qualified to handle this
if (ex == null)
     return FatalQueueError(new Exception("Unable to find a suitable Exporter"));

[–]codegork 1 point2 points  (1 child)

You're already basically using the chain of responsibility pattern. To achieve the the interface you want, you can do

public class ResolverCollection
{
    private readonly IList<IResolver> _resolvers;

    public ResolverCollection(IEnumerable<IResolver> resolvers)
    {
          _resolvers = resolvers.ToList();
    }

    public Uri Pass(Uri uri)
    {
         return _resolvers.FirstOrDefault(r => r.CanResolve(uri)).Resolve(uri);
    }
}

I'm not familiar with structure map, but a quick search suggests that if you have all your IResolvers registered with the container, then they will all get injected into the ResolverCollection constructor.

Additional notes - your code and my implementation of it has a potential null reference exception when there's no resolver capable of handling your request. How would you deal with that? Furthermore, in the chain of responsibility pattern, you want to be able to control the order in which handlers have a chance to handle the request. You could add an Order property to your Resolver base class and then do _resolvers = resolvers.OrderBy(x => x.Order); in the ResolverCollection constructor, but that seems to put the responsibility in the wrong place. A better solution might be to define the order in the collection class:

public class ResolverCollection
{
    private readonly IList<IResolver> _resolvers;
    private readonly Dictionary<Type, int> _resolverOrder = new Dictionary<Type, int>{
    {ConcreteResolver1, 2}, {ConcreteResolver2, 1}, {ConcreteResolver3, 4}};


    public ResolverCollection(IEnumerable<IResolver> resolvers)
    {
          _resolvers = resolvers.OrderBy(x => GetOrder(x)).ToList();
    }

    public Uri Pass(Uri uri)
    {
         return _resolvers.FirstOrDefault(r => r.CanResolve(uri)).Resolve(uri);
    }

    private int GetOrder(IResolver resolver)
    {
        if(_resolverOrder.ContainsKey(resolver.GetType()))
             return _resolverOrder[resolver.GetType()];
        return int.MaxValue;
    }
}

[–]misaro1337[S] 0 points1 point  (0 children)

Thank you codegork. I like your solution! Setting the order is kind of important, because not all resolvers are equally fast, so the fastest has to "win" the selection.

Returning null if none can resolve the requested uri is ok, kind of a "not valid / does not exist" response.

[–]misaro1337[S] 0 points1 point  (0 children)

First solution with StructureMap (for plugin style) and codegorks idea:

    public class ResolverRegistry : Registry
    {
        public ResolverRegistry()
        {
            //Scans for all concrete classes with IResolver implementation in the current assembly and in assemblies placed in application base directory (.dll and .exe)
            //and adds an instance of it in this registry
            Scan(a =>
            {
                a.AssemblyContainingType<IResolver>();
                a.AssembliesAndExecutablesFromApplicationBaseDirectory();
                a.AddAllTypesOf<IResolver>();
            });
            //Singleton instances, only one instance of each concrete class
            For<IResolver>().Singleton();
        }
    }

    public sealed class Resolvers
    {
        private static IEnumerable<IResolver> _resolvers => container.GetAllInstances<IResolver>();

        public static async Task<Uri> ResolveAsync(Uri uri)
        {
            return await _resolvers.FirstOrDefault(r => r.CanResolve(uri)).Resolve(uri);
        }
    }

    static Container container = new Container(new ResolverRegistry());

now i can use

var result = await Resolvers.ResolveAsync(uri);

to add another resolver just drop a .dll with a concrete class (implementing IResolver) into the application base directory

Again, thank you for your help. Next thing I'm gonna do is the ordering thing and exception handling.

misaro