do icon indicating copy to clipboard operation
do copied to clipboard

`InvokeAs` problems

Open d-enk opened this issue 9 months ago • 5 comments

  1. Can't invoke interface (can't cast nil)
package main

import "github.com/samber/do/v2"

func main() {
	type I interface{}

	i := do.New()
	do.ProvideValue(i, I("some"))
	do.MustInvokeAs[I](i)
}

// panic: DI: could not find service `*main.I`, available services: `*main.I`
  1. Random invoked, other not checked (https://github.com/samber/do/pull/45#issuecomment-2079226572)
package main

import (
	"fmt"

	"github.com/samber/do/v2"
)

type I interface{ Name() string }

type i struct{}

func (i) Name() string { return "EXPECTED" }

type other struct{}

func (other) Other()       {}
func (other) Name() string { return "OTHER" }

// type
func main() {
	scope := do.New()

	do.ProvideValue(scope, other{})
	do.ProvideValue(scope, i{})

	for range 10 {
		fmt.Println(do.MustInvokeAs[I](scope).Name())
	}
	// output: something like
	// OTHER
	// OTHER
	// OTHER
	// EXPECTED
	// OTHER
	// OTHER
	// OTHER
	// EXPECTED
	// OTHER
	// OTHER
}

I create a function that would temporarily delete InvokedAs result by OverrideNamedValue and tried to check the next not exist.

But I came across both 1 problem and https://github.com/samber/go-type-to-string/issues/2

d-enk avatar May 06 '24 12:05 d-enk

1- I think you should just call do.Invoke[I] instead of do.InvokeAs[i], since your not performing interface matching here. I will improve the error message.

2- I'm not sure to understand the issue here. Your example output seems valid to me.

samber avatar May 07 '24 04:05 samber

While i review comments on #45, I'm linking your issue to https://github.com/samber/do/pull/45#issuecomment-1993706391

To describe the feedback from @mbark:

  • We provide *MyServiceImplem
  • Then invoke IMyService
  • We implement a *MyServiceMock via an override for test purposes.

Problem: do.InvokeAs will pick a random implementation of IMyService

samber avatar May 07 '24 05:05 samber

Possible solutions:

  • invoke the latest injected service -> I don't like it, since the service injection order might be non-determinist
  • use do.OverrideNamedValue(i, do.NameOf[IMyService](), NewMyServiceMock) -> not developer friendly
  • use explicit aliases: do.AsNamed() -> might trigger error on override

@d-enk suggested reporting an error when multiple services match the interface.

Also, I would point out that since services injected in IoC containers are opaque, invoking an interface instead of a struct might lead to loading the wrong service if method signatures overlap. In that situation, an explicit alias is safer: do.AsNamed()

samber avatar May 07 '24 07:05 samber

Another option, maybe you just need a separate method that returns a list of all match? And in InvokeAs allow only one

looks like related https://github.com/samber/do/issues/33

d-enk avatar May 07 '24 08:05 d-enk

to fix 1 problem https://github.com/samber/do/issues/81

d-enk avatar May 13 '24 15:05 d-enk