Geeks With Blogs
New Things I Learned

A colleague recently got a project where they’d like to make sure the app he’s working on can still work even when some DLL / assemblies are not there.  These ‘extra’ assemblies are actually assemblies from another app, which can be used to have these 2 application communicate with each other, so there are a small number of entry points where calls are being made to these other assemblies.  Also, in most cases, it’s a simple method call, not actually embedding class objects defined in the other assembly in the current app.  And of course this scenario is for non-GAC installed assemblies.

Of course, in terms of development when they bridged these 2 applications, everyone assumed both applications will be deployed, hence all assemblies will exist.  And of course a new requirement came out where it is now acknowledged that the user can install only one of these applications and the application should never crash – it should just put up a nice error message box specifying that the other app / assembly is not there and recommend the user to install the other app to gain that functionality.

Common wisdom says that the code should be modified such that the code that try to call the method in the other assembly should first try to check for the existence of the assembly.  That check for existence can be simple (check if file exist) to something more complicated (use reflection to load the assembly).  In any case, since the number of places this needs to be done is small, and in the future these type of code will be added in other places, my colleague was thinking of using the latter approach and use a generic method that the consumer can call.  The code will look something like the following:

public class ConsumingClass
{
   public void ConsumerCode()
   {
      bool success;
      // Code that will call the wrapper
      GenericMethodWrapper.CallMethodInOtherAssembly(out success, "Initialize", null);
      GenericMethodWrapper.CalculateCostAndPrice(out success, 100M, 10M);
   }
}

public class GenericMethodWrapper
{
   private const string OtherAssemblyName = "SecondAppAssembly.dll";
   private const string TypeContainingMethods = "SecondAppAssembly.APIClass";

   public static object CallMethodInOtherAssembly(out bool success, string methodName, params object[] parameters)
   {
      success = false;
      string filename = Path.Combine(Environment.CurrentDirectory, OtherAssemblyName);
      // Verify assembly is there
      if (File.Exists(filename) == false)
      {
         DisplayAssemblyNotFoundError();
         return null;
      }

      // Load assembly & get the type containing method & then get the method
      Assembly assembly = Assembly.LoadFile(filename);
      Type apiType = assembly.GetType(TypeContainingMethods);
      MethodInfo method = apiType.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);

      // Execute the method
      object result = method.Invoke(null, parameters);
      success = true;
      return result;
   }

   // Helper Method
   public static object CalculateCostAndPrice(out bool success, decimal initialCost, decimal markup)
   {
      return CallMethodInOtherAssembly(out success, "CalculateCostAndPrice", initialCost, markup);
   }

   // Other code here too...
}

Essentially any code that previously calls the assembly directly, gets re-routed to use the GenericMethodWrapper class; in the code above I’ve shown the really generic method ( CallMethodInOtherAssembly ) and the friendlier one ( CalculateCostAndPrice ), which calls the first method anyway.  The CallMethodInOtherAssembly method essentially verifies that the assembly is there, loads it, and then it will load a Type containing the static method to execute.  I’m simplifying the code here to illustrate how to do this – in practice the method may not be a static method, thus an instance has to be created, so there can be variants to this too.

Even though the code above works, it bothers me that there are quite a bit of string literals being used (even though they are defined as constants); which make the code to be fairly brittle.  It doesn’t have compile-time checks, and there’s no guarantee the method name, or the parameter list won’t change as well.  So I tried to see if there’s a way to do it, but make it ‘safer’ (yeah, like any code is safe :) ).

My initial background was programming in C / C++, and in Windows, if you have DLLs that an application needs to run then that DLL has to be there for the application to run.  If you need a late-binding capability, you have to call LoadLibrary and use GetProcAddress to call methods using function pointers (huh, remembering that gives me the shivers…).  However, in .NET projects, even without late binding, .NET applications / assemblies do not require all assemblies to be there.  With JIT-compiling at a per-method level, only methods that are invoked will then have references within those method require loading assemblies (if that method refers to objects/methods residing in another assembly).  It’s actually a rather staggering difference when I transition, because in the past I’m almost guaranteed if my application is missing any DLLs it’ll squawk about it and I won’t be able to run the application.  .NET does it the other way around, completely.

However, due to that behavior, we can actually take advantage of this; assemblies referenced never gets checked/loaded until a method that references that assembly is hit.  So, we can basically provide a strongly-typed method that acts as a wrapper – like follows:

