Skip to content

Commit 627382b

Browse files
committed
Host generation complete. Orders of magnitue performance improvement. Pushing to nuget.
1 parent 7dad7a4 commit 627382b

File tree

4 files changed

+77
-354
lines changed

4 files changed

+77
-354
lines changed

EnumSourceGenerator/HostGenerator.cs

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -55,34 +55,38 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
5555
{
5656
var impl = item.symbol;
5757
var attr = item.attr;
58+
5859
var lifetime = attr.AttributeClass?.Name switch
5960
{
6061
"SingletonAttribute" => "ServiceLifetime.Singleton",
6162
"ScopedAttribute" => "ServiceLifetime.Scoped",
6263
"TransientAttribute" => "ServiceLifetime.Transient",
6364
_ => null
6465
};
66+
6567
if (lifetime == null)
66-
{
6768
continue;
68-
}
6969

70-
// interface inference
70+
// pick interface if explicitly provided, otherwise infer
7171
var iface = attr.ConstructorArguments.Length > 0
7272
? attr.ConstructorArguments[0].Value as INamedTypeSymbol
73-
: impl.AllInterfaces.FirstOrDefault(i =>
74-
i.Name == "I" + impl.Name)
73+
: impl.AllInterfaces.FirstOrDefault(i => i.Name == "I" + impl.Name)
7574
?? impl.AllInterfaces.FirstOrDefault();
7675

77-
// register concrete type
78-
sb.AppendLine($" host._serviceDescriptors.Add(new ServiceDescriptor(typeof({impl.ToDisplayString()}), {lifetime}));");
76+
// register concrete type to itself
77+
sb.AppendLine(
78+
$" host._serviceDescriptors.Add(new ServiceDescriptor(typeof({impl.ToDisplayString()}), typeof({impl.ToDisplayString()}), {lifetime}));");
7979

80+
// register interface -> concrete mapping (if any)
8081
if (iface != null)
81-
sb.AppendLine($" host._serviceDescriptors.Add(new ServiceDescriptor(typeof({iface.ToDisplayString()}), {lifetime}));");
82+
{
83+
sb.AppendLine(
84+
$" host._serviceDescriptors.Add(new ServiceDescriptor(typeof({iface.ToDisplayString()}), typeof({impl.ToDisplayString()}), {lifetime}));");
85+
}
8286
}
8387

8488
// call InitializeUsingAttribute (which builds factories)
85-
sb.AppendLine(" host.SetRoot(new Scope(host));");
89+
sb.AppendLine(" host._rootScope = new Scope(host.ResolveInternal);");
8690
sb.AppendLine(" host.InitializeUsingAttribute();");
8791
sb.AppendLine(" return host;");
8892
sb.AppendLine(" }");
@@ -98,11 +102,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
98102

99103

