Files
speckle-sharp-connectors/Sdk/Speckle.Converters.Common/Registration/ServiceRegistration.cs
T
Adam Hathcock 94104c2365 NameAndRankValue now uses a type to get FullName to avoid conflicts (#475)
* NameAndRankValue now uses a type to get FullName to avoid conflicts

* Full namespace for tests
2025-01-10 13:59:59 +00:00

115 lines
4.3 KiB
C#

using System.Collections.Concurrent;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Converters.Common.Objects;
using Speckle.Converters.Common.ToHost;
using Speckle.Sdk.Common;
namespace Speckle.Converters.Common.Registration;
public static class ServiceRegistration
{
public static void AddRootCommon<TRootToSpeckleConverter>(
this IServiceCollection serviceCollection,
Assembly converterAssembly
)
where TRootToSpeckleConverter : class, IRootToSpeckleConverter
{
serviceCollection.AddScoped<IRootToSpeckleConverter, TRootToSpeckleConverter>();
/*
POC: CNX-9267 Moved the Injection of converters into the converter module. Not sure if this is 100% right, as this doesn't just register the conversions within this converter, but any conversions found in any Speckle.*.dll file.
This will require consolidating across other connectors.
*/
serviceCollection.AddScoped<IRootToHostConverter, ConverterWithFallback>();
serviceCollection.AddScoped<ConverterWithoutFallback>(); //Register as self, only the `ConverterWithFallback` needs it
serviceCollection.AddConverters<IToSpeckleTopLevelConverter>(converterAssembly);
serviceCollection.AddConverters<IToHostTopLevelConverter>(converterAssembly);
}
public static IServiceCollection AddApplicationConverters<THostToSpeckleUnitConverter, THostUnits>(
this IServiceCollection serviceCollection,
Assembly converterAssembly
)
where THostToSpeckleUnitConverter : class, IHostToSpeckleUnitConverter<THostUnits>
{
serviceCollection.AddScoped<IHostToSpeckleUnitConverter<THostUnits>, THostToSpeckleUnitConverter>();
serviceCollection.RegisterRawConversions(converterAssembly);
return serviceCollection;
}
public static void AddConverters<T>(this IServiceCollection serviceCollection, Assembly converterAssembly)
{
ConcurrentDictionary<string, Type> converterTypes = new();
var exportedTypes = converterAssembly.ExportedTypes.Where(x => x.GetInterfaces().Contains(typeof(T)));
// we only care about named types
var byName = exportedTypes
.Where(x => x.GetCustomAttribute<NameAndRankValueAttribute>() != null)
.Select(x =>
{
var nameAndRank = x.GetCustomAttribute<NameAndRankValueAttribute>().NotNull();
return (Type: nameAndRank.Type, Rank: nameAndRank.Rank, Converter: x);
})
.ToList();
// we'll register the types accordingly
var types = byName.Select(x => x.Type).Distinct();
foreach (Type type in types)
{
var namedTypes = byName.Where(x => x.Type == type).OrderByDescending(y => y.Rank).ToList();
// first type found
var first = namedTypes[0];
// POC: may need to be instance per lifecycle scope
converterTypes.TryAdd(first.Type.FullName.NotNull(), first.Converter);
// POC: not sure yet if...
// * This should be an array of types
// * Whether the scope should be modified or modifiable
// * Whether this is in the write project... hmmm
// POC: IsAssignableFrom()
var secondaryType = first.Type.GetInterface(typeof(ITypedConverter<,>).Name);
// POC: should we explode if no found?
if (secondaryType != null)
{
converterTypes.TryAdd(first.Type.FullName, secondaryType);
}
// register subsequent types with rank
namedTypes.RemoveAt(0);
}
serviceCollection.AddScoped<IConverterManager<T>>(sp => new ConverterManager<T>(converterTypes, sp));
}
public static void RegisterRawConversions(this IServiceCollection serviceCollection, Assembly conversionAssembly)
{
// POC: hard-coding speckle... :/
foreach (Type speckleType in conversionAssembly.ExportedTypes)
{
RegisterRawConversionsForType(serviceCollection, speckleType);
}
}
private static void RegisterRawConversionsForType(IServiceCollection serviceCollection, Type type)
{
if (!type.IsClass || type.IsAbstract)
{
return;
}
var rawConversionInterfaces = type.GetInterfaces()
.Where(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(ITypedConverter<,>));
foreach (var conversionInterface in rawConversionInterfaces)
{
serviceCollection.Add(new ServiceDescriptor(conversionInterface, type, ServiceLifetime.Scoped));
}
}
}