views:

497

answers:

4

Morning guys,

We're currently developing a new piece of hand-held software. I cant discuss the nature of the application, so I'll use an example instead.

We've designing hand-held software for managing a school. We want to modularise each aspect of the system so that different schools can use different features.

Our system will start with a main menu and a login screen. I'd like this to be the base of the system and be where the modules will be added to. I.e. I'll have a project called SchoolPda.

I then want to have different modules. I.e. I want a registration module that will handle student registrations. I want a classroom module for managing classroom cleanliness, etc.

The way I'd possibly see this working is including/not including different dlls and having the base system's main menu expose buttons to access those modules if the dlls exist? That's just the kind of thing we're after.

Does anyone have any experience doing something like this? What will be the best way of doing it? We don't need to worry about the database as the database will always be the full database, but aspects wont get populated if the associated modules do not exist.

+3  A: 

I have been in projects that have done it two ways:

  • In one project we did not deploy certain DLLs if the customers weren't licensed. That's what you are suggesting. It worked fine. Of course, there was no way to enable those modules without an additional install, but it made perfect sense for that app.

  • In another project we deployed everything and only exposed to the end-users the menus, buttons, etc. for which the customer was licensed. We did that because then the user could easily add-on an extra module by adding a license for it. When the license was added, the stuff magically showed up on next login.

So in my experience, I would say look at your licensing model as one big piece of your decision. Think about whether you would ever want to add those extra modules on the fly.

nshaw
A: 

I don't know if it'll work on the CLR but have a look at MEF, for creating a way to discover and load your dlls / modules. You could also have your modules have a GetMenuItem method (or something to such of the ilk) that gets the information required so your main menu can add a button.

Obviously, use a different design for your main menu if it makes sense, but you'll want it to truly be both modulized and extendible, so that you can write your core, and in the future continue to write new components, without having to change you core.

I'm sorry if this doesn't make the largest amount of sense. Just hoped to give you an idea in one direction.

Sekhat
+3  A: 

I am currently also developing a application that runs on both Compact and Full framework and is built modular.

The way I implemented it is that it scans a location for dll's and enummerates over each type and looks if they have a "ScreenInfoAttribute" or "WidgetInfoAttribute" defined which contains usefull information about the class.

here's a snippet, it contains 3.5 code, but that's because we recently made the switch from 2.0, but the principle works in 2.0

public void Analyze(FileInfo file) {
        Assembly asm = Assembly.LoadFrom(file.FullName);
        List<Data.AnyPlugin> types = GetPluginTypes(asm.GetTypes());

        if (types.Count > 0) {
            types.ForEach(x => x.AssemblyPath = file.FullName);
            if (_plugins.ContainsKey(file.FullName)) {
                _plugins[file.FullName].Plugins.AddRange(types);
            } else {
                AssemblyPlugin asp = new AssemblyPlugin();
                asp.Ass = asm;
                asp.Plugins = types;
                _plugins.Add(file.FullName, asp);
            }
        }
    }

    private List<Data.AnyPlugin> GetPluginTypes(Type[] types) {
        List<Data.AnyPlugin> returnTypes = new List<AnyPlugin>();
        foreach (Type t in types) {
            Data.AnyPlugin st = GetPluginType(t);
            if (st != null) returnTypes.Add(st);
        }
        return returnTypes;
    }

    private Data.AnyPlugin GetPluginType(Type type) {
        if (type.IsSubclassOf(typeof(Screens.bScreen<T>))) {
            Screens.ScreenInfoAttribute s = GetScreenAttrib(type);
            if (s != null) {
                return new Data.ScreenPlugin("", type, s);
            }
        } else if (type.IsSubclassOf(typeof(Widgets.bWidget<T>))) {
            Widgets.WidgetInfoAttribute w = GetWidgetAttrib(type);
            if (w != null) return new Data.WidgetPlugin("", type, w);
        }
        return null;
    }

    private Screens.ScreenInfoAttribute GetScreenAttrib(Type t) {
        Attribute a = Attribute.GetCustomAttribute(t, typeof(Screens.ScreenInfoAttribute));
        return (Screens.ScreenInfoAttribute)a;
    }
Stormenet
We did something similar, but Assembly.LoadFrom is an expensive call. So we switched to a xml descriptor, where we have all plugins defined with AssemblyName and ClassName, so that you can just call Activator.CreateInstance directly.
Louis Haußknecht
A: 

Just let every Module implement a common interface. Add Methods like GetButtons() or GetActions().

You can then place infomation about the AssemblyName and the ClassName in a config - file. Now its's easy to load the specified Assembly and create an instance of the class with Activator.CreateInstance, cast it to your interface and call the GetButtons(), etc. methods.

Louis Haußknecht