views:

62

answers:

2

Hey

I'm currently attempting to read information about an object from the web.config file and create and object based on that. However, I only wish to be able to create objects from classes that extend a particular interface (IModule). The type of the object is coming from a type attribute in the web.config:

  <module moduleAlias="Test" type="ASPNETMVCMODULES.TestModule" connectionStringName="ApplicationServices">
    <properties>
      <property name="prop1" value="abc" type="System.String"/>
      <property name="prop2" value="123" type="System.Int32"/>
    </properties> 
  </module>

In my code, ASPNETMVCMODULES.TestModule does, in fact, extend IModule and it loads in just fine with the code below:

The problem I am having is that, because I'm defining every object as an IModule, I cannot gain access to any additional methods or properties I add to the extending class. Is there any way for me to create the object with its own type, whilst ensuring it extends IModule? Might using System.Convert be worthwhile to convert from IModule to TestModule (in this case)?

Ideally, I'd like to be able to define the object using modT which is type of the object straight from the web.config.

    public void LoadModules()
    {
        ASPNETMVCMODULES.Configuration.ModulesSection allModules = (ASPNETMVCMODULES.Configuration.ModulesSection)System.Web.Configuration.WebConfigurationManager.GetSection("mvcmodules");
        foreach (Configuration.Module mod in allModules.Modules)
        {
            Type modT = Type.GetType(mod.ModuleType);

            IModule ob = (IModule)Activator.CreateInstance(modT);
            foreach (ASPNETMVCMODULES.Configuration.ModuleProperty modP in mod.ModuleProperties)
            {
                object origVal = modP.Value;
                object newVal = Convert.ChangeType(origVal, Type.GetType(modP.PropertyTypeString));
                ob.Properties.Add(modP.Name, newVal);
            }

            //Have to add some properties manually
            ob.Properties.Add("ConnectionString", (string)mod.ConnectionString);

            //RegisterModule(string, IModule)
            RegisterModule(mod.ModuleAlias, ob);
        }
    }

Thanks

+2  A: 

You can determine if a class has a particular interface with the following bit of code:

public void LoadModules()
{
   // ...

   Type modT = Type.GetType(mod.ModuleType);
   if (InheritsFromInterface(modT, typeof(IModule))
   {
      IModule ob = (IModule)Activator.CreateInstance(modT);   
       // ...
   }
}

public bool InheritsFromInterface(Type inheritor, Type interface)
{
    Type result = inheritor.GetInterface(interface.FullName);
    return result != null;
}

Hopefully this is what you're after.

GenericTypeTea
That certainly helps with ensuring only those classes that extend the interface are loaded, thank you. However, I'm still having issues with creating an instance of `modT`. If I don't cast `Activator.CreateInstance(modT);` to `IModule` then I'm unable to access its methods and properties later in the code.
AndyBursh
@AndyBrush - I updated the answer. Is that what you meant? It's what you've done already and it should work fine.
GenericTypeTea
@GenericTypeTea Thanks. This code works fine for loading all of the modules from web.config, however it begins to flounder when I need to access any additional methods or properties from the type `modT`. Is there a way to explicitly create an object of `modT` and not `IModule`? Perhaps in a similar manner to `IModule ob = (IModule)Activator.CreateInstance(modT);`?
AndyBursh
@AndyBrush - I think you're misunderstanding how Activator works. Also, you are NOT creating an IModule, you are creating an instance of the class that inherits IModule. You can not create an Interface. System.Activator will create the full type. I.e. `System.Activator(modT);` will return an object that IS a new instance of modT. Just because you cast it to IModule does not turn it into just an IModule.
GenericTypeTea
@GenericTypeTea Yes, it appears I am misunderstannding how Activator works, thanks. Later in my code, I need to be able to access methods that are specific to `modT`; for example: `ViewData["USERNAME"] = Modules["Test"].TestDataConnection();`. With my current code, I get an error that says `IModule` does not contain a definition for `TestDataConnection` (which is in `TestModule`). Could this be a result of the way I'm storing and retrieving those objects?
AndyBursh
@AndyBrush - I'm assuming `Modules["RAR"]` is returning an `IModule`? The reason you get this is because you're calling a method that `IModule` does not enforce. The only way to use `TestDataConnection()` is to know the concrete type of the current `IModule`. When you are using `Modules["Test"]` do you know that it is in fact `TestModule`? If you do you can cast it: `((TestModule)Modules["Test"]).TestDataConnection();`
GenericTypeTea
@GenericTypeTea Yes, `Modules["RAR"]` is returning an IModule. However, I don't know that `Modules["RAR"]` is ALWAYS going to be type `TestModule`. It could be whatever type comes from web.config, so long as it extends `IModule`. I just tested casting it myself, but I came up with the same error. However, it would appear I was casting it wrong (casting the whole call, not just `Modules["Test"]`). Thanks a lot, that's solved my problem completely!
AndyBursh
@AndyBrush - no problem, but I do suggest you do a little more reading on Interfaces and Inheritance :) it'll make your life a lot easier.
GenericTypeTea
A: 

You have a few different options, I have used these two myself.

  1. Use XmlSerializer
  2. Create an instance with Activator.CreateInstance(T) then set the properties yourself using Reflection, PropertyInfo[] props = typeof(T).GetProperties() and try to find the properties in your created instance.
Patrick
Thanks for the input, but I have it creating all the objects and settings properties correctly right now. My issue is with typing those objects; I need them to be the same as the type in `modT` instead of `IModule`, which is the interface they extend, in order to access their properties and methods in subsequent code.
AndyBursh