FractalDataWorks.ServiceTypes 0.30.0-alpha.1227

This is a prerelease version of FractalDataWorks.ServiceTypes.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package FractalDataWorks.ServiceTypes --version 0.30.0-alpha.1227
                    
NuGet\Install-Package FractalDataWorks.ServiceTypes -Version 0.30.0-alpha.1227
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FractalDataWorks.ServiceTypes" Version="0.30.0-alpha.1227" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FractalDataWorks.ServiceTypes" Version="0.30.0-alpha.1227" />
                    
Directory.Packages.props
<PackageReference Include="FractalDataWorks.ServiceTypes" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FractalDataWorks.ServiceTypes --version 0.30.0-alpha.1227
                    
#r "nuget: FractalDataWorks.ServiceTypes, 0.30.0-alpha.1227"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FractalDataWorks.ServiceTypes@0.30.0-alpha.1227
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FractalDataWorks.ServiceTypes&version=0.30.0-alpha.1227&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=FractalDataWorks.ServiceTypes&version=0.30.0-alpha.1227&prerelease
                    
Install as a Cake Tool

FractalDataWorks.ServiceTypes

A plugin architecture framework that provides type-safe service registration, discovery, and factory creation with source-generated lookups.

Overview

ServiceTypes extend TypeCollections to provide service-specific capabilities including:

  • Factory registration and resolution
  • Configuration binding with IOptions/IOptionsSnapshot/IOptionsMonitor
  • Three-phase registration pattern for DI integration
  • Provider-based service resolution

Target Framework

This package targets netstandard2.0 and net10.0.

Core Components

IServiceType Interface Hierarchy

From IServiceType.cs:18-74:

public interface IServiceType<TKey, TService, TFactory, TConfiguration> : IServiceType<TKey, TService, TFactory>
    where TKey : IEquatable<TKey>
    where TService : IGenericService
    where TFactory : IServiceFactory<TService, TConfiguration>
    where TConfiguration : IGenericConfiguration
{
}

public interface IServiceType<TKey, TService, TFactory> : IServiceType<TKey, TService>
    where TKey : IEquatable<TKey>
    where TService : IGenericService
    where TFactory : IServiceFactory<TService>
{
}

public interface IServiceType<TKey, TService> : IServiceType<TKey>
    where TKey : IEquatable<TKey>
    where TService : IGenericService
{
}

public interface IServiceType<TKey> : ITypeOption<TKey, IServiceType<TKey>>
    where TKey : IEquatable<TKey>
{
    Type ServiceType { get; }
    Type FactoryType { get; }
    Type ConfigurationType { get; }
}

The non-generic IServiceType interface provides registration methods:

From IServiceType.cs:80-136:

public interface IServiceType : IServiceType<Guid>
{
    IServiceCollection RegisterRequiredServices(IServiceCollection services);
    void RegisterFactory(ServiceProvider provider, IServiceProvider services);
    void RegisterProviderServices(ServiceProvider provider, IServiceProvider services);
}

ServiceTypeBase Hierarchy

From ServiceTypeBase.cs:157-271:

public abstract class ServiceTypeBase<TService, TFactory> : TypeOptionBase<Guid, IServiceType<Guid>>, IServiceType<Guid, TService, TFactory>, IServiceType
    where TService : class, IGenericService
    where TFactory : class, IServiceFactory<TService>
{
    [TypeLookup("ById")]
    public new static Guid Id { get; }

    [TypeLookup("ByName")]
    public new string Name => base.Name;

    [TypeLookup("ServiceType")]
    public Type ServiceType => typeof(TService);

    [TypeLookup("FromConfigurationType")]
    public virtual Type ConfigurationType => typeof(IGenericConfiguration);

    public string SectionName => ConfigurationKey;
    public OptionsLoaderBase OptionsLoader { get; }
    public Type FactoryType => typeof(TFactory);

    public abstract IServiceCollection RegisterRequiredServices(IServiceCollection services);
    public virtual void RegisterFactory(ServiceProvider provider, IServiceProvider services) { }
    public virtual void RegisterProviderServices(ServiceProvider provider, IServiceProvider services) { }

    protected ServiceTypeBase(
        string name,
        string sectionName,
        string displayName,
        string description,
        OptionsLoaderBase optionsLoader,
        string? category = null)
        : base(Id, name, sectionName, displayName, description, category)
    {
        OptionsLoader = optionsLoader ?? throw new ArgumentNullException(nameof(optionsLoader));
    }
}

The three-parameter variant adds configuration binding:

From ServiceTypeBase.cs:98-148:

