Question

I have a Skype Bot service which has a defined Interface for shortening a given url.

namespace Skypnet.Modules.UrlShortener
{
    public interface IUrlShortenerProvider
    {
        string ApiKey { get; set; }
        string Shorten(string url);
    }
}

This interface is implemented by two services, one that uses the Google Url shortening API and the other which uses the TinyUrl API.

My bot loads multiple modules at start up and each module registers listening events on the SKype Client. So when I send a message in skype:

Patrick Magee>!tiny http://example.com/a-really-long-url-that-i-want-to-shorten 

Then my registered modules that have listened for a message event and have analysed my message to check if it validates against what they are there to do, they execute and return with a message of the tiny url.

Patrick Magee> Bot> http://tinyurl.com/2tx

At a slightly higher level, I have a defined Abstract Skypenet Module which all Skypenet Modules must implement.

public class UrlShortenerSkypnetModule : AbstractSkypenetModule

The only important thing about this abstract module is that it allows my UrlShortner to register its events for Skype like so:

public class UrlShortenerSkypnetModule : AbstractSkypenetModule
{
    private readonly IUrlShortenerProvider urlShortenerProvider;
    private const string RegexPatternV2 = @"^!(?<command>tiny)\s+(?<service>\S+?)\s+(?<url>(?<protocol>(ht|f)tp(s?))\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*))";
    //private const string RegexPattern = @"^!(?<command>tiny)\s+(?<url>(?<protocol>(ht|f)tp(s?))\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*))";
    private static readonly Regex UrlRegex = new Regex(RegexPatternV2, RegexOptions.Compiled);

    /// <summary>
    /// The Trigger used for this url shortener provider
    /// </summary>
    [Inject]
    public string Trigger { get; set; }

    [Inject]
    public UrlShortenerSkypnetModule(IUrlShortenerProvider urlShortenerProvider)
    {
        if (urlShortenerProvider == null)
            throw new ArgumentNullException("urlShortenerProvider");

        this.urlShortenerProvider = urlShortenerProvider;
    }

    public override void RegisterEventHandlers()
    {
        SkypeContainer.Skype.MessageStatus += SkypeOnMessageStatus;
    }

    private void SkypeOnMessageStatus(ChatMessage pMessage, TChatMessageStatus status)
    {
        if (status == TChatMessageStatus.cmsSent || status == TChatMessageStatus.cmsReceived)
        {
            Match match = UrlRegex.Match(pMessage.Body);

            if (match.Success)
            {
                var url = match.Groups["url"].Value;
                var trigger = match.Groups["service"].Value;

                // If the service matches
                if (trigger.ToLower().Equals(Trigger.ToLower()))
                {
                    string shorten = urlShortenerProvider.Shorten(url);
                    pMessage.Chat.SendMessage(shorten);
                }
            }
        }
    }
}

How can i use Ninject Modules to bind both url providers to the same parent abstract module and based on its name inject the different IUrlShortenerProvider into that instance. Further to this, is this the correct way to go about it?

public class TinyUrlProvider : IUrlShortenerProvider
public class GoogleUrlProvider : IUrlShortenerProvider

so that both implementations are instanced and if an implementation matches a trigger word, for example "google" or "tinyurl" which my UrlShortnerskypenetModule can have defined, the appropriate instance handles the request?

So far I have a NinjectModule for my UrlShortenerModule which looks like this, but it's completely wrong, but hopefully gives an understanding of what I'm trying to acomplish

public class UrlShortenerModules : NinjectModule
{
    public override void Load()
    {
        // The Abstract Module which all Modules must implement
        Bind<ISkypnetModule>()
            .To<AbstractSkypenetModule>()
            .Named("UrlShortener")
            .WithPropertyValue("Name", "Minify")
            .WithPropertyValue("Description", "Produces a minified url of a given url.")
            .WithPropertyValue("Instructions", "!tiny [service] [url] i.e '!tiny google http://example.com/a-really-long-url-you-want-to-minify'");

        // Well we have a Google url service
        // but i want this service to have the same abstract parent
        // as the tinyurl service - since both are part of the minify module
        Bind<ISkypnetModule>()
            .To<UrlShortenerSkypnetModule>()
            .WhenParentNamed("UrlShortener")
            .Named("Google")
            .WithPropertyValue("Trigger", "google");

        // We also have a tiny url service
        // but i want this service to have the same abstract parent 
        // as the google service - since both are part of the minify module
        Bind<ISkypnetModule>()
            .To<UrlShortenerSkypnetModule>()
            .WhenParentNamed("UrlShortener")
            .Named("Tinyurl")
            .WithPropertyValue("Trigger", "tinyurl");

        // Well the tiny url provider should be injected 
        // into urlshortener named tinyurl
        Bind<IUrlShortenerProvider>()
            .To<TinyUrlProvider>()
            .WhenParentNamed("Tinyurl")
            .WithPropertyValue("ApiKey", "");

        // Well the google url service should be injected
        // into urlshortener named google
        Bind<IUrlShortenerProvider>()
            .To<GoogleUrlProvider>()
            .WhenParentNamed("Google")
            .WithPropertyValue("ApiKey", "");
    }
}

I've seen some kind of similar behavour with Spring.NET configuration where you could define objects in Spring.config and have abstract="true" and declare it as a parent object and then have two objects which have the same parent abstract object. I believe this is what I'm after but I haven't ever gone this far into setting up containers and dependency injection.

Was it helpful?

Solution

Seems the way I declared it in my answer is the correct way to go about it. So the answer is to declare the multiple bindings and this should create the new instances to work in the same SkypenetModule. Since both are instanced based on the .Named UrlShortenerSkypenetModule

    Bind<ISkypnetModule>()
        .To<UrlShortenerSkypnetModule>()
        .WhenParentNamed("UrlShortener")
        .Named("Google")
        .WithPropertyValue("Trigger", "google");


    Bind<ISkypnetModule>()
        .To<UrlShortenerSkypnetModule>()
        .WhenParentNamed("UrlShortener")
        .Named("Tinyurl")
        .WithPropertyValue("Trigger", "tinyurl");


    Bind<IUrlShortenerProvider>()
        .To<TinyUrlProvider>()
        .WhenParentNamed("Tinyurl")
        .WithPropertyValue("ApiKey", "");


    Bind<IUrlShortenerProvider>()
        .To<GoogleUrlProvider>()
        .WhenParentNamed("Google")
        .WithPropertyValue("ApiKey", "");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top