9 Commits

Author SHA1 Message Date
Samuele Lorefice
b9fbb4b851 Version bump with new fixes
All checks were successful
Nuget Pkg Build / build (push) Successful in 1m0s
2025-09-24 04:08:53 +02:00
Samuele Lorefice
3df2f50765 Fixes #2 by providing a proper implementation of GetServices that actually returns instances of those services instead of just a list of types. 2025-09-24 04:07:24 +02:00
Samuele Lorefice
d20788de33 Rename method GetServices to GetServiceTypes for clarity (relevant for #2) 2025-09-24 03:55:01 +02:00
Samuele Lorefice
b8f2ddad5a Fixes #1 by searching also inside the implementation types list 2025-09-24 03:51:39 +02:00
86513ec6c6 Update .gitea/workflows/nuget-pkg-build.yml 2025-09-22 03:38:32 +02:00
Samuele Lorefice
debedc837e Added shorthands methods for no-interface service types
All checks were successful
Nuget Pkg Build / build (push) Successful in 42s
2025-09-22 03:15:51 +02:00
Samuele Lorefice
d0ccdbfa0f Updated workflow, widened support, now including Net 8, 9 and 10
Some checks failed
Nuget Pkg Build / build (push) Failing after 58s
2025-09-22 02:26:07 +02:00
Samuele Lorefice
16d0142967 enables CI flag 2025-09-21 23:04:36 +02:00
Samuele Lorefice
b06e886cf2 CI/CD Fixes 2025-09-21 21:54:22 +02:00
5 changed files with 74 additions and 21 deletions

View File

@@ -11,16 +11,21 @@ jobs:
timeout-minutes: 5 timeout-minutes: 5
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup .NET SDK - name: Setup .NET SDK
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v5
with: with:
dotnet-version: 9.x dotnet-version: |
10.x
9.x
8.x
- name: Build - name: Build
run: dotnet build Syrette -c Release run: dotnet build Syrette -c Release
#- name: Test #- name: Test
# run: dotnet test -c Release --no-build # run: dotnet test -c Release --no-build
- name: Pack nugets - name: Pack nugets
run: dotnet pack Syrette -c Release --no-build --output . --include-symbols --include-source run: dotnet pack Syrette -c Release --no-build --output . --include-symbols --include-source -p:SymbolPackageFormat=snupkg
- name: Push to NuGet - name: Push to NuGet
run: dotnet nuget push "*.nupkg" --api-key ${{secrets.NUGETAPIKEY}} --source https://api.nuget.org/v3/index.json run: dotnet nuget push "*.nupkg" --api-key ${{secrets.NUGETAPIKEY}} --skip-duplicate --source https://api.nuget.org/v3/index.json
- name: Push to Gitea
run: dotnet nuget push "*.nupkg" --api-key ${{secrets.NUGETGITEA}} --skip-duplicate --source https://git.r3d.codes/api/packages/REDCODE/nuget/index.json

View File

@@ -12,6 +12,12 @@ class Service : IService {
} }
} }
class AnotherService : IService {
public void Log(string message) {
Console.WriteLine($"[AnotherService] {message}");
}
}
interface IOtherService { interface IOtherService {
public Guid Id { get; } public Guid Id { get; }
@@ -39,7 +45,7 @@ class GuidDependantService {
} }
public void LogWithId(string message) { public void LogWithId(string message) {
logService.Log($"[GuidDependantService] {message} (ID: {guidService.Id})"); logService.Log($"[GuidDependantService] {message} (ID: {guidService?.Id})");
} }
} }
@@ -47,6 +53,7 @@ static class Program {
static void Main(string[] args) { static void Main(string[] args) {
var container = new ServiceContainer() var container = new ServiceContainer()
.AddSingleton<IService, Service>() .AddSingleton<IService, Service>()
.AddTransient<IService, AnotherService>()
.AddTransient<IOtherService, GuidService>() .AddTransient<IOtherService, GuidService>()
.AddTransient<GuidDependantService, GuidDependantService>(); .AddTransient<GuidDependantService, GuidDependantService>();
@@ -55,5 +62,6 @@ static class Program {
container.GetService<IOtherService>().ShowId(); container.GetService<IOtherService>().ShowId();
container.GetService<GuidDependantService>().LogWithId("Hello, sent from the dependency."); container.GetService<GuidDependantService>().LogWithId("Hello, sent from the dependency.");
container.GetService<IService>().Log("Goodbye, Dependency Injection!"); container.GetService<IService>().Log("Goodbye, Dependency Injection!");
var res = container.GetServices<IService>();
} }
} }

