views:

149

answers:

5

I am designing a loosely-coupled structure. I want to call classes from different assemblies/namespaces via a code which is represented by a String. My design is, each of client's business rules is on different assemblies and not dependent on each other (ONE client is to ONE DLL ratio) so that when I made an update on business rules of 1 client, it would not affect the others. My attention now is on using Factory Design and using Activator.CreateInstance() Method.

This is the project setup (2+n DLL's)

 namespace Foundation; // where the interfaces/abstract resides
 namespace Factory; // has dependency on Foundation assembly
 namespace Client1; // client1's DLL, no dependency
 namespace Client2; // client2's DLL, no dependency
 The UI // only referenced to the Foundation and Factory not the Clients

The actual code

 namespace Foundation
 {
   public interface IBusinessRules
   {
     string GetBusinessRule();
  }
 }

 namespace Client1 //DLL for client 1
 {
   public class BusinessRules : Foundation.IBusinessRules
   {
    public string GetBusinessRule()
    {
        return "Client1 Business Rule";
    }
   }
}

namespace Client2 //DLL for client 2
{
   public class BusinessRules : Foundation.IBusinessRules
   {
     public string GetBusinessRule()
     {
        return "Client2 Business Rule";
     }
   }
}


namespace Factory
{
  public static class Invoker<T> where T: Foundation.IBusinessRules
  {
    public static T FetchInstance(string clientCode) 
    {
        return (T)Activator.CreateInstance(Type.GetType(clientCode));   
    }
  }
}


 //sample implementation that generates unhandled Exception
 using Factory;
 using Foundation;
 static void Main(string[] args)
 {
      //the parameter is maintained in the database
       IBusinessRules objClient1 = Invoker<IBusinessRules>.FetchInstance("Client1"); 

       //should call Client1.BusinessRules method
        Console.WriteLine(objClient.GetBusinessRule());  
        Console.Read();

        objClient = Invoker<IBusinessRules>.FetchInstance("Client2");

        //should call Client2.BusinessRules method
        Console.WriteLine(objClient.GetBusinessRule()); 
        Console.Read();      
  }

Any idea why my sample doesn't work? And any suggestion to improve the design? Thanks in advance.

How about using

Expression.Lambda

anyone?

+1  A: 

You need to use the full name of the class.

for example:

Type.GetType("System.Collections.Generic.Dictionary`2[System.String,[MyType,MyAssembly]]")
CD
+1  A: 

If you use FetchInstance("Client.BusinessRules") your code works, IF everything is in the same assembly. If it's not (as per your design) you need to give an AssemblyQualifiedName.

I would do the design differently though. Keep your call with just "Client1" as Parameter but change the implementation of the Factory. Dynamically load the assembly for the given client (with Assembly.Load() or Assembly.LoadFrom()), then use clientAssembly.CreateInstance() to istantiate your type.

Edit: Crude code sample:

namespace Factory
{
  public static class Invoker<T> where T: IBusinessRules
  {
    public static T FetchInstance(string clientCode)
    {
        var clientAssembly = Assembly.LoadFrom(clientCode + ".dll");

        return (T)clientAssembly.CreateInstance(clientCode+".BusinessRules");
    }
  }
}

If you dont't know the class name in the client-dll, you have to search for an applicable Type, for example with clientAssembly.GetTypes().

TToni
Yes, I want to keep the parameter as is.
CSharpNoob
It still throws error when I pass "Client1" on the parameter using your code
CSharpNoob
You need to have the Client1.dll in the same directory as the executing assembly. If that doesn't work: What's the error-message and at which line is it thrown?
TToni
A: 

If you are loading the type from an external assembly, I would recommend using Activator.CreateInstanceFrom.

var typeReference = Activator.CreateInstanceFrom(assemblyPath, fullyQualifiedClassName);
return typeReference.Unwrap() as T;
AJ
I tried it but it returns nullreferenceexception
CSharpNoob
I assume it's throwing the nullreference exception from the Unwrap() call, which tells me that either your assemblyPath or your fullyQualifiedClassname are incorrect in the CreateInstanceFrom call. Make sure that your path is right, and that you are including ALL of your namespaces in your fullyQualifiedClassname.
AJ
A: 

If you want to be able to add business rules as dlls after deployment and create them at runtime, I suggest you have a business rules folder under you app, load all dlls in that app, search for all types that implement of IBusinessRules in each dll using reflection. Given that you now have handles on the types, creating one based on name would be easy and your project would scale out.

Either that, or pass the assembly qualified name of the classes to your methods.

Kell
A: 

Thanks to your help guys i finally Got it! I just modify the Factory

 namespace Factory
 {
    public static class Invoker<T> where T : Foundation.IBusinessRules
    {
        public static T FetchInstance(string clientCode)
        {
           Type objType = Type.GetType(clientCode + ".BusinessRules," + clientCode);
           return (T)Activator.CreateInstance(objType);

    }
}

But I wonder about its effeciency (performance hit) because it uses Reflection..

CSharpNoob