Dependency Injection inside Excel VSTO and Ninject.Extensions.Factory
Asked Answered
S

1

10

I'm trying to configure DI for an Excel VSTO project.

The generated code-behind for a given worksheet offers me a event called Startup, which is reponsible to set event handlers for events like Startup, Change, BeforeDoubleClick and so on.

I think is generally a good practice do avoid code in code-behind files.
What I do is create external classes that are responsible to manipulate the worksheet and call external code like web services, databases and domain logic.

I can create successfully a Factory to be consumed by the codebehind file and instantiate the worksheet logic class.

For example:

//...inside Sheet1.cs

private IExpenseWorksheetFactory _factory;

void ExpensesBeforeRightClick(Excel.Range target, ref bool cancel)
{
    Application.EnableEvents = false;

    var popup = _factory.CreateContextMenu();
    popup.ShowContextMenu(target, ref cancel);

    Application.EnableEvents = true;
}
// ... rest of Sheet1.cs

The code above is inside the code-behind file Visual Studio generates and it's minimal. The responsibility to show the popup is delegated to a distinct object. The factory object is responsible to talk with Ninject and get the object for me. This proxy is generated automatically using Ninject.Extensions.Factory project as I pass an interface like this:

/// <summary>
/// Abstract Factory for creating Worksheet logic objects. Meant to be used with Ninject Factory extension.
/// </summary>
public interface IExpenseWorksheetFactory
{
    ExpenseWorksheet CreateWorksheet();
    ExpenseWorksheet.ContextMenus CreateContextMenu();
    ExpenseWorksheet.Events CreateEventHandlers();
}

In the application startup, I have defined the the bindings and the binding for the factory itself:

//instantiate the kernel in app's Composition Root
_kernel = new StandardKernel();

//worksheet related stuff - seems to be ok to be singleton
_kernel.Bind<ExpenseWorksheet>().ToSelf().InSingletonScope();
_kernel.Bind<ExpenseWorksheet.Events>().ToSelf().InSingletonScope();
_kernel.Bind<ExpenseWorksheet.ContextMenus>().ToSelf().InSingletonScope();

//"automagic" factories
_kernel.Bind<IExpenseWorksheetFactory>().ToFactory();

The problem is:

How can I inject this factory in the generated code of VSTO worksheet? I don't like the idea of calling _kernel.Get<IExpenseWorksheetFactory> inside the Startup method of the worksheet. Is it possible to look for all available instances of Sheet1 and force the injection of the factory?

Senter answered 7/9, 2012 at 14:6 Comment(0)
H
4

Short answer: No.

You're looking for something like constructor injection, which reads admittedly more elegant. But as it is, this is not possible here because you don't have access to the c'tor in a VSTO addin.

But anyway, what's so bad in calling _kernel.Get<IExpenseWorksheetFactory>? After all, calling the DI container explicitly to resolve a dependency is one of its normal use cases, and the code-behind file is made for exactly that: Wiring things up.

You're right, that the code-behind should not contain any business logic, but on the other hand it definitely should contain all code that is required to wire things up. That's the primary reason why it exists. Or, in other words:

I think is generally a good practice do avoid code in code-behind files.

is not true in each and every case (but only for the vast majority of them) and you shouldn't overdo it here. If you do so, you'll find yourself fighting against the system - and remember, the system always wins ;-)...

Holladay answered 1/10, 2014 at 7:34 Comment(2)
Agree... The main focus is always to solve business problems and not, fight against the system and frameworks!Notch
How is constructor injection not possible? VSTO's startup event handler is where you'd configure ninject and then you're able to use DI for all of your service and repository objects as well as forms.Sextans

© 2022 - 2024 — McMap. All rights reserved.