Click here to Skip to main content
15,888,330 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hi everyone

I'm kinda stuck here with some requirement. We have a base code for an app that works for different clients, out of which some have specific code that applies just to them and we were looking for a way to merge the base code into one app and code the specific operations in different assemblies.

My approach is to make most of the methods virtual and when someone needs to add a specific operation in a method, he should inherit the base class, override the method and change whatever he has to there; after that, on the code base, it will load the assembly and find which methods were overridden so that when the method let's say Start() executes, if it's overridden, it should get the method from the assembly and call the InvokeMember but the target should be the actual instance of the class.
SO let´s say that I have a method like this:

C#
//Class in basecode
public class ProcessManager
{
     public virtual void Start()
     {
      //Check to see if the method is overridden
      //If it is, load the assembly:
      Assembly myDll = Assembly.LoadFrom(@"C:\Users...\My.dll");
            Type myType = myDll.GetType("MyNameSpace.OverridenClass");
            MethodInfo mi = myType.GetMethod("Start");
            myType.InvokeMember(mi.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, this, null);
     }
}
//Class in a different Assembly
public class OverriddenClass : ProcessManager
{
     public override Start()
     {
        //My implementation
     }
}


Like that, I'm getting a TargetException, the object doesn't match target. Idk why, if the overriden class inherits from ProcessManager.
Is there a way to correct this or is this approach incorrect?? I don't wanna specify a new Instance since the ProcessManager instance has already done some Initialization and has some values so I need to execute that method on that instance.

What do you guys recommend????

Thanks a lot!
Posted
Updated 2-Feb-12 12:37pm
v2
Comments
Sergey Alexandrovich Kryukov 2-Feb-12 19:48pm    
No wonder you have this problem. You are doing good thing but in a wrong way. You need to change the way a little bit to make it order of magnitude more robust, easily. I also feel that you have enough knowledge to get the right idea and follow it. So first, try to follow my approach. If you have problems, feel free to ask some follow-up questions.

No need to be silent all the time :-)
--SA

1 solution

What are you doing is a very bad way of getting the code out of an assembly using Reflection. It is based on the string, which you could misspell; or the profile of the method could be different, of something else. Even one of the methods InvokeMembers is not a best invocation technique, by far. In all cases, the compiler won't be able to detect a problem if you do something wrong.

Why doing all that, if a perfect technique exists? If you do it right, a compiler won't allow you to make a wrong invocation or a wrong call. The good technique is the best for maintenance.

You should do it based on interfaces. First, you should create some plug-in interface or a set of interfaces. You can define the interface in a separate assembly referenced by both host application and all valid plug-ins. You can also simply put it in the host assembly, but then you will need to reference you host assembly by the plug-in. Don't: worry: this is not a circular reference, because host assembly does not reference plug-ins, the dependency is created only during run time. So, this is perfectly legitimate technique which works.

Now, you expect the interface to be implemented by one of the classes of the loaded assembly. You can find out if some class implements your interface using GetInterface or GetInterfaces method to get implemented interfaces and compare it with the type of the interface you expect. Note that the name of interface type does not have to be involved, and even if you use it, you calculate it out of interface type, never hard-code (as you did in your code sample), which is critically important.

By the way, the type does not have to be public. More over, keep it internal. In this way, Reflection can access it, but direct referencing — not. Unless you need it, too, of cause.

On next step, you find out a constructor of expected signature and invoke it using System.Type.GetConstructors and invoke it using one of System.Reflection.ConstructorInfo.Invoke methods.

Finally, you cast the obtained class instance to the required interface. You can safely do it, as you already checked up that this interface is implemented by your type.

You are done! You got the interface referenced which is guaranteed to be valid and work. As you are calling interface methods only, they cannot be wrong or having wrong signature.

Please see:
http://msdn.microsoft.com/en-us/library/system.type.aspx[^],
http://msdn.microsoft.com/en-us/library/system.reflection.constructorinfo.invoke.aspx[^].

I use a bit more advanced method. The problem that in the previous steps, you have to scan all type. This is not so good. I add a special assembly-level attribute which tells me what types should be examined by the host as those implementing some interface. The host code gets this type (or types) from the attribute object and examine it (them) only.

I described this and other technique in my past answers:
Create WPF Application that uses Reloadable Plugins...[^],
AppDomain refuses to load an assembly[^],
code generating using CodeDom[^].

See also my other solutions on related questions:
Create WPF Application that uses Reloadable Plugins...[^],
Dynamically Load User Controls[^].

—SA
 
Share this answer
 
Comments
mario_silent 2-Feb-12 20:02pm    
Alright, thanks a lot for the reply. I was not gonna leave the hard-coded items the way they were of course.... I was just running a concept test to see if my idea was the right approach, it's my first time messing with dynamic assemblies so I just wanted some pointers and good practices to make my approach robust; I thank you a lot for your support, I'll try out your solution and get back to you if I have any questions, sounds pretty good to me :)

Thanks and also for the links! BRB
Sergey Alexandrovich Kryukov 2-Feb-12 20:46pm    
You are welcome. This certainly works and good for maintenance, was used in different projects.

What I describe not just helps to remove any hard-coding of immediate constants, this is the way to avoid using any names at all. Everything is justified by a compiler. During run time you only check up consistency of the assembly for being a right plug-in. There is no room for an error just by mistyping anything.

Good luck, call again.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900