Zenject Locator
Zenject Locator
Zenject Installer & DI Graph — editor tool that auto-wires services and visualises the dependency graph without Play Mode
What it is. A thin bootstrap layer over Zenject that discovers services/modules and wires them automatically, so teams don’t babysit installers by hand.
Why it matters. Fewer entry-point mistakes, faster onboarding, consistent boot across projects.
How it works.
Discovery. We scan assemblies (TypeCache/Reflection) for IService/IModule or tagged classes and persist the list in a ScriptableObject.
Boot. BootInstaller calls each element’s static initializer (InitializeService/Module), where the service defines its own bindings (factories, sub-containers, FromNew/FromComponent, Id, NonLazy, UnderTransform, etc.).
Safety nets. Logs for missing initializers, duplicate contracts, and ordering issues.
Why it’s useful.
Zero manual plumbing. Add a service → it wires itself.
Standardised boot. One place to look, same pattern in every project.
Full flexibility. Binding logic stays with the service; any Zenject trick still works.
Under the hood. C# · Unity Editor · Zenject · TypeCache/Reflection · ScriptableObject registry.
The services found are placed in the SO object, where you can track which services will be bound.
public abstract class TypeSearcher<Ttype, Tinfo> : ScriptableObject, ITypeSearcher
where Tinfo : SearchableItemInfo
{
public List<Tinfo> itemInfos = new List<Tinfo>();
public void Search()
{
itemInfos.Clear();
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
Type[] types = assembly.GetTypes();
foreach (var type in types)
{
if (!typeof(Ttype).IsAssignableFrom(type) || type.IsInterface || type.IsAbstract)
continue;
itemInfos.Add(CreateNewInfo(type));
}
}
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
public void Reset()
{
itemInfos.Clear();
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
protected abstract Tinfo CreateNewInfo(Type type);
}
What it is. An Editor window that simulates DI and draws who provides/consumes what — no Play Mode.
Why it matters. You see missing/ambiguous bindings early and can explain the architecture with a single diagram.
How it works.
Single source of truth. Each module has Describe(IBinder). In runtime it runs with RealBinder (real Zenject); in Editor — with RecordingBinder (records BindSpec IR).
Consumer discovery. InjectScanner reads [Inject] on fields/properties/methods and, if there’s no [Inject] on ctors, picks the greediest constructor (Zenject-like). Produces ConsumerSpec.
Graph & checks. The window renders Providers → Contracts and Consumers → Contracts, highlights missing/ambiguous providers, and exports Graphviz DOT/PNG.
Why it’s useful.
No Play Mode debugging. Catch holes in DI before entering the scene.
Shareable docs. One exportable diagram for reviews and onboarding.
Confidence. Fewer hidden DI surprises in release builds.
Under the hood. IBinder (Real/Recording) · BindSpec/ConsumerSpec IR · Reflection/TypeCache · Graphviz DOT export.
The graph displays all bound services and their dependencies in a convenient table. This makes it easy to see the dependencies between different classes in a large project.
[DiModule("UI Factory")]
public class UIFactoryModule
{
public static void Describe(IBinder b)
{
b.Bind<IUIFactory, UIFactory>()
.Scope(DiScope.Single)
.NonLazy();
}
}
public interface IBinder {
IBindFluent Bind<TContract, TImpl>() where TImpl : TContract;
IBindFluent BindComponentOnNewGo<TContract, TComp>(string goName = null, bool dontDestroy = true)
where TComp : UnityEngine.MonoBehaviour, TContract;
IBindFactoryFluent BindFactory<TValue, TFactory>();
void Raw(string commentOrPseudoCode);
}