Skip to content

Commit

Permalink
DESIGN -> POC Implementation #10
Browse files Browse the repository at this point in the history
  • Loading branch information
LBoullosa committed Feb 4, 2024
1 parent 4c46007 commit 60050ae
Show file tree
Hide file tree
Showing 15 changed files with 369 additions and 1 deletion.
10 changes: 10 additions & 0 deletions STX.SPAL.Abstractions/ISPALProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// ----------------------------------------------------------------------------------
// Copyright (c) The Standard Organization: A coalition of the Good-Hearted Engineers
// ----------------------------------------------------------------------------------

namespace STX.SPAL.Abstractions
{
public interface ISPALProvider
{
}
}
9 changes: 9 additions & 0 deletions STX.SPAL.Abstractions/STX.SPAL.Abstractions.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

</Project>
11 changes: 11 additions & 0 deletions STX.SPAL.Core/ISPALOrchestrationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// ----------------------------------------------------------------------------------
// Copyright (c) The Standard Organization: A coalition of the Good-Hearted Engineers
// ----------------------------------------------------------------------------------

namespace STX.SPAL.Core
{
public interface ISPALOrchestrationService
{
T GetImplementation<T>();
}
}
25 changes: 25 additions & 0 deletions STX.SPAL.Core/SPALOrchestrationService.Exceptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ----------------------------------------------------------------------------------
// Copyright (c) The Standard Organization: A coalition of the Good-Hearted Engineers
// ----------------------------------------------------------------------------------

using System;

namespace STX.SPAL.Core
{
public partial class SPALOrchestrationService
{
private static T TryCatch<T>(
Func<T> function)
{
try
{
return function();
}

catch (Exception exception)
{
throw;
}
}
}
}
134 changes: 134 additions & 0 deletions STX.SPAL.Core/SPALOrchestrationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// ----------------------------------------------------------------------------------
// Copyright (c) The Standard Organization: A coalition of the Good-Hearted Engineers
// ----------------------------------------------------------------------------------

using Microsoft.Extensions.DependencyInjection;
using STX.SPAL.Abstractions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

namespace STX.SPAL.Core
{
public partial class SPALOrchestrationService : ISPALOrchestrationService
{
private const int DEFAULT_MAXIMUM_DEPTH_ANALISYS = 0;

private readonly IServiceProvider serviceProvider;

public SPALOrchestrationService(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}

private static Type[] GetInterfaceImplementations<T>(Assembly assembly)
where T : ISPALProvider
{
Type spalInterfaceType = typeof(T);

Type[] implementations =
assembly
.GetExportedTypes()
.Where(type =>
type.GetInterfaces()
.Any(interfaceType =>
//@interface is T
interfaceType.Assembly.FullName == spalInterfaceType.Assembly.FullName
&& interfaceType.Namespace == spalInterfaceType.Namespace
&& interfaceType.Name == spalInterfaceType.Name
))
.ToArray();

return implementations;
}

private static Assembly[] GetDependantAssemblies(
Assembly assembly,
int currentDepth = 0,
int maximumDepth = DEFAULT_MAXIMUM_DEPTH_ANALISYS)
{
return assembly
.GetReferencedAssemblies()
.SelectMany(referencedAssemblyName =>
{
Assembly referencedAssembly = Assembly.Load(referencedAssemblyName);

return currentDepth < maximumDepth
? GetDependantAssemblies(referencedAssembly, currentDepth + 1, maximumDepth)
: new Assembly[] { referencedAssembly };
})
.ToArray();
}

private static Assembly[] GetAllAssemblies(Assembly rootAssembly)
{
Assembly[] assemblies =
GetDependantAssemblies(rootAssembly)
.Distinct()
.ToArray();

return assemblies;
}

// Maybe this should go under an Extension Class for IServiceColletion
public static ServiceCollection RegisterAllImplementations<T>(ServiceCollection services)
where T : ISPALProvider
{
// Not work because, even STX.Serializations.Providers.XXXXX were added as a dependant project, there is no
// type used from the main project. So the compiler "removes" those assemblies from GetReferencedAssemblies.
Assembly[] assemblies = GetAllAssemblies(Assembly.GetEntryAssembly());

Type[] typesToRegister = assemblies
.SelectMany(assembly => GetInterfaceImplementations<T>(assembly))
.ToArray();

// This strategy uses MetadataLoadContext. Allows inspecting without explicit dependencies and more memory-efficient.
// https://learn.microsoft.com/en-us/dotnet/standard/assembly/inspect-contents-using-metadataloadcontext
string[] runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll");
var paths = new List<string>(runtimeAssemblies);

string applicationPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
string[] applicationAssembliesPaths = Directory.GetFiles(applicationPath, "*.dll");
paths.AddRange(applicationAssembliesPaths);

var pathAssemblyResolver = new PathAssemblyResolver(paths);
string coreAssemblyName = typeof(object).Assembly.GetName().Name;

using (var metadaLoadContext = new MetadataLoadContext(pathAssemblyResolver, coreAssemblyName))
{
foreach (string applicationAssemblyPath in applicationAssembliesPaths)
{
Assembly assembly = metadaLoadContext.LoadFromAssemblyPath(applicationAssemblyPath);

GetInterfaceImplementations<T>(assembly)
.Select(implementationType =>
{
Assembly runtimeAssembly = Assembly.LoadFrom(applicationAssemblyPath);
implementationType = runtimeAssembly.GetExportedTypes()
.Single(type => type.Name == implementationType.Name);

if (services.Any(serviceDescriptor => serviceDescriptor.ServiceType == typeof(T)))
throw new Exception($"More than one implementation registered for {typeof(T).Name}. Please specify one of them or use keys for specifing multiple implementations for the same interface.");

services.AddTransient(typeof(T), implementationType);
Console.WriteLine($"Registered {implementationType.FullName} ({typeof(T).Name})");

return services;
})
.ToArray();
}
}

Console.WriteLine($"Register Done!.");

return services;
}

public T GetImplementation<T>() =>
TryCatch(() =>
serviceProvider.GetRequiredService<T>());
}
}
18 changes: 18 additions & 0 deletions STX.SPAL.Core/STX.SPAL.Core.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\STX.SPAL.Abstractions\STX.SPAL.Abstractions.csproj" />
</ItemGroup>