View File

@@ -14,24 +14,47 @@ public class ServiceContainer {
/// </summary> /// </summary>
/// <typeparam name="TServices"></typeparam> /// <typeparam name="TServices"></typeparam>
/// <returns></returns> /// <returns></returns>
public List<Type> GetServices<TServices>() => public List<Type> GetServiceTypes<TServices>() =>
descriptors.Where(d => d.ServiceType == typeof(TServices)) descriptors.Where(d => d.ServiceType == typeof(TServices))
.Select(d => d.ImplementationType).ToList(); .Select(d => d.ImplementationType).ToList();
/// <summary>
/// Get all registered services for a given service type.
/// </summary>
/// <typeparam name="TService"></typeparam>
public List<TService> GetServices<TService>() where TService : class =>
descriptors.Where(d => d.ServiceType == typeof(TService))
.Select(d => (TService)GetService(d.ImplementationType)).ToList();
/// <summary> /// <summary>
/// Registers a singleton service with its implementation. /// Registers a singleton service with its implementation.
/// </summary> /// </summary>
/// <typeparam name="TInterface">Interface the service is implementing</typeparam> /// <typeparam name="TInterface">Interface the service is implementing</typeparam>
/// <typeparam name="TImplementation">Implementation type of the service</typeparam> /// <typeparam name="TImplementation">Implementation type of the service</typeparam>
public ServiceContainer AddSingleton<TInterface, TImplementation>() public ServiceContainer AddSingleton<TInterface, TImplementation>()
where TInterface : class
where TImplementation : class, TInterface { where TImplementation : class, TInterface {
descriptors.Add(new ServiceDescriptor { descriptors.Add(new () {
ServiceType = typeof(TInterface), ServiceType = typeof(TInterface),
ImplementationType = typeof(TImplementation), ImplementationType = typeof(TImplementation),
Lifetime = ServiceLifetime.Lifetime Lifetime = ServiceLifetime.Lifetime
}); });
return this; return this;
} }
/// <summary>
/// Registers a singleton service where the service type is the same as the implementation type.
/// </summary>
/// <typeparam name="TClass">Class type of the service</typeparam>
public ServiceContainer AddSingleton<TClass>()
where TClass : class {
descriptors.Add(new () {
ServiceType = typeof(TClass),
ImplementationType = typeof(TClass),
Lifetime = ServiceLifetime.Lifetime
});
return this;
}
/// <summary> /// <summary>
/// Registers a transient service with its implementation. /// Registers a transient service with its implementation.
@@ -39,8 +62,9 @@ public class ServiceContainer {
/// <typeparam name="TInterface">Interface the service is implementing</typeparam> /// <typeparam name="TInterface">Interface the service is implementing</typeparam>
/// <typeparam name="TImplementation">Implementation type of the service</typeparam> /// <typeparam name="TImplementation">Implementation type of the service</typeparam>
public ServiceContainer AddTransient<TInterface, TImplementation>() public ServiceContainer AddTransient<TInterface, TImplementation>()
where TInterface : class
where TImplementation : class, TInterface { where TImplementation : class, TInterface {
descriptors.Add(new ServiceDescriptor { descriptors.Add(new () {
ServiceType = typeof(TInterface), ServiceType = typeof(TInterface),
ImplementationType = typeof(TImplementation), ImplementationType = typeof(TImplementation),
Lifetime = ServiceLifetime.Transient Lifetime = ServiceLifetime.Transient
@@ -48,6 +72,20 @@ public class ServiceContainer {
return this; return this;
} }
/// <summary>
/// Registers a transient service where the service type is the same as the implementation type.
/// </summary>
/// <typeparam name="TClass">Class type of the service</typeparam>
public ServiceContainer AddTransient<TClass>()
where TClass : class {
descriptors.Add(new () {
ServiceType = typeof(TClass),
ImplementationType = typeof(TClass),
Lifetime = ServiceLifetime.Transient
});
return this;
}
// you can't call generic methods with an unknown type at compile time // you can't call generic methods with an unknown type at compile time
// so we use reflection to call the generic GetService<T> method with the provided type // so we use reflection to call the generic GetService<T> method with the provided type
// Basically we build the method GetService<serviceType>() at runtime and then call it. // Basically we build the method GetService<serviceType>() at runtime and then call it.
@@ -62,12 +100,12 @@ public class ServiceContainer {
/// <summary> /// <summary>
/// Resolves and returns an instance of the requested service type. /// Resolves and returns an instance of the requested service type.
/// </summary> /// </summary>
/// <typeparam name="TInterface">Interface type of the service being requested</typeparam> /// <typeparam name="TService">Interface type of the service being requested</typeparam>
/// <returns>Resolved service instance</returns> /// <returns>Resolved service instance</returns>
public TInterface GetService<TInterface>() { public TService GetService<TService>() {
var descriptor = descriptors.FirstOrDefault(d => d.ServiceType == typeof(TInterface)); var descriptor = descriptors.FirstOrDefault(d => d.ServiceType == typeof(TService) || d.ImplementationType == typeof(TService));
if (descriptor == null) throw new Exception($"Service of type {typeof(TInterface)} not registered."); if (descriptor == null) throw new Exception($"Service of type {typeof(TService)} not registered.");
var ctors = descriptor.ImplementationType.GetConstructors(); var ctors = descriptor.ImplementationType.GetConstructors();
int max = -1; int max = -1;
@@ -85,19 +123,19 @@ public class ServiceContainer {
} }
} }
if (bestCtor == null) if (bestCtor == null)
throw new Exception($"Cannot create service of type {typeof(TInterface)}. No suitable constructor found."); throw new Exception($"Cannot create service of type {typeof(TService)}. No suitable constructor found.");
// Transient: create a new instance each time // Transient: create a new instance each time
if (descriptor.Lifetime != ServiceLifetime.Lifetime) { if (descriptor.Lifetime != ServiceLifetime.Lifetime) {
var service = Instantiate<TInterface>(descriptor, bestCtor); var service = Instantiate<TService>(descriptor, bestCtor);
return service; return service;
} }
// Singleton: return existing instance // Singleton: return existing instance
if (singletons.TryGetValue(descriptor.ServiceType, out object? singleton)) return (TInterface)singleton; if (singletons.TryGetValue(descriptor.ServiceType, out object? singleton)) return (TService)singleton;
// or create a new one if not yet created. // or create a new one if not yet created.
var newSingleton = Instantiate<TInterface>(descriptor); var newSingleton = Instantiate<TService>(descriptor);
singletons[descriptor.ServiceType] = newSingleton!; singletons[descriptor.ServiceType] = newSingleton!;
return newSingleton; return newSingleton;
} }

View File

@@ -9,7 +9,7 @@
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Version>0.0.1.2-alpha</Version> <Version>0.0.1.5-alpha</Version>
<Title>Syrette </Title> <Title>Syrette </Title>
<Authors>Lorefice Samuele</Authors> <Authors>Lorefice Samuele</Authors>
<Description>Syrette is a minimalistic dependency injection library for C#. It aims to provide a simple and efficient way to achieve dependency injections in your applications without the overhead of larger frameworks.</Description> <Description>Syrette is a minimalistic dependency injection library for C#. It aims to provide a simple and efficient way to achieve dependency injections in your applications without the overhead of larger frameworks.</Description>
@@ -23,7 +23,9 @@
<Company>Samuele Lorefice</Company> <Company>Samuele Lorefice</Company>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<TargetFrameworks>net9.0;net8.0</TargetFrameworks> <TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>

View File

@@ -1,7 +1,7 @@
{ {
"sdk": { "sdk": {
"version": "9.0.0", "version": "10.0.0",
"rollForward": "latestMinor", "rollForward": "latestMinor",
"allowPrerelease": false "allowPrerelease": true
} }
} }