public abstract class ServiceTypeBase<TService, TFactory, TConfiguration> : ServiceTypeBase<TService, TFactory>, IServiceType<Guid, TService, TFactory, TConfiguration>
    where TService : class, IGenericService
    where TConfiguration : class, IGenericConfiguration
    where TFactory : class, IServiceFactory<TService, TConfiguration>
{
    [TypeLookup("FromConfigurationType")]
    public override Type ConfigurationType => typeof(TConfiguration);

    protected void RegisterConfiguration(IServiceCollection services)
    {
        services.AddOptions<TConfiguration>()
            .BindConfiguration(SectionName)
            .ValidateDataAnnotations()
            .ValidateOnStart();
    }
}

The four-parameter variant adds typed provider support:

From ServiceTypeBase.cs:19-88:

public abstract class ServiceTypeBase<TService, TFactory, TConfiguration, TProvider> : ServiceTypeBase<TService, TFactory, TConfiguration>
    where TService : class, IGenericService
    where TConfiguration : class, IGenericConfiguration
    where TFactory : class, IServiceFactory<TService, TConfiguration>
    where TProvider : ServiceProvider
{
    public abstract void RegisterFactory(TProvider provider, IServiceProvider services);
    public virtual void RegisterProviderServices(TProvider provider, IServiceProvider services) { }
}

ServiceTypeCollectionBase

From ServiceTypeCollectionBase.cs:1-35:

public abstract class ServiceTypeCollectionBase<TBase, TGeneric, TService, TConfiguration, TFactory>
    where TBase : class, IServiceType<Guid, TService, TFactory, TConfiguration>
    where TGeneric : IServiceType<Guid, TService, TFactory, TConfiguration>
    where TService : class, IGenericService
    where TConfiguration : class, IGenericConfiguration
    where TFactory : class, IServiceFactory<TService, TConfiguration>
{
    // Source generator provides all implementation:
    // - Empty sentinel
    // - ById(TKey) method
    // - ByName(string) method
    // - All() method
    // - RegisterAll(IServiceCollection) convenience method
    // - Custom lookup methods from [TypeLookup] attributes
    // - Static extension methods for each ServiceTypeOption
}

ServiceProvider (Mini-IoC)

From ServiceProvider.cs:1-67:

public class ServiceProvider
{
    private readonly Dictionary<Type, object> _services = new();

    public ServiceProvider AddSingleton<T>(T instance) where T : class
    {
        _services[typeof(T)] = instance;
        return this;
    }

    public T GetRequiredService<T>() where T : class
    {
        if (!_services.TryGetValue(typeof(T), out var service))
        {
            throw new InvalidOperationException(
                $"Service of type {typeof(T).Name} is not registered.");
        }
        return (T)service;
    }

    public T? GetService<T>() where T : class
    {
        return _services.TryGetValue(typeof(T), out var service) ? (T)service : null;
    }
}

Attributes

ServiceTypeCollectionAttribute

From ServiceTypeCollectionAttribute.cs:1-115:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class ServiceTypeCollectionAttribute : Attribute
{
    public ServiceTypeCollectionAttribute(
        Type baseType,
        Type interfaceType,
        Type collectionType,
        Type? parentCollection = null,
        string? name = null)
    { }

    public Type BaseType { get; }
    public Type InterfaceType { get; }
    public Type CollectionType { get; }
    public Type? ParentCollection { get; }
    public string? Name { get; }
    public bool RestrictToCurrentCompilation { get; set; }
    public string? DefaultOptionsLoader { get; set; }
    public bool GenerateProvider { get; set; }
    public Type? ServiceInterface { get; set; }
    public Type? ConfigurationInterface { get; set; }
    public Type? ProviderType { get; set; }
    public Type? ProviderInterface { get; set; }
}

ServiceTypeOptionAttribute

From ServiceTypeOptionAttribute.cs:1-38:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class ServiceTypeOptionAttribute : Attribute
{
    public ServiceTypeOptionAttribute(Type collectionType, string name)
    {
        CollectionType = collectionType;
        Name = name;
    }

    public Type CollectionType { get; }
    public string Name { get; }
}

TypeLookupAttribute

From TypeLookupAttribute.cs:1-42:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class TypeLookupAttribute : Attribute
{
    public TypeLookupAttribute(
        string methodName,
        bool returnsList = false,
        Type? returnType = null)
    {
        MethodName = methodName;
        ReturnsList = returnsList;
        ReturnType = returnType;
    }

    public string MethodName { get; }
    public bool ReturnsList { get; }
    public Type? ReturnType { get; }
}

Real-World Example: Connection Types

