Monday, December 31, 2012

ClickOnce Dynamically Loaded assemblies + ninject

ClickOnce deployment of my winforms dev tool was failing to the dba team because Microsoft.Web.Administration wasn't installed. Is this feature required for the tool to be useful? Not at all. Did MEF, and it wasn't helpful at all. How do you get MEF assemblies deployed for the clients to be able to pick them up? Read about Dynamically loaded assemblies via ClickOnce. Ah the magic is happening now.
[SecurityPermission(SecurityAction.Demand, ControlAppDomain = true)]
  class DynamicDownload
  {
    // Maintain a dictionary mapping DLL names to download file groups. This is trivial for this sample, 
    // but will be important in real-world applications where a feature is spread across multiple DLLs, 
    // and you want to download all DLLs for that feature in one shot. 
    Dictionary<String, String> DllMapping = new Dictionary<String, String>();

    public DynamicDownload()
    {
      DllMapping["Domain.WebAdministration"] = "Domain.WebAdministration";
      AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
      Assembly newAssembly = null;

      if(!ApplicationDeployment.IsNetworkDeployed)
      {
        return null; //this was required to debug locally, otherwise reactive was failing to load
        throw new FileLoadException("Cannot load assemblies dynamically - application is not deployed using ClickOnce");
      }
      var deploy = ApplicationDeployment.CurrentDeployment;

      var nameParts = args.Name.Split(',');
      var dllName = nameParts[0];
      string downloadGroupName = DllMapping[dllName];
      try
      {
        deploy.DownloadFileGroup(downloadGroupName);
      }
      catch (DeploymentException de)
      {
        MessageBox.Show("Downloading file group failed. Group name: " + downloadGroupName + "; DLL name: " + args.Name);
        throw;
      }

        newAssembly = Assembly.LoadFile(Application.StartupPath + @"\" + dllName + ".dll");
      
      return newAssembly;
    }
  }
static void RegisterDynamicAssemblies()
    {
      Kernel.Bind<DynamicDownload>().ToSelf().InSingletonScope(); //this should never run twice
      var dd=Kernel.Get<DynamicDownload>(); 

      Kernel.Bind<Func<string, IAdministerIIS>>().ToMethod(context => s => new IISAdministration(s));
    }
And now that feature is dynamically loaded the first time the feature is attempted to be used.