Com.MarcusTS.SmartDI 1.0.14

A container that creates and then (optionally) stores variables to provide caching and centralized access.

These sorts of containers are sometimes misdescribed as IOC ("Inversion of Control") Containers. Since they do not provide any control over program flow, the accurate term is DI ("Dependency Injection") Container.

The SmartDI Container is unique in that it:

* Does not store instantiated objects unnecessarily.

* Supports object life-cycle management. When an object dies, it is removed from the container.  This requires you to implement an interface.

* Indexes a shared container class instance to the objects that share it so that when those objects die, the container class instance is also removed.  This does not requires any other interfaces or management steps.

Install-Package Com.MarcusTS.SmartDI -Version 1.0.14
dotnet add package Com.MarcusTS.SmartDI --version 1.0.14
<PackageReference Include="Com.MarcusTS.SmartDI" Version="1.0.14" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Com.MarcusTS.SmartDI --version 1.0.14
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

Finally, a DI Container Without The Fake "IOC" Swagger

It's just dependency injection - that's all, folks

Modern programmers require some means of creating new classes out of other classes that are passed as parameters, and to do so in an elegant, seamless manner. Those new instances often require caching, especially if they are services or view models. The got-to solution for this challenge has been the mis-named and grossly mis-described "IOC Container". In another article, I explained why that is a bad idea. If we can't even name something accurately, it is time to reconsider it entirely. So I have done that here with the tool that all of us actually need: a DI ("Dependency Injection") Container.

What's So Smart About It?

1. It provides true lifecycle management

IOC containers always store an instance when you create it. That is extremely wasteful. It is also unsafe. The Smart DI Container provides only three types of registrations:

  • Any Access Level / Do Not Store: Use these to create instances at any time and without caching. The local variable you retrieve on Resolve() is the only stored reference.

  • Shared Between Instances: When you Resolve() using this access level, you must to pass in a "parent" object that gets indexed to that new class instance. You can also link the same instance to any number of other consumers/parents by calling Resolve() again. Once all of the parents die, the cached instance is also removed. Note: This requires you to raise a Xamarin.Forms event to notify the container about the death of a shared instance parent. We do this for you if you also use our Lifecycle Aware Library.

  • Global Singleton: The container creates and caches a permanent instance of any type registered with this access level. The cached reference dies when the container itself falls out of scope.

In spite of the ginormous size of other containers on the market, none of them can pass this test. The container must provide a physical mechanism to make this functionality possible. We have one!

2. It is not global or static

You can declare an instance of the Smart DI Container wherever you please. This supports "nested" scenarios, where containers live within narrowly defined class inheritance trees. Remember: all "global" variables stored/cached will only live as long as the container does.

3. It is well-behaved

The Smart DI Container protects against recursive calls, or any other violation of the rules-based registrations you make. For instance, if you register two competing interfaces for the same base type:

<pre class="prettyprint lang-javascript" data-start-line="1" data-visibility="visible" data-highlight="" data-caption="">_container = new SmartDIContainer();
_container.RegisterTypeAsInterface<FirstSimpleClass>(typeof(IAmSimple));
_container.RegisterTypeAsInterface<SecondSimpleClass>(typeof(IAmSimple));
</pre>

... and then resolve IAmSimple, you have created a conflict. The container cannot know which one to return. You can set a Boolean property to throw an error in this case. Or you can provide a conflict resolver:

<pre class="prettyprint lang-javascript" data-start-line="1" data-visibility="visible" data-highlight="" data-caption="">var simple = _container.Resolve<IAmSimple>(StorageRules.AnyAccessLevel, null, ForbidSpecificClass<FirstSimpleClass>);

private static IConflictResolution ForbidSpecificClass<T>(IDictionary<Type, ITimeStampedCreatorAndStorageRules> registrations)
{
// Find any registration where the key (the main class that was registered and that is being constructed) is not the forbidden one
var legalValues = registrations.Where(r => r.Key != typeof(T)).ToArray();

if (legalValues.IsEmpty())
{
return null;
}

return new ConflictResolution
{
MasterType = legalValues.First().Key,
TypeToCastWithStorageRule = legalValues.First().Value.CreatorsAndStorageRules.First()
};
}
</pre>

4. It is tiny

Smart DI Container occupies almost no space at all, and rarely touches memory, since it does not store anything unnecessarily.

5. It's basic, honest open source C#, and easy to read.

We actually added comments! (And we were not struck by lightning)

6. It is tested and proven