Collection Declaration

From ConnectionTypes.cs:1-25:

[ServiceTypeCollection(
    typeof(ConnectionTypeBase<IGenericConnection, IConnectionFactory<IGenericConnection, IConnectionConfiguration>, IConnectionConfiguration>),
    typeof(IConnectionType),
    typeof(ConnectionTypes),
    GenerateProvider = true,
    ServiceInterface = typeof(IGenericConnection),
    ConfigurationInterface = typeof(IConnectionConfiguration),
    ProviderType = typeof(DefaultConnectionProvider),
    ProviderInterface = typeof(IConnectionProvider))]
public partial class ConnectionTypes : ServiceTypeCollectionBase<
    ConnectionTypeBase<IGenericConnection, IConnectionFactory<IGenericConnection, IConnectionConfiguration>, IConnectionConfiguration>,
    IConnectionType<IGenericConnection, IConnectionConfiguration, IConnectionFactory<IGenericConnection, IConnectionConfiguration>>>
{
}

Base Class

From ConnectionTypeBase.cs:30-55:

public abstract class ConnectionTypeBase<TService, TFactory, TConfiguration> :
    ServiceTypeBase<TService, TFactory, TConfiguration, DefaultConnectionProvider>,
    IConnectionType<TService, TConfiguration, TFactory>
    where TService : class, IGenericConnection
    where TConfiguration : class, IConnectionConfiguration
    where TFactory : class, IConnectionFactory<TService, TConfiguration>
{
    protected ConnectionTypeBase(
        string name,
        string sectionName,
        string displayName,
        string description,
        OptionsLoaderBase? optionsLoader = null,
        string? category = null)
        : base(name, sectionName, displayName, description, optionsLoader ?? OptionsLoaderTypes.Reloadable, category ?? "Connection")
    {
    }
}

Concrete Implementation

From MsSqlConnectionType.cs:41-86:

[ServiceTypeOption(typeof(ConnectionTypes), "MsSql")]
public sealed class MsSqlConnectionType : ConnectionTypeBase<IGenericConnection, IMsSqlConnectionFactory, MsSqlConnectionConfiguration>
{
    public MsSqlConnectionType() : base(
        name: "MsSql",
        sectionName: "MsSql",
        displayName: "SQL Server",
        description: "Microsoft SQL Server database connection",
        category: "Database")
    {
    }

    public override IServiceCollection RegisterRequiredServices(IServiceCollection services)
    {
        services.AddSingleton<IMsSqlConnectionFactory, MsSqlConnectionFactory>();
        return services;
    }

    public override void RegisterFactory(DefaultConnectionProvider provider, IServiceProvider services)
    {
        var factory = services.GetRequiredService<IMsSqlConnectionFactory>();
        provider.RegisterFactory(Name, factory);
    }
}

OptionsLoader Types

ServiceTypes use OptionsLoader to control configuration lifetime. Three options are available:

From SingletonOptionsLoader.cs:24-31:

[TypeOption(typeof(OptionsLoaderTypes), "Singleton")]
public sealed class SingletonOptionsLoader : OptionsLoaderBase
{
    public SingletonOptionsLoader() : base(1, "Singleton") { }
    public override string OptionsInterfaceName => "IOptions";
    public override bool SupportsHotReload => false;
}

From ScopedOptionsLoader.cs:27-34:

[TypeOption(typeof(OptionsLoaderTypes), "Scoped")]
public sealed class ScopedOptionsLoader : OptionsLoaderBase
{
    public ScopedOptionsLoader() : base(2, "Scoped") { }
    public override string OptionsInterfaceName => "IOptionsSnapshot";
    public override bool SupportsHotReload => false;
}

From ReloadableOptionsLoader.cs:28-35:

[TypeOption(typeof(OptionsLoaderTypes), "Reloadable")]
public sealed class ReloadableOptionsLoader : OptionsLoaderBase
{
    public ReloadableOptionsLoader() : base(3, "Reloadable") { }
    public override string OptionsInterfaceName => "IOptionsMonitor";
    public override bool SupportsHotReload => true;
}
Loader IOptions Interface When to Use
SingletonOptionsLoader IOptions<T> Static configuration that never changes
ScopedOptionsLoader IOptionsSnapshot<T> Multi-tenant, per-request configuration
ReloadableOptionsLoader IOptionsMonitor<T> Database config, hot reload needed

Cross-Assembly Registration

ServiceTypes use a deferred freeze pattern for cross-assembly type discovery. This ensures ServiceTypeOptions defined in separate NuGet packages are registered before the collection is accessed.

The Problem