100104
const string _stockMethods = """
101-
private void SetRoot(Scope rootScope)
102-
{
103-
_rootScope = rootScope;
104-
}
105-
106105
private void InitializeUsingAttribute()
107106
{
108107
if (_initialized)
@@ -111,6 +110,14 @@ private void InitializeUsingAttribute()
111110
BuildFactories();
112111
_initialized = true;
113112
}
113+
114+
private object ResolveInternal(Type type, Scope scope)
115+
{
116+
if (_factories.TryGetValue(type, out var factory))
117+
return factory(scope);
118+
119+
throw new InvalidOperationException($"Service of type {type.Name} is not registered.");
120+
}
114121
115122
private void BuildFactories()
116123
{
@@ -211,37 +218,41 @@ private void CreateFactory(ServiceDescriptor descriptor)
211218
212219
private void CreateSingletonFactory(ServiceDescriptor descriptor)
213220
{
214-
var serviceType = descriptor.ServiceType;
221+
var implType = descriptor.ImplementationType ?? descriptor.ServiceType;
215222
216-
_factories[serviceType] = scope =>
223+
_factories[implType] = scope =>
217224
{
218-
if (!_singletonInstances.TryGetValue(serviceType, out var instance))
225+
if (!_singletonInstances.TryGetValue(implType, out var instance))
219226
{
220-
instance = CreateInstance(serviceType, scope);
221-
_singletonInstances[serviceType] = instance;
227+
instance = CreateInstance(implType, scope);
228+
_singletonInstances[implType] = instance;
222229
}
223230
return instance;
224231
};
225232
}
226233
227234
private void CreateScopedFactory(ServiceDescriptor descriptor)
228235
{
229-
var serviceType = descriptor.ServiceType;
236+
var implType = descriptor.ImplementationType ?? descriptor.ServiceType;
230237
231-
_factories[serviceType] = scope =>
232-
scope.GetOrCreateScopedInstance(serviceType, () => CreateInstance(serviceType, scope));
238+
_factories[implType] = scope =>
239+
scope.GetOrCreateScopedInstance(implType, () => CreateInstance(implType, scope));
233240
}
234241
235242
private void CreateTransientFactory(ServiceDescriptor descriptor)
236243
{
237-
var serviceType = descriptor.ServiceType;
238-
_factories[serviceType] = scope => CreateInstance(serviceType, scope);
244+
var implType = descriptor.ImplementationType ?? descriptor.ServiceType;
245+
246+
_factories[implType] = scope => CreateInstance(implType, scope);
239247
}
240248
241249
private object CreateInstance(Type type, Scope scope)
242250
{
251+
if (type.IsInterface || type.IsAbstract)
252+
throw new InvalidOperationException($"Cannot create an instance of {type.Name} directly. It must be mapped to a concrete type.");
253+
243254
var constructor = type.GetConstructors().FirstOrDefault()
244-
?? throw new InvalidOperationException($"No public constructor found for type {type.Name}");
255+
?? throw new InvalidOperationException($"No public constructor found for type {type.Name}");
245256
246257
var parameters = constructor.GetParameters();
247258
var arguments = new object[parameters.Length];
@@ -295,7 +306,26 @@ private object CreateInstance(Type type, Scope scope)
295306
/// <returns>A new <see cref="Scope"/> instance.</returns>
296307
/// <exception cref="InvalidOperationException">Thrown if the host has not been initialized.</exception>
297308
public Scope CreateScope()
298-
=> !_initialized ? throw new InvalidOperationException("Host must be initialized before creating a scope.") : new Scope((type, s) => _factories[type](s));
309+
{
310+
if (!_initialized)
311+
throw new InvalidOperationException("Host must be initialized before creating a scope.");
312+
313+
return new Scope((type, s) =>
314+
{
315+
// Direct factory lookup
316+
if (_factories.TryGetValue(type, out var factory))
317+
return factory(s);
318+
319+
// If type is an interface or abstract, resolve via descriptor → ImplementationType
320+
var descriptor = _serviceDescriptors.FirstOrDefault(sd => sd.ServiceType == type);
321+
if (descriptor?.ImplementationType is not null && _factories.TryGetValue(descriptor.ImplementationType, out var implFactory))
322+
{
323+
return implFactory(s);
324+
}
325+
326+
throw new InvalidOperationException($"Cannot resolve type {type.Name} from scope.");
327+
});
328+
}
299329
300330
/// <summary>
301331
/// Retrieves a singleton service of the specified type.
@@ -322,19 +352,26 @@ internal T GetService<T>(Scope scope) where T : class
322352
{
323353
var type = typeof(T);
324354
355+
// Fast path: direct factory lookup
325356
if (_factories.TryGetValue(type, out var factory))
326357
return (T)factory(scope);
327358
328-
// If requesting an interface, check its registration
359+
// Lookup by descriptor
329360
var descriptor = _serviceDescriptors.FirstOrDefault(sd => sd.ServiceType == type);
330-
if (descriptor is not null)
361+
if (descriptor is not null && descriptor.ImplementationType is not null)
331362
{
332-
if (_factories.TryGetValue(descriptor.ServiceType ?? descriptor.ServiceType, out var implFactory))
363+
if (_factories.TryGetValue(descriptor.ImplementationType, out var implFactory))
333364
return (T)implFactory(scope);
334365
}
335366
336367
throw new InvalidOperationException($"Service of type {type.Name} is not registered.");
337368
}
369+
370+
public void Dispose()
371+
{
372+
_rootScope.Dispose();
373+
}
374+
338375
""";
339376

340377
}

0 commit comments

Comments
 (0)