0

Been using LightInject for a while now and it has been great! Hit a snag trying to support multiple constructors of the same type, though. See the simplified example below. Foo has four constructors, differing by the type and number of arguments. I register one mapping per constructor. The first time I call GetInstance to retrieve an IFoo, it blows up with the following exception. What am I missing? How can I accomplish this functionality?

InvalidCastException: Unable to cast object of type 'LightInject.ServiceContainer' to type 'System.Object[]'.

Public Interface IFoo

End Interface

Public Class Foo
    Implements  IFoo

    Public Sub New()

    End Sub

    Public Sub New(name As String)

    End Sub

    Public Sub New(age As Integer)

    End Sub

    Public Sub New(name As String, age As Integer)

    End Sub

End Class


container.Register(Of IFoo, Foo)
container.Register(Of String, IFoo)(Function(factory, name) New Foo(name))
container.Register(Of Integer, IFoo)(Function(factory, age) New Foo(age))
container.Register(Of String, Integer, IFoo)(Function(factory, name, age) New Foo(name, age))

Dim f1 As IFoo = container.GetInstance(Of IFoo)()                     'BOOM!
Dim f2 As IFoo = container.GetInstance(Of String, IFoo)("Scott")
Dim f3 As IFoo = container.GetInstance(Of Integer, IFoo)(25)
Dim f4 As IFoo = container.GetInstance(Of String, Integer, IFoo)("Scott", 25)
  • Your `Foo` class doesn't seem like a component that should be resolved by your DI container; it seems like an entity or DTO instead. Objects like those should not be resolved by your DI container, because that would only lead to ambiguity. – Steven Nov 05 '16 at 11:34

1 Answers1

0

You can use Typed Factories to cleanly accomplish this.

http://www.lightinject.net/#typed-factories

Imports LightInject

Namespace Sample
    Class Program
        Private Shared Sub Main(args As String())
            Console.WriteLine("Go")

            Dim container = New ServiceContainer()
            container.Register(Of FooFactory)()

            Dim fooFactory = container.GetInstance(Of FooFactory)()
            Dim f1 As IFoo = fooFactory.Create()
            Dim f2 As IFoo = fooFactory.Create("Scott")
            Dim f3 As IFoo = fooFactory.Create(25)
            Dim f4 As IFoo = fooFactory.Create("Scott", 25)

            Console.WriteLine("Stop")
            Console.ReadLine()
        End Sub
    End Class

    Public Interface IFoo

    End Interface

    Public Class Foo
        Implements IFoo


        Public Sub New()
        End Sub


        Public Sub New(name As String)
        End Sub


        Public Sub New(age As Integer)
        End Sub


        Public Sub New(name As String, age As Integer)
        End Sub

    End Class

    Public Class FooFactory

        Public Function Create() As IFoo
            Return New Foo()
        End Function

        Public Function Create(name As String) As IFoo
            Return New Foo(name)
        End Function

        Public Function Create(age As Integer) As IFoo
            Return New Foo(age)
        End Function

        Public Function Create(name As String, age As Integer) As IFoo
            Return New Foo(name, age)
        End Function
    End Class
End Namespace

Note, you can create an IFooFactory interface if you feel it adds value.

Jeffrey Patterson
  • 2,342
  • 1
  • 13
  • 9
  • Thanks for the quick answer! The need to use a Factory to accomplish this seems like an unnecessary layer of abstraction. I have a data access layer with over one hundred entities that all implement a unique interface. The need for a factory layer will result in the need for hundreds of factories and corresponding interface definitions. Do you know, is there a technical reason why it is designed this way? –  Nov 04 '16 at 20:16
  • Do you really need multiple constructors for each of your data layer classes? Are they necessary (perhaps because the behavior of the class changes depending on which constructor you use), or just for convenience? If it's the later, I'd stick with just the default constructors as it provides the cleanest path to using IOC containers as no factory would be necessary. – Jeffrey Patterson Nov 07 '16 at 13:24
  • I have to ask though, why do you need an IOC container to new up your data layer classes? I've seen code bases where dependency injection was overused. You should ask yourself, am I using dependency injection here because I need to mock/fake the thing I'm injecting? If the answer is no, perhaps you don't need to inject the dependency. In a lot of code bases, the data layer is really just a bunch of structures with no real behavior, and in that case I just new up those data layer classes where I need them. Hope this helps. – Jeffrey Patterson Nov 07 '16 at 13:24
  • We inherited a code base that was pretty much one large class library, including the DAL, business logic, helper classes, etc. We've been trying to break it up into smaller, well-defined, libraries. Unfortunately there is a ton of circular references, so we need dependency injection in order to refactor that gigantic library. We're not overusing DI by any stretch. –  Nov 07 '16 at 13:40
  • We have multiple constructors because there are different use cases for instantiating an entity. One is to create an empty entity. Another is to provide the primary key ID and have it automatically populated from the DB. Another is to provide a DataRow. We also allow partially populating an entity by specifying the properties to load as arguments to a constructor. –  Nov 07 '16 at 13:45
  • There is a lot of existing code that invokes each of those different types of constructors. We could separate it out, as you've suggested, and force our developers to write two (or more) lines of code instead of just one line whenever they want to use an entity, but we would receive a lot of push back and negative sentiment from the team. –  Nov 07 '16 at 13:46
  • I've gotta ask again, is there a technical reason why it is designed this way? It just seems like an unnecessary level of abstraction. –  Nov 07 '16 at 13:49
  • I don't think there's a technical reason. But there is theoretical reasons. Please note, I did not contribute to the design nor implementation of LightInject. Using an IOC container to resolve constructor overloads sounds like your headed towards the Service Locator pattern, which some consider an anti-pattern. The LightInject documentation indicates that the designers of the library consider it as such. http://www.lightinject.net/#function-factories – Jeffrey Patterson Nov 07 '16 at 15:20
  • Since you want to consider the instantiation of your data layer classes to be a service, you could have 1 typed factory per class, or even wrap those into a factory service of factories. In this way, you only have to inject 1 factory service and you can then elegantly instantiate any of your data layer classes in a type safe way. – Jeffrey Patterson Nov 07 '16 at 15:22
  • If you really want to do it without declaring more services, you can use Named Factories. I just tested them and they will work, but you have to "name" each of the factories and then retrieve them by name, which is very fragile in my opinion. http://www.lightinject.net/#named-factories – Jeffrey Patterson Nov 07 '16 at 15:27
  • Given the size and structure of our legacy code base, the service locator is what we have needed most to separate that large class library. Unfortunate that LightInject makes us jump through hoops with a bunch of factories, but it is what it is, I guess. Thanks for your explanation, Jeffrey! –  Nov 07 '16 at 17:46