sequenceDiagram
    participant App as Your Application
    participant Col as ConnectionTypes
    participant Lib as MsSql Package

    Note over App: Application starts
    App->>Col: ConnectionTypes.All()
    Note over Col: Collection freezes!
    Col-->>App: Returns 0 items
    Note over Lib: MsSql static ctor never ran

Static constructors only run when something in that assembly is accessed. If you call ConnectionTypes.All() before any code references MsSqlConnectionType, the MsSql library is never loaded.

The Solution: Module Initializers

The Registration source generator creates module initializers in consuming assemblies that register all ServiceTypeOptions from referenced packages:

sequenceDiagram
    participant CLR as .NET Runtime
    participant Init as Module Initializer
    participant Col as ConnectionTypes
    participant App as Main()

    Note over CLR: Assembly loads
    CLR->>Init: [ModuleInitializer] runs
    Init->>Col: RegisterMember(new MsSqlConnectionType())
    Note over Col: Added to pending list
    CLR->>App: Main() starts
    App->>Col: ConnectionTypes.All()
    Note over Col: Freezes with MsSql
    Col-->>App: Returns 1 item

Registration Flow

flowchart TB
    subgraph CompileTime["Compile Time"]
        A[Library defines ServiceTypeOption] --> B[Static ctor registers local options]
        C[Consumer references library] --> D[Registration generator scans references]
        D --> E[Generates module initializer]
    end

    subgraph BeforeMain["Runtime - Before Main()"]
        F[Assembly loads] --> G[Module initializer runs]
        G --> H[RegisterMember called]
        H --> I[Options in pending list]
    end

    subgraph FirstAccess["Runtime - First Access"]
        J[Code calls All/ByName] --> K[EnsureFrozen]
        K --> L[Collection freezes]
        L --> M[FrozenDictionary created]
    end

    CompileTime --> BeforeMain --> FirstAccess

Generated Code

The collection generator produces this registration infrastructure:

public partial class ConnectionTypes
{
    // Pending registrations (before freeze)
    private static readonly List<IServiceType> _pendingRegistrations = new();
    private static readonly object _lock = new();
    private static volatile bool _frozen;
    private static FrozenDictionary<Guid, IConnectionType>? _all;

    // Static constructor - registers options discovered at compile time
    static ConnectionTypes()
    {
        // Only options in THIS assembly (Abstractions has none)
    }

    // Called by module initializers from consuming assemblies
    public static void RegisterMember(IServiceType type)
    {
        if (_frozen)
            throw new InvalidOperationException("Collection is frozen");

        lock (_lock)
        {
            if (!_pendingRegistrations.Any(p => p.Id == type.Id))
                _pendingRegistrations.Add(type);
        }
    }

    // Called on first access to Any lookup method
    private static void EnsureFrozen()
    {
        if (_all != null) return;
        lock (_lock)
        {
            _frozen = true;
            _all = _pendingRegistrations
                .Cast<IConnectionType>()
                .ToFrozenDictionary(x => x.Id);
        }
    }
}

The Registration generator produces this in consuming assemblies:

// Generated in YOUR application
internal static class ServiceTypeOptionModuleInitializer
{
    [ModuleInitializer]
    internal static void RegisterServiceTypeOptions()
    {
        ConnectionTypes.RegisterMember(new MsSqlConnectionType());
    }
}

For Package Authors

To enable transitive registration for consumers, embed the Registration generator:

<ItemGroup>
  
  <ProjectReference Include="..\Collections.SourceGenerators.Registration\..."
                    OutputItemType="Analyzer"
                    ReferenceOutputAssembly="false" />

  
  <None Include="$(OutputPath)FractalDataWorks.Collections.SourceGenerators.Registration.dll"
        Pack="true"
        PackagePath="analyzers/dotnet/cs" />
</ItemGroup>

Configuration Binding

Each ServiceTypeOption is responsible for binding its own configuration section via the Configure() method:

// MsSqlConnectionType.cs
public override void Configure(IServiceCollection services, IConfiguration configuration)
{
    services.Configure<List<MsSqlConnectionConfiguration>>(
        configuration.GetSection("Connections:MsSql"));
}

The source-generated ConfigureAll() method iterates through all registered ServiceTypes:

// Generated in ConnectionTypes
public static void ConfigureAll(IServiceCollection services, IConfiguration configuration)
{
    EnsureFrozen();  // Freezes the collection first!

    foreach (var serviceType in _all!.Values)
    {
        serviceType.Configure(services, configuration);
    }
}

Configuration Binding Flow

