mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-23 21:13:35 +00:00
docs(di): clarify 'toInstance' binding limitations in builder
- Add explicit note for users about the impossibility to use scope.resolve<T>() for just-to-be-registered types inside Module.builder when registering chained dependencies via toInstance. - Show correct and incorrect usage patterns, functional and anti-pattern Dart examples in RU and EN full tutorials. - Add the warning to the main README after core concept bindings block, improving discoverability for users starting with the library. - Motivation: Prevent common misuse and hard-to-debug runtime errors for users who construct chains using toInstance/resolve inside the builder.
This commit is contained in:
@@ -102,31 +102,73 @@ A **Binding** acts as a configuration for how to create or provide a particular
|
|||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// Provide a direct instance
|
void builder(Scope scope) {
|
||||||
Binding<String>().toInstance("Hello world");
|
// Provide a direct instance
|
||||||
|
bind<String>().toInstance("Hello world");
|
||||||
|
|
||||||
// Provide an async direct instance
|
// Provide an async direct instance
|
||||||
Binding<String>().toInstanceAsync(Future.value("Hello world"));
|
bind<String>().toInstanceAsync(Future.value("Hello world"));
|
||||||
|
|
||||||
// Provide a lazy sync instance using a factory
|
// Provide a lazy sync instance using a factory
|
||||||
Binding<String>().toProvide(() => "Hello world");
|
bind<String>().toProvide(() => "Hello world");
|
||||||
|
|
||||||
// Provide a lazy async instance using a factory
|
// Provide a lazy async instance using a factory
|
||||||
Binding<String>().toProvideAsync(() async => "Hello async world");
|
bind<String>().toProvideAsync(() async => "Hello async world");
|
||||||
|
|
||||||
// Provide an instance with dynamic parameters (sync)
|
// Provide an instance with dynamic parameters (sync)
|
||||||
Binding<String>().toProvideWithParams((params) => "Hello $params");
|
bind<String>().toProvideWithParams((params) => "Hello $params");
|
||||||
|
|
||||||
// Provide an instance with dynamic parameters (async)
|
// Provide an instance with dynamic parameters (async)
|
||||||
Binding<String>().toProvideAsyncWithParams((params) async => "Hello $params");
|
bind<String>().toProvideAsyncWithParams((params) async => "Hello $params");
|
||||||
|
|
||||||
// Named instance for retrieval by name
|
// Named instance for retrieval by name
|
||||||
Binding<String>().toProvide(() => "Hello world").withName("my_string");
|
bind<String>().toProvide(() => "Hello world").withName("my_string");
|
||||||
|
|
||||||
// Mark as singleton (only one instance within the scope)
|
// Mark as singleton (only one instance within the scope)
|
||||||
Binding<String>().toProvide(() => "Hello world").singleton();
|
bind<String>().toProvide(() => "Hello world").singleton();
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ⚠️ **Important note about using `toInstance` in Module `builder`:**
|
||||||
|
>
|
||||||
|
> If you register a chain of dependencies via `toInstance` inside a Module's `builder`, **do not** call `scope.resolve<T>()` for types that are also being registered in the same builder — at the moment they are registered.
|
||||||
|
>
|
||||||
|
> CherryPick initializes all bindings in the builder sequentially. Dependencies registered earlier are not yet available to `resolve` within the same builder execution. Trying to resolve just-registered types will result in an error (`Can't resolve dependency ...`).
|
||||||
|
>
|
||||||
|
> **How to do it right:**
|
||||||
|
> Manually construct the full dependency chain before calling `toInstance`:
|
||||||
|
>
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> final a = A();
|
||||||
|
> final b = B(a);
|
||||||
|
> final c = C(b);
|
||||||
|
> bind<A>().toInstance(a);
|
||||||
|
> bind<B>().toInstance(b);
|
||||||
|
> bind<C>().toInstance(c);
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Wrong:**
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> bind<A>().toInstance(A());
|
||||||
|
> // Error! At this point, A is not registered yet.
|
||||||
|
> bind<B>().toInstance(B(scope.resolve<A>()));
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Wrong:**
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> bind<A>().toProvide(() => A());
|
||||||
|
> // Error! At this point, A is not registered yet.
|
||||||
|
> bind<B>().toInstance(B(scope.resolve<A>()));
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Note:** This limitation applies **only** to `toInstance`. With `toProvide`/`toProvideAsync` and similar providers, you can safely use `scope.resolve<T>()` inside the builder.
|
||||||
|
|
||||||
### Module
|
### Module
|
||||||
|
|
||||||
A **Module** is a logical collection point for bindings, designed for grouping and initializing related dependencies. Implement the `builder` method to define how dependencies should be bound within the scope.
|
A **Module** is a logical collection point for bindings, designed for grouping and initializing related dependencies. Implement the `builder` method to define how dependencies should be bound within the scope.
|
||||||
|
|||||||
@@ -44,6 +44,46 @@ final setupFuture = loadEnvironment();
|
|||||||
bind<Environment>().toInstanceAsync(setupFuture);
|
bind<Environment>().toInstanceAsync(setupFuture);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ⚠️ **Important note about using toInstance in Module**
|
||||||
|
>
|
||||||
|
> If you register a chain of dependencies via `toInstance` inside the `builder` method of your `Module`, you must NOT call `scope.resolve<T>()` for a type that you have just bound—at this moment.
|
||||||
|
>
|
||||||
|
> CherryPick initializes all bindings inside `builder` sequentially: at the time of a new binding, not all other dependencies are registered yet in the DI container. If you try to use `scope.resolve<T>()` for an object you have just added in the same `builder`, it will result in an error (`Can't resolve dependency ...`).
|
||||||
|
>
|
||||||
|
> **Correct way:**
|
||||||
|
> Manually construct the entire object chain before registering:
|
||||||
|
>
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> final a = A();
|
||||||
|
> final b = B(a);
|
||||||
|
> final c = C(b);
|
||||||
|
> bind<A>().toInstance(a);
|
||||||
|
> bind<B>().toInstance(b);
|
||||||
|
> bind<C>().toInstance(c);
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Incorrect:**
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> bind<A>().toInstance(A());
|
||||||
|
> // Error! At this point, A is not registered yet.
|
||||||
|
> bind<B>().toInstance(B(scope.resolve<A>()));
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Incorrect:**
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> bind<A>().toProvide(() => A());
|
||||||
|
> // Error! At this point, A is not registered yet.
|
||||||
|
> bind<B>().toInstance(B(scope.resolve<A>()));
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Note:** This limitation applies only to `toInstance`. For providers (`toProvide`/`toProvideAsync`) and other strategies, you can freely use `scope.resolve<T>()` inside `builder`.
|
||||||
|
|
||||||
- **toProvide** — regular sync factory
|
- **toProvide** — regular sync factory
|
||||||
- **toProvideAsync** — async factory (if you need to await a Future)
|
- **toProvideAsync** — async factory (if you need to await a Future)
|
||||||
- **toProvideWithParams / toProvideAsyncWithParams** — factories with runtime parameters
|
- **toProvideWithParams / toProvideAsyncWithParams** — factories with runtime parameters
|
||||||
|
|||||||
@@ -44,6 +44,46 @@ final setupFuture = loadEnvironment();
|
|||||||
bind<Environment>().toInstanceAsync(setupFuture);
|
bind<Environment>().toInstanceAsync(setupFuture);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ⚠️ **Важное примечание по использованию toInstance в Module**
|
||||||
|
>
|
||||||
|
> Если вы регистрируете цепочку зависимостей через `toInstance` внутри метода `builder` вашего `Module`, нельзя в это же время вызывать `scope.resolve<T>()` для только что объявленного типа.
|
||||||
|
>
|
||||||
|
> CherryPick инициализирует биндинги последовательно внутри builder: в этот момент ещё не все зависимости зарегистрированы в DI-контейнере. Попытка воспользоваться `scope.resolve<T>()` для только что добавленного объекта приведёт к ошибке (`Can't resolve dependency ...`).
|
||||||
|
>
|
||||||
|
> **Как правильно:**
|
||||||
|
> Складывайте всю цепочку вручную до регистрации:
|
||||||
|
>
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> final a = A();
|
||||||
|
> final b = B(a);
|
||||||
|
> final c = C(b);
|
||||||
|
> bind<A>().toInstance(a);
|
||||||
|
> bind<B>().toInstance(b);
|
||||||
|
> bind<C>().toInstance(c);
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Неправильно:**
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> bind<A>().toInstance(A());
|
||||||
|
> // Ошибка! В этот момент A ещё не зарегистрирован.
|
||||||
|
> bind<B>().toInstance(B(scope.resolve<A>()));
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Неправильно:**
|
||||||
|
> ```dart
|
||||||
|
> void builder(Scope scope) {
|
||||||
|
> bind<A>().toProvide(() => A());
|
||||||
|
> // Ошибка! В этот момент A ещё не зарегистрирован.
|
||||||
|
> bind<B>().toInstance(B(scope.resolve<A>()));
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> **Примечание:** Это ограничение касается только `toInstance`. Для провайдеров (`toProvide`/`toProvideAsync`) и других стратегий вы можете использовать `scope.resolve<T>()` внутри builder без ограничений.
|
||||||
|
|
||||||
|
|
||||||
- **toProvide** — обычная синхронная фабрика.
|
- **toProvide** — обычная синхронная фабрика.
|
||||||
- **toProvideAsync** — асинхронная фабрика (например, если нужно дожидаться Future).
|
- **toProvideAsync** — асинхронная фабрика (например, если нужно дожидаться Future).
|
||||||
|
|||||||
Reference in New Issue
Block a user