</Project>
33 changes: 33 additions & 0 deletions STX.Serialization.POC/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// ----------------------------------------------------------------------------------
// Copyright (c) The Standard Organization: A coalition of the Good-Hearted Engineers
// ----------------------------------------------------------------------------------

using Microsoft.Extensions.DependencyInjection;
using STX.Serialization.Providers.Abstractions;
using STX.SPAL.Core;

namespace STX.Serialization.POC
{
internal class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services = SPALOrchestrationService.RegisterAllImplementations<ISerializationProvider>(services);
services
.AddScoped<ISPALOrchestrationService, SPALOrchestrationService>();

IServiceProvider serviceProvider = services.BuildServiceProvider();
using IServiceScope scope = serviceProvider.CreateScope();

ISPALOrchestrationService spalOrchestrationService =
scope.ServiceProvider
.GetRequiredService<ISPALOrchestrationService>();

ISerializationProvider serializationProvider =
spalOrchestrationService.GetImplementation<ISerializationProvider>();

System.Console.WriteLine(serializationProvider.GetName());
}
}
}
21 changes: 21 additions & 0 deletions STX.Serialization.POC/STX.Serialization.POC.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\STX.Serialization.Providers.Abstractions\STX.Serialization.Providers.Abstractions.csproj" />
<ProjectReference Include="..\STX.Serialization.Providers.NewtonsoftJson\STX.Serialization.Providers.NewtonsoftJson.csproj" />
<ProjectReference Include="..\STX.Serialization.Providers.SystemTextJson\STX.Serialization.Providers.SystemTextJson.csproj" />
<ProjectReference Include="..\STX.SPAL.Core\STX.SPAL.Core.csproj" />
</ItemGroup>