sequenceDiagram
    participant Init as Module Initializer
    participant Col as ConnectionTypes
    participant App as Program.cs
    participant MsSql as MsSqlConnectionType

    Note over Init: Assembly loads (before Main)
    Init->>Col: RegisterMember(new MsSqlConnectionType())
    Note over Col: Added to pending list

    Note over App: ConfigureServices starts
    App->>Col: ConnectionTypes.ConfigureAll(services, config)
    Col->>Col: EnsureFrozen()
    Note over Col: Collection freezes with MsSql included

    loop For each ServiceType
        Col->>MsSql: Configure(services, configuration)
        MsSql->>App: services.Configure<List<MsSqlConfig>>(...)
    end

    Note over App: Configuration sections bound to IOptions

Critical: ConfigureAll() calls EnsureFrozen() first. All ServiceTypeOptions must be registered (via module initializers) BEFORE ConfigureAll() is called, otherwise their configuration won't be bound.

Usage in Program.cs

var builder = WebApplication.CreateBuilder(args);

// Configuration binding - must happen after module initializers run
// (which happens automatically before Main())
ConnectionTypes.ConfigureAll(builder.Services, builder.Configuration);

// Three-phase DI registration
ConnectionTypes.RegisterAll(builder.Services);

var app = builder.Build();

// Initialize factories (Phase 2 completion)
ConnectionTypes.InitializeFactories(app.Services);

Three-Phase DI Registration Pattern

ServiceTypes use a three-phase registration pattern for DI integration:

flowchart LR
    subgraph Phase1["Phase 1: RegisterRequiredServices"]
        A[Register factories as singletons]
        B[Register infrastructure services]
    end

    subgraph Phase2["Phase 2: RegisterFactory"]
        C[Resolve factory from DI]
        D[Register with typed provider]
    end

    subgraph Phase3["Phase 3: RegisterProviderServices"]
        E[Register config loaders]
        F[Register provider-scoped services]
    end

    Phase1 --> Phase2 --> Phase3

Phase 1: RegisterRequiredServices - Register infrastructure and factories with main DI container Phase 2: RegisterFactory - Resolve factories from DI and register with typed provider Phase 3: RegisterProviderServices - Register services scoped to the provider

See the MsSqlConnectionType example above for the complete pattern.

Configuration Self-Registration Pattern

Each ServiceType is responsible for loading its own configuration from the database. This replaces the previous centralized header table pattern (cfg.Configuration) with a distributed approach where each service type manages its own configuration tables.

How It Works

  1. Define Configuration Table: Use [ManagedConfiguration] attribute to specify database schema:
[ManagedConfiguration(
    Schema = "cfg",
    TableName = "MsSqlConnection",
    Parent = nameof(ConnectionConfigurationBase<MsSqlConnectionConfiguration>),
    ServiceCategory = "Connection",
    ServiceType = "MsSql")]
public partial class MsSqlConnectionConfiguration : ConnectionConfigurationBase<MsSqlConnectionConfiguration>
{
    // Type-specific properties loaded from cfg.MsSqlConnection table
}
  1. Implement Configure Method: Each ServiceType should implement a Configure() method to load its configuration:
// TODO: This pattern is being implemented via ConfigureAll()
public override void Configure(IConfiguration configuration, IServiceProvider services)
{
    // Load type-specific configuration from database
    var configService = services.GetRequiredService<IConfigurationService>();
    var configs = await configService.LoadAsync<MsSqlConnectionConfiguration>();

    // Register with IOptions or directly with provider
}
  1. ConfigureAll Pattern: The {ServiceType}Types.ConfigureAll() method will iterate through all registered service types and call their Configure() methods.

Benefits

  • Decoupled: Each ServiceType manages its own configuration independently
  • Extensible: New service types can define their own tables without modifying core schema
  • Type-Safe: Configuration classes are strongly typed via source generation
  • Lazy Loading: Configuration is loaded only when the ServiceType is used

Dependencies

  • FractalDataWorks.Abstractions
  • FractalDataWorks.Results
  • FractalDataWorks.Configuration.Abstractions
  • Microsoft.Extensions.Configuration.Abstractions
  • Microsoft.Extensions.DependencyInjection.Abstractions
  • Microsoft.Extensions.Options
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on FractalDataWorks.ServiceTypes:

Package Downloads
CyberdyneDevelopment.Mc3Po.Tools.Abstractions

Tool abstractions for FractalDataWorks Roslyn development tools. Provides TypeCollections for tool categories and parameter types.

CyberdyneDevelopment.Mc3Po.Protocols

Protocol infrastructure for mc3-po - base classes, provider, and ServiceType integration

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
Loading failed