Com.MarcusTS.SmartDI
1.0.7
See the version list below for details.
dotnet add package Com.MarcusTS.SmartDI --version 1.0.7
NuGet\Install-Package Com.MarcusTS.SmartDI -Version 1.0.7
<PackageReference Include="Com.MarcusTS.SmartDI" Version="1.0.7" />
paket add Com.MarcusTS.SmartDI --version 1.0.7
#r "nuget: Com.MarcusTS.SmartDI, 1.0.7"
// Install Com.MarcusTS.SmartDI as a Cake Addin #addin nuget:?package=Com.MarcusTS.SmartDI&version=1.0.7 // Install Com.MarcusTS.SmartDI as a Cake Tool #tool nuget:?package=Com.MarcusTS.SmartDI&version=1.0.7
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: Use this 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.
Quick Start
Create DI Containers Wherever You Need Them; Do Not Centralize
DI Containers both register and provide access to variables. To stay within the C# SOLID Guidance, your app should be as private as possible. So the last thing you need are global containers. Perhaps services can be stored in such a manner. But not your view models. Consider using a factory that generates view models and regulates what gets stored in the container and what is just instantiated out-right:
<pre class="prettyprint lang-javascript" data-start-line="1" data-visibility="visible" data-highlight="" data-caption="">public interface IViewModel <t>{}
public interface IViewModelFactory { IViewModel CreateSimpleViewModel<T>(); IViewModel CreateGlobalViewModel<T>(); IViewModel CreateSharedViewModel<T>(object o); }
public class ViewModelFactory : IViewModelFactory { private SmartDIContainer _viewModelContainer = new SmartDIContainer();
public ViewModelFactory(IService service) { // Perform registrations at the constructor, privately.
// Can pass in services to seed this container or can Resolve this factory from another, more global container
_smartDIContainer.RegisterTypeAsInterface<SomeService>(typeof(IService), StorageRules.AnyAccessLevel, o => service);
// Register other known types using various access levels
_smartDIContainer.RegisterTypeAsInterface<SomeClass>(typeof(ISomeClass));
_smartDIContainer.RegisterTypeAsInterface<GlobalClass>(typeof(IGlobalClass), StorageRules.GlobalSingleton);
_smartDIContainer.RegisterTypeAsInterface<SharedClass>(typeof(ISharedClass), StorageRules.SharedDependencyBetweenInstances);
}
public IViewModel CreateSimpleViewModel<T>() { return _container.Resolve<T>(); }
public IViewModel CreateGlobalViewModel() { return _container.Resolve<T>(StorageRules.GlobalSingleton); }
public IViewModel CreateSharedViewModel(object o) { return _container.Resolve<T>(StorageRules.SharedDependencyBetweenInstances, o); } }</t></pre>
Implement the LifecycleAware Guidance
You can easily make your Xamarin.Forms pages, views, and view models compliant with lifecycle management. See the description and code samples. If you already have your own base classes, you can copy a few snippets of code from the base class examples in the LifecycleAware library into those existing files. If that is too much trouble, you can still use the Smart DI Container's "Any" and "Global" access levels, which cover most cases for smaller apps.
This is an open source project.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Com.MarcusTS.SharedUtils (>= 1.0.5)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Com.MarcusTS.SmartDI:
Package | Downloads |
---|---|
Com.MarcusTS.PlatformIndependentShared
Platform independent utilities for C# development. |
|
Com.MarcusTS.UI.XamForms
Xamarin.Forms abstract classes and utilities to support creating flowable animated apps. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
2.0.5 | 1,020 | 7/30/2022 |
2.0.4 | 434 | 7/29/2022 |
2.0.2 | 1,936 | 10/29/2021 |
2.0.1 | 627 | 10/28/2021 |
1.0.39 | 387 | 10/28/2021 |
1.0.37 | 433 | 8/27/2021 |
1.0.36 | 367 | 8/24/2021 |
1.0.35 | 448 | 8/24/2021 |
1.0.34 | 680 | 8/17/2021 |
1.0.32 | 698 | 8/15/2021 |
1.0.31 | 600 | 8/10/2021 |
1.0.30 | 380 | 8/10/2021 |
1.0.29 | 385 | 8/10/2021 |
1.0.28 | 899 | 7/6/2021 |
1.0.27 | 923 | 6/24/2021 |
1.0.24 | 991 | 4/7/2021 |
1.0.23 | 543 | 4/1/2021 |
1.0.22 | 610 | 1/30/2021 |
1.0.21 | 497 | 10/29/2020 |
1.0.20 | 492 | 10/29/2020 |
1.0.19 | 860 | 10/28/2020 |
1.0.18 | 792 | 10/28/2020 |
1.0.17 | 894 | 4/22/2020 |
1.0.16 | 1,241 | 10/26/2019 |
1.0.15 | 648 | 7/30/2019 |
1.0.14 | 989 | 6/10/2019 |
1.0.13 | 723 | 6/10/2019 |
1.0.12 | 1,048 | 3/12/2019 |
1.0.11 | 742 | 1/7/2019 |
1.0.10 | 810 | 12/28/2018 |
1.0.9 | 830 | 12/27/2018 |
1.0.8 | 738 | 12/26/2018 |
1.0.7 | 728 | 12/25/2018 |
1.0.5 | 719 | 12/24/2018 |
1.0.4 | 742 | 12/24/2018 |