Search code examples
c#excelsimple-injector

SimpleInjector ActivatorException when resolving COM interop type


When attempting to resolve a type from my Simple Injector container it is throwing an ActivatorException saying that my type is not registered although I am fairly sure that it has been.

ThisAddin.cs

 private void ThisAddIn_Startup(object sender, EventArgs e)
 {
     var container = new Container();
     container.RegisterSingleton<Microsoft.Office.Interop.Excel.Application>(() => this.GetHostItem<Microsoft.Office.Interop.Excel.Application>(
            typeof(Microsoft.Office.Interop.Excel.Application), "Application"));
     var application = container.GetInstance<IApplicationAdapter>();
 }

In another Assembly, in this case ExcelInterface.

ApplicationAdapter.cs

internal class ApplicationAdapter : IApplicationAdapter
{
    public Application(Microsoft.Office.Interop.Excel.Application excelApplication)
    {
    }
}

IApplicationAdapter.cs

public interface IApplicationAdapter
{
}

But when it comes to resolve the Excel.Application I get the exception as:

enter image description here

So trying to debug this I thought that it hadn't actually been registered correctly, but when checking the added registrations in the container I can see that the Excel.Application has been added:

enter image description here

I've also checked and made 100% sure that the two classes are referencing the same Excel.Application assembly which they are.

Finally I would have added the Excel.Application instance to the container but since this is a __COMObject I am unable to register it as an Excel.Application.

I'm totally at a loss to why Simple Injector is unable to correctly resolve the interface.


Solution

  • I'm not sure why this happens. I can confirm that an exception occurs when trying to register this as in your example.

    With what you're trying to do, I agree with Steven's comment, yuck!! Creating your own, possible multiple, abstractions over the application object should be the way to go.

    However what you want is possible with some minor changes. Instead of registering the application object itself, which throws for some strange reason, you could register a IApplicationProvider like this:

    internal interface IApplicationProvider
    {
        Excel.Application CurrentApplication { get; }
    }
    
    class ApplicationProvider : IApplicationProvider
    {
        public ApplicationProvider(Excel.Application application)
        {
            this.CurrentApplication = application;
        }
        // c# 6 syntax
        public Excel.Application CurrentApplication { get; } 
    }
    

    Which you can register as:

    var container = new Container();
    var app = this.GetInstance<Microsoft.Office.Interop.Excel.Application>("Application");
    var appicationProvider = new ApplicationProvider(app);
    
    container.RegisterSingleton<IApplicationProvider>(appicationProvider);
    
    //Make other registrations....
    
    container.Verify();
    

    I made a little helper method for readability:

    public TInstance GetInstance<TInstance>(string key) where TInstance : class
    {
       return this.GetHostItem<TInstance>(typeof (TInstance), key);
    }
    

    Everywhere you now need the application object you can just inject the IApplicationProvider and use like this:

    class SomeService
    {
        private readonly IApplicationProvider applicationProvider;
    
        public SomeService(IApplicationProvider applicationProvider)
        {
            this.applicationProvider = applicationProvider;
        }
    
        public int GetWorkbookCount()
        {
            return this.currentApplication.Workbooks.Count;
        }
    
        // c# 6 syntax
        private Excel.Application currentApplication => 
                   this.applicationProvider.CurrentApplication;
    }
    

    Tested with Office 2016 and this works as expected.