Skip to content

Commit

Permalink
added to the docs the example of using Decorator as initializer for the
Browse files Browse the repository at this point in the history
@bmarinov question
  • Loading branch information
dadhi committed Apr 20, 2021
1 parent 0cbdb2d commit 58c3bea
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 0 deletions.
76 changes: 76 additions & 0 deletions docs/DryIoc.Docs/Decorators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [Decorator as Interceptor with Castle DynamicProxy](#decorator-as-interceptor-with-castle-dynamicproxy)
- [Doing the interesting things with decorators](#doing-the-interesting-things-with-decorators)
- [Reusing the scoped service from the parent scope](#reusing-the-scoped-service-from-the-parent-scope)
- [Using the Decorator directly for the complex initialization](#using-the-decorator-directly-for-the-complex-initialization)
[FactoryMethod]:SelectConstructorOrFactoryMethod.md#factory-method-instead-of-constructor
Expand Down Expand Up @@ -721,4 +722,79 @@ public static T GetFromParentOrCurrent<T>(FactoryDelegate<T> fd, FactoryInfo<T>
} /*md
```
### Using the Decorator directly for the complex initialization
When you have a complex initialization scenario at your hands where `RegisterInitializer` behavior does not fit,
you may remember that `RegisterInitializer` is just a Decorator in disguise. So we may use it directly.
Especially, because the features of DryIoc are greatly composable - you can decorate wrapped services,
inject the additional services (either into the decorator constructor or into the method or into both!), etc., etc.
Let's imagine that we need a transient `DbContext` which should be consumed lazily,
but requires a one-time initialization logic (say a database migration) based on the config provided.
Btw, this is the real case from the Gitter discussion with the user.
```cs md*/
class Using_the_Decorator_directly_for_the_complex_initialization
{
[Test]
public void Example()
{
var c = new Container();

var config = new Config();
c.RegisterInstance(config);

c.Register<DbContext>();
c.Register<InitDbContext>(Reuse.Singleton, serviceKey: "initializer"); // serviceKey is optional, here it basically hides the service unless you know its key
c.Register<DbContext>(setup: Setup.Decorator,
made: Made.Of(_ => ServiceInfo.Of<InitDbContext>(serviceKey: "initializer"),
f => f.Init(Arg.Of<DbContext>())));

var ctx0 = c.Resolve<Lazy<DbContext>>();
var ctx1 = c.Resolve<Lazy<DbContext>>();

var initializer = c.Resolve<InitDbContext>("initializer");

Assert.IsFalse(initializer.Initialized);

Assert.IsTrue(ctx0.Value.Migrated);
Assert.IsTrue(initializer.Initialized);

Assert.AreNotSame(ctx0.Value, ctx1.Value);
}

// DI infrastructure, but it does not need any knowledge of DryIoc - it may perfectly work and be tested without DryIoc.
class InitDbContext
{
Config _config;
public bool Initialized;
public InitDbContext(Config config) { _config = config; }
public DbContext Init(DbContext ctx)
{
if (!Initialized)
{
if (_config.ShouldMigrateDatabase())
ctx.MigrateDatabase();
Initialized = true;
}
return ctx;
}
}

// Application services
class DbContext
{
public bool Migrated;
public void MigrateDatabase() { Migrated = true; }
}

class Config
{
public bool ShouldMigrateDatabase() => true;
}
} /*md
```
md*/
76 changes: 76 additions & 0 deletions docs/DryIoc.Docs/Decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [Decorator as Interceptor with Castle DynamicProxy](#decorator-as-interceptor-with-castle-dynamicproxy)
- [Doing the interesting things with decorators](#doing-the-interesting-things-with-decorators)
- [Reusing the scoped service from the parent scope](#reusing-the-scoped-service-from-the-parent-scope)
- [Using the Decorator directly for the complex initialization](#using-the-decorator-directly-for-the-complex-initialization)


[FactoryMethod]:SelectConstructorOrFactoryMethod.md#factory-method-instead-of-constructor
Expand Down Expand Up @@ -720,3 +721,78 @@ class Reusing_the_scoped_service_from_the_parent_scope
}
```

### Using the Decorator directly for the complex initialization

When you have a complex initialization scenario at your hands where `RegisterInitializer` behavior does not fit,
you may remember that `RegisterInitializer` is just a Decorator in disguise. So we may use it directly.

Especially, because the features of DryIoc are greatly composable - you can decorate wrapped services,
inject the additional services (either into the decorator constructor or into the method or into both!), etc., etc.

Let's imagine that we need a transient `DbContext` which should be consumed lazily,
but requires a one-time initialization logic (say a database migration) based on the config provided.

Btw, this is the real case from the Gitter discussion with the user.

```cs
class Using_the_Decorator_directly_for_the_complex_initialization
{
[Test]
public void Example()
{
var c = new Container();

var config = new Config();
c.RegisterInstance(config);

c.Register<DbContext>();
c.Register<InitDbContext>(Reuse.Singleton, serviceKey: "initializer"); // serviceKey is optional, here it basically hides the service unless you know its key
c.Register<DbContext>(setup: Setup.Decorator,
made: Made.Of(_ => ServiceInfo.Of<InitDbContext>(serviceKey: "initializer"),
f => f.Init(Arg.Of<DbContext>())));

var ctx0 = c.Resolve<Lazy<DbContext>>();
var ctx1 = c.Resolve<Lazy<DbContext>>();

var initializer = c.Resolve<InitDbContext>("initializer");

Assert.IsFalse(initializer.Initialized);

Assert.IsTrue(ctx0.Value.Migrated);
Assert.IsTrue(initializer.Initialized);

Assert.AreNotSame(ctx0.Value, ctx1.Value);
}

// DI infrastructure, but it does not need any knowledge of DryIoc - it may perfectly work and be tested without DryIoc.
class InitDbContext
{
Config _config;
public bool Initialized;
public InitDbContext(Config config) { _config = config; }
public DbContext Init(DbContext ctx)
{
if (!Initialized)
{
if (_config.ShouldMigrateDatabase())
ctx.MigrateDatabase();
Initialized = true;
}
return ctx;
}
}

// Application services
class DbContext
{
public bool Migrated;
public void MigrateDatabase() { Migrated = true; }
}

class Config
{
public bool ShouldMigrateDatabase() => true;
}
}
```

0 comments on commit 58c3bea

Please sign in to comment.