</Project>
38 changes: 37 additions & 1 deletion STX.Serialization.Providers.Abstractions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34511.84
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STX.Serialization.Providers.Abstractions", "STX.Serialization.Providers.Abstractions\STX.Serialization.Providers.Abstractions.csproj", "{38A8052D-3C6E-44DC-ACEB-AA28881C5CD5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "STX.Serialization.Providers.Abstractions", "STX.Serialization.Providers.Abstractions\STX.Serialization.Providers.Abstractions.csproj", "{38A8052D-3C6E-44DC-ACEB-AA28881C5CD5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "STX.Serialization.Providers.Abstractions.Infrastructure.Build", "STX.Serialization.Providers.Abstractions.Infrastructure.Build\STX.Serialization.Providers.Abstractions.Infrastructure.Build.csproj", "{D47D1D45-BD43-4735-BFB5-83296B2941A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "STX.Serialization.Providers.Abstractions.Tests.Acceptance", "STX.Serialization.Providers.Abstractions.Tests.Acceptance\STX.Serialization.Providers.Abstractions.Tests.Acceptance.csproj", "{A3E3CBE6-74E2-4196-94E1-F4238C36639E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "STX.Serialization.Core", "..\STX.Serialization.Core\STX.Serialization.Core.csproj", "{816563DA-AAED-4BDB-A73B-7AF9E0D7CD52}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STX.SPAL.Core", "STX.SPAL.Core\STX.SPAL.Core.csproj", "{9B7FD3F7-B60C-446E-9934-D55C2FFFE8C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STX.SPAL.Abstractions", "STX.SPAL.Abstractions\STX.SPAL.Abstractions.csproj", "{4D5CC8C1-688F-41A6-B866-57AD16FEFFA0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STX.Serialization.Providers.SystemTextJson", "STX.Serialization.Providers.SystemTextJson\STX.Serialization.Providers.SystemTextJson.csproj", "{A0DE0DD1-7975-47A4-AC20-F95BCC4D8901}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STX.Serialization.Providers.NewtonsoftJson", "STX.Serialization.Providers.NewtonsoftJson\STX.Serialization.Providers.NewtonsoftJson.csproj", "{9DC355E4-5391-4572-B4D0-26684F00F737}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STX.Serialization.POC", "STX.Serialization.POC\STX.Serialization.POC.csproj", "{4ADB3A3D-FF90-4531-AA33-446A67DF2471}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,6 +39,30 @@ Global
{A3E3CBE6-74E2-4196-94E1-F4238C36639E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3E3CBE6-74E2-4196-94E1-F4238C36639E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3E3CBE6-74E2-4196-94E1-F4238C36639E}.Release|Any CPU.Build.0 = Release|Any CPU
{816563DA-AAED-4BDB-A73B-7AF9E0D7CD52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{816563DA-AAED-4BDB-A73B-7AF9E0D7CD52}.Debug|Any CPU.Build.0 = Debug|Any CPU
{816563DA-AAED-4BDB-A73B-7AF9E0D7CD52}.Release|Any CPU.ActiveCfg = Release|Any CPU
{816563DA-AAED-4BDB-A73B-7AF9E0D7CD52}.Release|Any CPU.Build.0 = Release|Any CPU
{9B7FD3F7-B60C-446E-9934-D55C2FFFE8C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9B7FD3F7-B60C-446E-9934-D55C2FFFE8C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B7FD3F7-B60C-446E-9934-D55C2FFFE8C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B7FD3F7-B60C-446E-9934-D55C2FFFE8C7}.Release|Any CPU.Build.0 = Release|Any CPU
{4D5CC8C1-688F-41A6-B866-57AD16FEFFA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D5CC8C1-688F-41A6-B866-57AD16FEFFA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D5CC8C1-688F-41A6-B866-57AD16FEFFA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D5CC8C1-688F-41A6-B866-57AD16FEFFA0}.Release|Any CPU.Build.0 = Release|Any CPU
{A0DE0DD1-7975-47A4-AC20-F95BCC4D8901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0DE0DD1-7975-47A4-AC20-F95BCC4D8901}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0DE0DD1-7975-47A4-AC20-F95BCC4D8901}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0DE0DD1-7975-47A4-AC20-F95BCC4D8901}.Release|Any CPU.Build.0 = Release|Any CPU
{9DC355E4-5391-4572-B4D0-26684F00F737}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DC355E4-5391-4572-B4D0-26684F00F737}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DC355E4-5391-4572-B4D0-26684F00F737}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DC355E4-5391-4572-B4D0-26684F00F737}.Release|Any CPU.Build.0 = Release|Any CPU
{4ADB3A3D-FF90-4531-AA33-446A67DF2471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4ADB3A3D-FF90-4531-AA33-446A67DF2471}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4ADB3A3D-FF90-4531-AA33-446A67DF2471}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4ADB3A3D-FF90-4531-AA33-446A67DF2471}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
13 changes: 13 additions & 0 deletions STX.Serialization.Providers.Abstractions/ISerializationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// ----------------------------------------------------------------------------------
// Copyright (c) The Standard Organization: A coalition of the Good-Hearted Engineers
// ----------------------------------------------------------------------------------

using STX.SPAL.Abstractions;

namespace STX.Serialization.Providers.Abstractions
{
public interface ISerializationProvider : ISPALProvider
{
string GetName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\STX.SPAL.Abstractions\STX.SPAL.Abstractions.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\STX.Serialization.Providers.Abstractions\STX.Serialization.Providers.Abstractions.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// ----------------------------------------------------------------------------------
// Copyright (c) The Standard Organization: A coalition of the Good-Hearted Engineers
// ----------------------------------------------------------------------------------

using STX.Serialization.Providers.Abstractions;

namespace STX.Serialization.Providers.NewtonsoftJson
{
public class SerializationProvider : ISerializationProvider
{
public string GetName() =>
this.GetType().FullName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\STX.Serialization.Providers.Abstractions\STX.Serialization.Providers.Abstractions.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit 60050ae

Please sign in to comment.