public class ConsumingClass
{
   public void ConsumerCode()
   {
      // Code that will call the wrapper
      MethodWrapper.Initialize();
      MethodWrapper.CalculateCostAndPrice(100M, 10M);
   }
}

public class MethodWrapper
{
   public static decimal CalculateCostAndPrice(decimal cost, decimal markup)
   {
      decimal result = 0;
      try
      {
         result = CalculateCostAndPriceInternal(cost, markup);
      }
      catch (FileNotFoundException ex)
      {
         // Throws a FileNotFoundException when assembly can't be found - process error here
         DisplayAssemblyNotFoundError();
      }

      return result;
   }

   public static void Initialize()
   {
      try
      {
         InitializeInternal();
      }
      catch (FileNotFoundException ex)
      {
         // Throws a FileNotFoundException when assembly can't be found - process error here
         DisplayAssemblyNotFoundError();
      }
   }

   private static decimal CalculateCostAndPriceInternal(decimal cost, decimal markup)
   {
      return SecondAppAssembly.APIClass.CalculateCostAndPrice(cost, markup);
   }

   private static void InitializeInternal()
   {
      SecondAppAssembly.APIClass.Initialize();
   }

   // Other code here too...
}

The first method basically just encapsulates calling an internal method, which will call the actual implementation in the assembly.  However, by encapsulating it this way, when CalculateCostAndPrice gets called, it sees that everything in that method resides in the current assembly, so no other assembly is loaded.  When it tries to execute the CalculateCostAndPriceInternal method, it’ll see that it needs to load the SecondAppAssembly, and will try to load it.  If the assembly is not found, then it’ll throw a FileNotFoundException, which we can trap for.

This approach allows proper references to the assembly during development, making sure any erroneous method calls or parameter types are caught during compile time but also allows a way to have the code fails gracefully when the assembly is not available.  However, the drawback as seen from the code above seems to be you have to create 2 methods as wrapper to the API methods.  Each initial wrapper method also needs to have this somewhat cumbersome try-catch blocks, and have to be repeated for each method we’d like to make as entry points.  If the methods need to be static methods, then there are not that many option; however if it’s an interface, it’s easier to abstract the above code to look like the following:

public interface IAPIMethods
{
   decimal CalculateCostAndPrice(decimal cost, decimal markup);
   void Initialize();
}
public class MethodWrapper
{
   private static IAPIMethods _apiMethods = null;
   private static IAPIMethods APIMethods
   {
      get { return _apiMethods ?? GetAPIMethods(); }
   }

   private static IAPIMethods GetAPIMethods()
   {
      try
      {
         // APIClass is changed to use instance methods, not static methods
         return GetAPIMethodsInternal();
      }
      catch (FileNotFoundException ex)
      {
         // Throws a FileNotFoundException when assembly can't be found - process error here
         DisplayAssemblyNotFoundError();
      }

      return _apiMethods;
   }

   private static IAPIMethods GetAPIMethodsInternal()
   {
      _apiMethods = new APIClass();
      return _apiMethods;
   }

   public static decimal CalculateCostAndPrice(decimal cost, decimal markup)
   {
      decimal result = 0;
      if (APIMethods != null)
         result = APIMethods.CalculateCostAndPrice(cost, markup);

      return result;
   }

   public static void Initialize()
   {
      if (APIMethods != null)
         APIMethods.Initialize();
   }

   // Other code here too...
}

The APIClass needs to be augmented to have its methods be interface implementations of IAPIMethods and not static and once that’s done, further methods that needs to be exposed will just require 1 wrapper, and the code will be fairly sparse (checks to make sure the IAPIMethods interface is not null, then calling the method).  Having the APIClass implements interface also allows future replacement of the APIClass with little change to the consuming code.  You can even make the MethodWrapper class itself implement the same IAPIMethods interface, that way consuming code will use the same construct as when calling the actual implementation – abstracted via the IAPIMethods interface.

To make the code to be one-liner, you can provide helper methods and you’d call those helper methods with lambda expressions, but I think that’s a bit overkill.  Overall, the idea is to use how .NET loads assemblies and use it to make a ‘late-binding-behaved’ code but without the hazards of trying to use reflection to call late-bound methods / classes.

Posted on Friday, August 14, 2009 7:12 AM | Back to top


Comments on this post: Trap for assemblies that may not exist

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Muljadi Budiman | Powered by: GeeksWithBlogs.net