4

Note: All sample code is greatly simplified.

I have a DLL defined as:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;

namespace RIV.Module
{
    public interface IModule
    {
        StringWriter ProcessRequest(HttpContext context);
        string Decrypt(string interactive);
        string ExecutePlayerAction(object ParamObjectFromFlash);
        void LogEvent(object LoggingObjectFromFlash);
    }
}

Now, outside of my solution, other developers can define concrete classes and drop them into the BIN folder of my app. Maybe something like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RIV.Module;

namespace RIV.Module.Greeting
{
    public class Module : IModule
    {
        public System.IO.StringWriter ProcessRequest(System.Web.HttpContext context)
        {
            //...
        }
        public string Decrypt(string interactive)
        {
            //...
        }
        public string ExecutePlayerAction(object ParamObjectFromFlash)
        {
            //...
        }
        public void LogEvent(object LoggingObjectFromFlash)
        {
            //...
        }
    }
}

Now, in my app I would need to know that a new Module was available (I am guessing via web.config or something along those lines) and then be able to call it based off of some trigger in the database Campaign table (which maps to the module to use for that specific campaign).

I am trying to instantiate it this way:

var type = typeof(RIV.Module.Greeting.Module);
var obj = (RIV.Module.Greeting.Module)Activator.CreateInstance(type);

However, the compiler belches because a reference was never set to RIV.Module.Greeting.dll!

What am I doing wrong?

Keith Barrows
  • 24,802
  • 26
  • 88
  • 134
  • How will you know what to do with these modules? – Anon. Feb 09 '11 at 00:42
  • where and at what point are you trying to instantiate? – deostroll Feb 09 '11 at 00:46
  • @SLaks: The question was an exploratory one directed at the asker. I know how one interacts with classes in dynamically-loaded assemblies - my intention was to get the asker thinking about this stuff and ending up with a better understanding. – Anon. Feb 09 '11 at 00:55
  • @Anon: He seems to understand that part (except for the cast). You should be asking how he wants to find the assemblies, and (more importantly), the class names. – SLaks Feb 09 '11 at 00:58
  • @SLaks: Not really. Having a common interface but not attempting to use it when casting the instantiated object was, at least in my mind, a quite significant "not-getting-it". Even if it is as you say, I'd rather hear the asker's point-of-view. Because what the asker actually knows is probably more relevant than what you, personally, think they understand. – Anon. Feb 09 '11 at 01:02
  • To define a module for our system will require (1) reading the API definition and understanding what is expected, (2) implementing it with the IModule interface, submitting it (via an as yet undetermined path - probably a developers web page of some sort) & (3) our team reviewing, testing and publishing it. – Keith Barrows Feb 09 '11 at 20:58
  • Part of the submittal process will include a description of what the module does. For instance, a module I am writing, Automatic Negotiation, allows a client to enter a Retail Price, Lowest Acceptable Price, # of bids allowed, bidding algorithm type. An end user would then be able to bid on an item interactively. – Keith Barrows Feb 09 '11 at 21:01
  • Instantiation will happen when a client site requests a predefined campaign via web service or http handler. The Controller object would then determine from the campaign info what module is responsible for handling the setup & response traffic from our widget (currently a Flash App). Right now I am unsure as to use a Factory Pattern, an IoC Pattern or some other Pattern. I am, at this point, exploring technologies, ease of use, what most correctly supports our (flexible) goals. And yes - I am "not getting it" 100% when trying to use MSDN examples (which are all in the same prj/namespace!) – Keith Barrows Feb 09 '11 at 21:07

2 Answers2

2

You need to use more reflection:

  • Load the assembly by calling Assembly.Load
  • Find the type by calling someAssembly.GetType(name) or searching someAssembly.GetTypes()
  • Pass the Type instance to Activator.CreateInstance
  • Cast it to your interface.
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
1

Instead of typeof(RIV.Module.Greeting.Module), try using

var type = Type.GetType("RIV.Module.Greeting.Module, RIV.Module.Greeting");

(i.e. load the type by specifying its assembly-qualified name as string) and casting to IModule.

This approach requires you to know the exact class and assembly names of the modules (as you wrote, they could be stored in web.config).

Alternatively, you could go for a completely dynamic plugin approach:

  1. establish a convention that all module assemblies should be named "RIV.Module.XYZ"
  2. scan the bin directory for matching DLLs
  3. for each DLL, load it (e.g. Assembly.Load) and scan for types implementing IModule
  4. instantiate all found types and cast to IModule
Jakub Berezanski
  • 1,053
  • 9
  • 13
  • This will only work if the loader can find the assembly on its own. (Or if you handle `AssemblyResolve`) – SLaks Feb 09 '11 at 00:59
  • I was thinking more of an approach where new App Modules are sent in via a Create/Edit/Publish pipeline and the Assembly, Type & Path are stored in the DB table called Modules (in one of those states where **published** would mean they are available to our client base). – Keith Barrows Feb 09 '11 at 21:25
  • Sorry for the delay. If you would like the client application to download the modules from a server (where they are stored in the DB), then no static config is necessary - the client asks the server for available modules, downloads the assemblies, caches them in a local directory and then invokes the dynamic loading logic I wrote about (minus the discovery part, as the client already knows what modules it should load and what are their assembly and type names). – Jakub Berezanski Apr 19 '11 at 09:01