See the unit tests.

See the Source

It's all available for free on GitHub.

To bring the Smart DI Container into your own app, include these NuGet packages:

Com.MarcusTS.SmartDI Com.MarcusTS.SmartDI.Lifecycle Com.MarcusTS.LifecycleAware

Finally, a DI Container Without The Fake "IOC" Swagger

It's just dependency injection - that's all, folks

Modern programmers require some means of creating new classes out of other classes that are passed as parameters, and to do so in an elegant, seamless manner. Those new instances often require caching, especially if they are services or view models. The got-to solution for this challenge has been the mis-named and grossly mis-described "IOC Container". In another article, I explained why that is a bad idea. If we can't even name something accurately, it is time to reconsider it entirely. So I have done that here with the tool that all of us actually need: a DI ("Dependency Injection") Container.

What's So Smart About It?

1. It provides true lifecycle management

IOC containers always store an instance when you create it. That is extremely wasteful. It is also unsafe. The Smart DI Container provides only three types of registrations:

  • Any Access Level / Do Not Store: Use these to create instances at any time and without caching. The local variable you retrieve on Resolve() is the only stored reference.

  • Shared Between Instances: When you Resolve() using this access level, you must to pass in a "parent" object that gets indexed to that new class instance. You can also link the same instance to any number of other consumers/parents by calling Resolve() again. Once all of the parents die, the cached instance is also removed. Note: This requires you to raise a Xamarin.Forms event to notify the container about the death of a shared instance parent. We do this for you if you also use our Lifecycle Aware Library.

  • Global Singleton: The container creates and caches a permanent instance of any type registered with this access level. The cached reference dies when the container itself falls out of scope.

In spite of the ginormous size of other containers on the market, none of them can pass this test. The container must provide a physical mechanism to make this functionality possible. We have one!

2. It is not global or static

You can declare an instance of the Smart DI Container wherever you please. This supports "nested" scenarios, where containers live within narrowly defined class inheritance trees. Remember: all "global" variables stored/cached will only live as long as the container does.

3. It is well-behaved

The Smart DI Container protects against recursive calls, or any other violation of the rules-based registrations you make. For instance, if you register two competing interfaces for the same base type:

<pre class="prettyprint lang-javascript" data-start-line="1" data-visibility="visible" data-highlight="" data-caption="">_container = new SmartDIContainer();
_container.RegisterTypeAsInterface<FirstSimpleClass>(typeof(IAmSimple));
_container.RegisterTypeAsInterface<SecondSimpleClass>(typeof(IAmSimple));
</pre>

... and then resolve IAmSimple, you have created a conflict. The container cannot know which one to return. You can set a Boolean property to throw an error in this case. Or you can provide a conflict resolver:

<pre class="prettyprint lang-javascript" data-start-line="1" data-visibility="visible" data-highlight="" data-caption="">var simple = _container.Resolve<IAmSimple>(StorageRules.AnyAccessLevel, null, ForbidSpecificClass<FirstSimpleClass>);

private static IConflictResolution ForbidSpecificClass<T>(IDictionary<Type, ITimeStampedCreatorAndStorageRules> registrations)
{
// Find any registration where the key (the main class that was registered and that is being constructed) is not the forbidden one
var legalValues = registrations.Where(r => r.Key != typeof(T)).ToArray();

if (legalValues.IsEmpty())
{
return null;
}

return new ConflictResolution
{
MasterType = legalValues.First().Key,
TypeToCastWithStorageRule = legalValues.First().Value.CreatorsAndStorageRules.First()
};
}
</pre>

4. It is tiny

Smart DI Container occupies almost no space at all, and rarely touches memory, since it does not store anything unnecessarily.

5. It's basic, honest open source C#, and easy to read.

We actually added comments! (And we were not struck by lightning)

6. It is tested and proven

See the unit tests.

See the Source

It's all available for free on GitHub.

To bring the Smart DI Container into your own app, include these NuGet packages:

Com.MarcusTS.SmartDI Com.MarcusTS.SmartDI.Lifecycle Com.MarcusTS.LifecycleAware

Release Notes

Bug fixes.

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
1.0.14 81 6/10/2019
1.0.13 69 6/10/2019
1.0.12 88 3/12/2019
1.0.11 119 1/7/2019
1.0.10 127 12/28/2018
1.0.9 122 12/27/2018
1.0.8 131 12/26/2018
1.0.7 115 12/25/2018
1.0.5 105 12/24/2018
1.0.4 119 12/24/2018