0

I have an existing winforms application I'm trying to rework to now use Unity as a container instead of manually having to create objects all over the place. At the moment things aren't too bad but we will expand the program and without Unity I can see things getting complicated. I'm struggling to understand how to create some objects which depend on runtime information. Take the following code as an example of how things currently work (the program allows you to view multiple customers at once, each one on a new tab)…..

Public Class CustomerSearchPresenter
    Implements ICustomerSearchPresenter

    Private Repo As ICustomerRepo
    Private View As ICustomerSearchView

    Public Sub New(_view As ICustomerSearchView, _customerRepo As ICustomerRepo)
        View = _view
        Repo = _customerRepo
    End Sub

    Public Sub SearchForCustomer(_customerNumber As String) Implements ICustomerSearchPresenter.SearchForCustomer
        'seach the database to see if customer exists and if so create a new customer view...
        Dim foundCustomer As ICustomer = Repo.find(_customerNumber)
        If Not IsNothing(foundCustomer) Then
            '* Manual creation of all objects happens here - how do I change this in unity to create a new 
            Dim cusView As New customerView
            Dim custPresenter As New CustomerPresenter(cusView, foundCustomer)
            'custPresenter creates a new tab on the main form to display the details for that customer
        Else
            View.feedback("No customer found for " & _customerNumber)
        End If

    End Sub

End Class

Public Class CustomerPresenter
    Implements ICustomerPresenter

    Private View As ICustomerView
    Private Customer As ICustomer

    Public Sub New(_View As ICustomerView, _customer As ICustomer)
        View = _View
        Customer = _customer
    End Sub

    Public Sub RefreshData() Implements ICustomerPresenter.RefreshData
        'do some stuff here ....
    End Sub

End Class

The CustomerSearchPresenter is responsible for creating a new customer section each time the user searches a valid customer number. This is simple enough as the customer object is returned by the repository and we can manually inject it when creating the related view/presenter (despite this being based on MVP I'm guessing same theory applies across MVC etc).

My first attempt to rework this with Unity was the following (using Func(of)) but it doesn't work - the invoke call doesn't create me a new object and returns the same one. How should I create a new customer specific presenter every time the user searches for one?

Public Class CustomerSearchPresenter
    Implements ICustomerSearchPresenter

    Private Repo As ICustomerRepo
    Private View As ICustomerSearchView
    Private Builder As Func(Of ICustomerPresenter)

    Public Sub New(_view As ICustomerSearchView, _customerRepo As ICustomerRepo, _customerBuilder As Func(Of ICustomerPresenter))
        View = _view
        Repo = _customerRepo
    End Sub

    Public Sub SearchForCustomer(_customerNumber As String) Implements ICustomerSearchPresenter.SearchForCustomer
        'seach the database to see if customer exists and if so create a new customer view...
        Dim foundCustomer As ICustomer = Repo.find(_customerNumber)
        If Not IsNothing(foundCustomer) Then
            '* Assumed I could call invoke and then register the customer afterwards, but this just invokes the same object
            Dim custPresenter As ICustomerPresenter = Builder.Invoke
            custPresenter.registerCustomer(_foundCustomer)
        Else
            View.feedback("No customer found for " & _customerNumber)
        End If

    End Sub

End Class

Edit: The func(of) method does work, I'd registered the type with ContainerControlledLifetimeManager meaning it always returned the same Presenter. So now that works is this the best/correct way to solve it?

Edit 2: Thanks pointing me at the other questions but I'm struggling to apply them to my code. I still need my objects to be resolved from the container (without passing the container around) which means how does the following example help me resolve that...

Public Class CustomerPresenterFactory
    Implements ICustomerPresenterFactory

    Function Create(_customer As ICustomer) As ICustomerPresenter Implements ICustomerPresenterFactory.Create
        Return ????
    End Function

End Class

I wouldn't have access to any objects the presenter needs in addition to the customer such as the View.

  • Possible duplicate of [Is there a pattern for initializing objects created via a DI container](http://stackoverflow.com/questions/1943576/is-there-a-pattern-for-initializing-objects-created-via-a-di-container) – NightOwl888 Jul 01 '16 at 09:40
  • Related: [Dependency-injection anti-pattern: Injecting runtime data into components](https://cuttingedge.it/blogs/steven/pivot/entry.php?id=99) – NightOwl888 Jul 01 '16 at 09:41

1 Answers1

0

I've gone with the following solution which I think solves the problem in a round about way. I can now create a customer presenter and know that it will be given a customer object to use.

Public Class CustomerPresenterFactory
        Implements ICustomerPresenterFactory

        Private creator As Func(Of ICustomerPresenter)

        Public Sub New(_creator As Func(Of ICustomerPresenter))
            creator = _creator
        End Sub

        Function Create(_customer As ICustomer) As ICustomerPresenter Implements ICustomerPresenterFactory.Create
            Dim obj As ICustomerPresenter = creator.Invoke
            obj.Customer = _customer
            Return obj
        End Function

    End Class

The searchPresenter in the original post now takes this as a dependency and can create a new CustomerPresenter after each succesful search result.

I'm slowly starting to see some of my mistakes/learns with how to do dependency injection. I don't know if this is the correct solution but with my current understanding this feels a much nicer solution that the original code.