├── resources ├── logo-circle.ico ├── logo-circle.png ├── logo-horizontal.ico ├── logo-horizontal.png ├── logo-circle.svg └── logo-horizontal.svg ├── StrongInject.Generator ├── IsExternalInit.cs ├── DisposalStyleDeterminant.cs ├── FactoryOfMethod.cs ├── DisposalStyle.cs ├── Disposal.cs ├── DecoratorOptions.cs ├── StrongInject.Generator.csproj ├── Visitors │ ├── IVisitor.cs │ ├── RequiresUnsafeVisitor.cs │ ├── PartialOrderingOfSingleInstanceDependenciesVisitor.cs │ ├── RequiresAsyncVisitor.cs │ ├── SingleInstanceVariablesToCreateEarlyVisitor.cs │ └── SimpleVisitor.cs ├── DecoratorSource.cs ├── Options.cs ├── Operation.cs ├── Scope.cs ├── Statement.cs ├── AutoIndenter.cs └── InstanceSources.cs ├── StrongInject.Tests.Integration ├── IsExternalInit.cs ├── TestNullableConversion.cs ├── Modules │ ├── LazyTests.cs │ ├── CollectionTests.cs │ └── ImmutableArrayTests.cs ├── TestSingleInstanceNull.cs ├── TestFuncParameterInjection.cs ├── TestInstancePerResolutionHasCorrectScope.cs ├── TestSingleInstanceHasCorrectScope.cs ├── TestInstancePerDependencyHasCorrectScope.cs ├── StrongInject.Tests.Integration.csproj └── TestFuncScope.cs ├── Samples ├── Xamarin │ ├── StrongInject.Samples.XamarinApp │ │ ├── AssemblyInfo.cs │ │ ├── Services │ │ │ ├── Browser.cs │ │ │ ├── IBrowser.cs │ │ │ ├── IDataStore.cs │ │ │ ├── INavigationService.cs │ │ │ ├── NavigationService.cs │ │ │ └── MockDataStore.cs │ │ ├── Views │ │ │ ├── AboutPage.xaml.cs │ │ │ ├── ItemDetailPage.xaml.cs │ │ │ ├── IViewOf.cs │ │ │ ├── LoginPage.xaml.cs │ │ │ ├── NewItemPage.xaml.cs │ │ │ ├── ItemsPage.xaml.cs │ │ │ ├── ItemDetailPage.xaml │ │ │ ├── LoginPage.xaml │ │ │ ├── NewItemPage.xaml │ │ │ ├── ItemsPage.xaml │ │ │ └── AboutPage.xaml │ │ ├── Models │ │ │ └── Item.cs │ │ ├── ViewModels │ │ │ ├── AboutViewModel.cs │ │ │ ├── LoginViewModel.cs │ │ │ ├── ItemDetailViewModel.cs │ │ │ ├── BaseViewModel.cs │ │ │ └── NewItemViewModel.cs │ │ ├── App.xaml.cs │ │ ├── StrongInject.Samples.XamarinApp.csproj │ │ ├── AppShell.xaml.cs │ │ ├── GettingStarted.txt │ │ ├── App.xaml │ │ └── Container.cs │ ├── StrongInject.Samples.XamarinApp.Android │ │ ├── Resources │ │ │ ├── mipmap-hdpi │ │ │ │ ├── icon.png │ │ │ │ └── launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── icon.png │ │ │ │ └── launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── icon.png │ │ │ │ └── launcher_foreground.png │ │ │ ├── drawable │ │ │ │ ├── icon_about.png │ │ │ │ ├── icon_feed.png │ │ │ │ └── xamarin_logo.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── icon.png │ │ │ │ └── launcher_foreground.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── icon.png │ │ │ │ └── launcher_foreground.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── icon.xml │ │ │ │ └── icon_round.xml │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── layout │ │ │ │ ├── Toolbar.xml │ │ │ │ └── Tabbar.xml │ │ │ └── AboutResources.txt │ │ ├── Properties │ │ │ ├── AndroidManifest.xml │ │ │ └── AssemblyInfo.cs │ │ ├── Assets │ │ │ └── AboutAssets.txt │ │ └── MainActivity.cs │ └── README.md ├── Console │ ├── config.json │ ├── IConfigLoader.cs │ ├── Message.cs │ ├── ICommitable.cs │ ├── IProducer.cs │ ├── User.cs │ ├── IConsumer.cs │ ├── Config.cs │ ├── JsonSerializer.cs │ ├── JsonDeserializer.cs │ ├── Cache.cs │ ├── JsonConfigLoader.cs │ ├── KafkaProducer.cs │ ├── StrongInject.Samples.ConsoleApp.csproj │ ├── Container.cs │ ├── docker-compose.yml │ ├── README.md │ ├── App.cs │ ├── KafkaModule.cs │ └── KafkaConsumer.cs ├── AspNetCore │ ├── Services │ │ ├── IWeatherSummarizer.cs │ │ ├── IDatabase.cs │ │ ├── IUsersCache.cs │ │ ├── IWeatherForecastProvider.cs │ │ ├── User.cs │ │ ├── WeatherSummarizer.cs │ │ ├── MockDatabase.cs │ │ ├── DatabaseUsersCache.cs │ │ ├── WeatherForecastProvider.cs │ │ └── DatabaseDecorator.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── StrongInject.Samples.AspNetCore.csproj │ ├── WeatherForecast.cs │ ├── Program.cs │ ├── Controllers │ │ ├── UsersController.cs │ │ └── WeatherForecastController.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Container.cs │ ├── README.md │ └── Startup.cs └── Wpf │ ├── Models │ ├── User.cs │ ├── IDatabase.cs │ └── MockDatabase.cs │ ├── App.xaml │ ├── ViewModels │ ├── MainWindowViewModel.cs │ ├── UserViewModel.cs │ └── UsersViewModel.cs │ ├── Container.cs │ ├── StrongInject.Samples.Wpf.csproj │ ├── MainWindow.xaml.cs │ ├── AssemblyInfo.cs │ ├── Views │ ├── UsersView.xaml.cs │ └── UsersView.xaml │ ├── MainWindow.xaml │ ├── App.xaml.cs │ ├── Commands │ └── RelayCommand.cs │ └── README.md ├── NuGet.config ├── StrongInject ├── StrongInjectException.cs ├── Modules │ ├── LazyModule.cs │ ├── StandardModule.cs │ ├── SafeImmutableArrayModule.cs │ ├── CollectionsModule.cs │ ├── UnsafeImmutableArrayModule.cs │ └── ValueTupleModule.cs ├── ObsoleteTypes │ ├── RegistrationAttribute.cs │ ├── ModuleRegistrationAtribute.cs │ └── FactoryRegistrationAttribute.cs ├── DummyParameter.cs ├── ValueTaskExtensions.cs ├── buildTransitive │ └── StrongInject.targets ├── DecoratorOptions.cs ├── IRequiresInitialization.cs ├── InstanceAttribute.cs ├── RegisterModuleAttribute.cs ├── DecoratorFactoryAttribute.cs ├── Helpers.cs ├── FactoryOfAttribute.cs ├── IContainer.cs ├── Scope.cs ├── RegisterFactoryAttribute.cs ├── StrongInject.csproj ├── FactoryAttribute.cs ├── IFactory.cs ├── RegisterDecoratorAttribute.cs └── Owned.cs ├── .github └── workflows │ ├── toc.yml │ └── wiki.yml ├── StrongInject.Tests.Unit ├── AssertionExtensions.cs ├── RoslynExtensions.cs ├── StrongInject.Tests.Unit.csproj └── OwnedTests.cs ├── Utilities └── GeneratorTestsUpdater │ └── GeneratorTestsUpdater.csproj ├── StrongInject.Generator.Roslyn38 └── StrongInject.Generator.Roslyn38.csproj ├── StrongInject.Generator.Roslyn40 └── StrongInject.Generator.Roslyn40.csproj ├── StrongInject.Extensions.DependencyInjection.AspNetCore ├── StrongInject.Extensions.DependencyInjection.AspNetCore.csproj └── MvcBuilderExtensions.cs ├── StrongInject.Extensions.DependencyInjection └── StrongInject.Extensions.DependencyInjection.csproj ├── LICENSE ├── StrongInject.Extensions.DependencyInjection.Tests └── StrongInject.Extensions.DependencyInjection.Tests.csproj ├── Directory.Build.props └── docs ├── Registration ├── BestRegistration.md ├── Decorators.md ├── Scopes.md └── FactoryMethodRegistration.md └── Containers.md /resources/logo-circle.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/resources/logo-circle.ico -------------------------------------------------------------------------------- /resources/logo-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/resources/logo-circle.png -------------------------------------------------------------------------------- /resources/logo-horizontal.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/resources/logo-horizontal.ico -------------------------------------------------------------------------------- /resources/logo-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/resources/logo-horizontal.png -------------------------------------------------------------------------------- /StrongInject.Generator/IsExternalInit.cs: -------------------------------------------------------------------------------- 1 | namespace System.Runtime.CompilerServices { internal class IsExternalInit { } } 2 | -------------------------------------------------------------------------------- /StrongInject.Tests.Integration/IsExternalInit.cs: -------------------------------------------------------------------------------- 1 | namespace System.Runtime.CompilerServices { internal class IsExternalInit { } } -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using Xamarin.Forms.Xaml; 2 | 3 | [assembly: XamlCompilation(XamlCompilationOptions.Compile)] -------------------------------------------------------------------------------- /Samples/Console/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "BootstrapServers": "localhost:29092", 3 | "GroupId": "StrongInJectKafkaConsumer", 4 | "TargetTopicPrefix": "inbox_", 5 | "ConsumedTopic": "all_messages" 6 | } -------------------------------------------------------------------------------- /StrongInject.Generator/DisposalStyleDeterminant.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Generator 2 | { 3 | internal enum DisposalStyleDeterminant 4 | { 5 | Container, 6 | OwnedType, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Samples/AspNetCore/Services/IWeatherSummarizer.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Samples.AspNetCore.Services 2 | { 3 | public interface IWeatherSummarizer 4 | { 5 | string Summarize(int temperatureC); 6 | } 7 | } -------------------------------------------------------------------------------- /StrongInject.Generator/FactoryOfMethod.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace StrongInject.Generator 4 | { 5 | internal record FactoryOfMethod(FactoryMethod Underlying, ITypeSymbol FactoryOfType); 6 | } 7 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-hdpi/icon.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-mdpi/icon.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xhdpi/icon.png -------------------------------------------------------------------------------- /Samples/Console/IConfigLoader.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace StrongInject.Samples.ConsoleApp 4 | { 5 | public interface IConfigLoader 6 | { 7 | ValueTask LoadConfig(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/drawable/icon_about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/drawable/icon_about.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/drawable/icon_feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/drawable/icon_feed.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xxhdpi/icon.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xxxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xxxhdpi/icon.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/drawable/xamarin_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/drawable/xamarin_logo.png -------------------------------------------------------------------------------- /Samples/AspNetCore/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Samples/Console/Message.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Samples.ConsoleApp 2 | { 3 | public record Message 4 | { 5 | public string Content { get; init; } = null!; 6 | public User Recipient { get; init; } = null!; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Samples/Console/ICommitable.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Samples.ConsoleApp 2 | { 3 | public interface ICommitable 4 | { 5 | TKey Key { get; } 6 | TValue Value { get; } 7 | void Commit(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Samples/Console/IProducer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace StrongInject.Samples.ConsoleApp 4 | { 5 | public interface IProducer 6 | { 7 | public Task Produce(TKey key, TValue value); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Samples/Console/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject.Samples.ConsoleApp 4 | { 5 | public record User 6 | { 7 | public Guid Id { get; init; } 8 | public string Name { get; init; } = null!; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Samples/AspNetCore/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-hdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-hdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-mdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-mdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xxhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xxhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YairHalberstadt/stronginject/HEAD/Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Samples/Console/IConsumer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace StrongInject.Samples.ConsoleApp 4 | { 5 | public interface IConsumer 6 | { 7 | IAsyncEnumerable> Consume(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Samples/AspNetCore/Services/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace StrongInject.Samples.AspNetCore.Services 5 | { 6 | public interface IDatabase 7 | { 8 | public Task> Get(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Samples/AspNetCore/Services/IUsersCache.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace StrongInject.Samples.AspNetCore.Services 5 | { 6 | public interface IUsersCache 7 | { 8 | public ValueTask> GetUsersList(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Samples/AspNetCore/Services/IWeatherForecastProvider.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.AspNetCore; 2 | using System; 3 | 4 | namespace StrongInject.Samples.AspNetCore.Services 5 | { 6 | public interface IWeatherForecastProvider 7 | { 8 | WeatherForecast GetForecast(DateTime day); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Samples/Wpf/Models/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject.Samples.Wpf.Models 4 | { 5 | public record User(Guid Id, string FirstName, string LastName) 6 | { 7 | public static User CreateNew(string FirstName, string LastName) => new User(Guid.NewGuid(), FirstName, LastName); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Services/Browser.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace StrongInject.Samples.XamarinApp.Services 4 | { 5 | public class Browser : IBrowser 6 | { 7 | public Task OpenAsync(string uri) => Xamarin.Essentials.Browser.OpenAsync(uri); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /StrongInject/StrongInjectException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace StrongInject 6 | { 7 | public sealed class StrongInjectException : Exception 8 | { 9 | public StrongInjectException(string message) : base(message) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Services/IBrowser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace StrongInject.Samples.XamarinApp.Services 7 | { 8 | public interface IBrowser 9 | { 10 | Task OpenAsync(string uri); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-anydpi-v26/icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | #3F51B5 5 | #303F9F 6 | #FF4081 7 | 8 | -------------------------------------------------------------------------------- /Samples/Wpf/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/mipmap-anydpi-v26/icon_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Samples/Console/Config.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Samples.ConsoleApp 2 | { 3 | public class Config 4 | { 5 | public string BootstrapServers { get; init; } = ""; 6 | public string GroupId { get; init; } = ""; 7 | public string ConsumedTopic { get; init; } = ""; 8 | public string TargetTopicPrefix { get; init; } = ""; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Samples/Wpf/Models/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace StrongInject.Samples.Wpf.Models 6 | { 7 | public interface IDatabase 8 | { 9 | Task> GetUsers(); 10 | Task DeleteUser(Guid userId); 11 | Task AddOrUpdateUser(User user); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /StrongInject/Modules/LazyModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject.Modules 4 | { 5 | /// 6 | /// Provides a registration for . 7 | /// 8 | public static class LazyModule 9 | { 10 | [Factory(Scope.InstancePerResolution)] public static Lazy CreateLazy(Func func) => new Lazy(func); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /StrongInject/ObsoleteTypes/RegistrationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace StrongInject 5 | { 6 | [Obsolete("Use RegisterAttribute instead", error: true)] 7 | [EditorBrowsable(EditorBrowsableState.Never)] 8 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] 9 | public class RegistrationAttribute : Attribute 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Samples/AspNetCore/Services/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject.Samples.AspNetCore.Services 4 | { 5 | public class User 6 | { 7 | public User(string name, DateTime dateOfBirth) 8 | { 9 | Name = name; 10 | DateOfBirth = dateOfBirth; 11 | } 12 | 13 | public string Name { get; } 14 | 15 | public DateTime DateOfBirth { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /StrongInject/Modules/StandardModule.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Modules 2 | { 3 | /// 4 | /// Provides registrations for the most common and least opinionated inbuilt modules. 5 | /// 6 | [RegisterModule(typeof(CollectionsModule))] 7 | [RegisterModule(typeof(LazyModule))] 8 | [RegisterModule(typeof(ValueTupleModule))] 9 | public class StandardModule 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /StrongInject/DummyParameter.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace StrongInject.Internal 4 | { 5 | /// 6 | /// A class with no possible value other than null. Used to mark an optional parameter which should never be set. 7 | /// 8 | [EditorBrowsable(EditorBrowsableState.Never)] 9 | public sealed class DummyParameter 10 | { 11 | private DummyParameter() { } 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /StrongInject/ObsoleteTypes/ModuleRegistrationAtribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace StrongInject 5 | { 6 | [Obsolete("Use RegisterModuleAttribute instead", error: true)] 7 | [EditorBrowsable(EditorBrowsableState.Never)] 8 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] 9 | public class ModuleRegistrationAttribute : Attribute 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Samples/Console/JsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka; 2 | using System; 3 | using System.Text.Json; 4 | 5 | namespace StrongInject.Samples.ConsoleApp 6 | { 7 | public class JsonSerializer : ISerializer 8 | { 9 | public byte[] Serialize(T? data, SerializationContext context) 10 | { 11 | return data is null ? Array.Empty() : JsonSerializer.SerializeToUtf8Bytes(data); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /StrongInject/ObsoleteTypes/FactoryRegistrationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace StrongInject 5 | { 6 | [Obsolete("Use RegisterFactoryAttribute instead", error: true)] 7 | [EditorBrowsable(EditorBrowsableState.Never)] 8 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] 9 | public class FactoryRegistrationAttribute : Attribute 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /StrongInject.Generator/DisposalStyle.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Generator 2 | { 3 | internal readonly struct DisposalStyle 4 | { 5 | public DisposalStyle(bool isAsync, DisposalStyleDeterminant determinant) 6 | { 7 | IsAsync = isAsync; 8 | Determinant = determinant; 9 | } 10 | 11 | public bool IsAsync { get; } 12 | public DisposalStyleDeterminant Determinant { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Samples/AspNetCore/StrongInject.Samples.AspNetCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Debug;Release;Wpf;All 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/AboutPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.ViewModels; 2 | using Xamarin.Forms; 3 | 4 | namespace StrongInject.Samples.XamarinApp.Views 5 | { 6 | public partial class AboutPage : ContentPage 7 | { 8 | public AboutPage(AboutViewModel aboutViewModel) 9 | { 10 | InitializeComponent(); 11 | BindingContext = aboutViewModel; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /.github/workflows/toc.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | name: TOC Generator 6 | jobs: 7 | generateTOC: 8 | name: TOC Generator 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: technote-space/toc-generator@v2 12 | with: 13 | TOC_TITLE: | 14 | ## Table Of Contents 15 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 16 | TARGET_PATHS: README.md, docs/**.md, docs/*/**.md 17 | 18 | -------------------------------------------------------------------------------- /Samples/AspNetCore/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject.Samples.AspNetCore 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string? Summary { get; set; } 14 | 15 | public string? Location { get; internal set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/layout/Toolbar.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /Samples/Console/JsonDeserializer.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka; 2 | using System; 3 | using System.Text.Json; 4 | 5 | namespace StrongInject.Samples.ConsoleApp 6 | { 7 | public class JsonDeserializer : IDeserializer 8 | { 9 | public T? Deserialize(ReadOnlySpan data, bool isNull, SerializationContext context) 10 | { 11 | if (isNull) 12 | return default; 13 | return JsonSerializer.Deserialize(data); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Samples/Wpf/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace StrongInject.Samples.Wpf.ViewModels 8 | { 9 | public class MainWindowViewModel 10 | { 11 | public MainWindowViewModel(UsersViewModel usersViewModel) 12 | { 13 | UsersViewModel = usersViewModel; 14 | } 15 | 16 | public UsersViewModel UsersViewModel { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/ItemDetailPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.ViewModels; 2 | using Xamarin.Forms; 3 | 4 | namespace StrongInject.Samples.XamarinApp.Views 5 | { 6 | public partial class ItemDetailPage : ContentPage, IViewOf 7 | { 8 | public ItemDetailPage(ItemDetailViewModel itemDetailViewModel) 9 | { 10 | InitializeComponent(); 11 | BindingContext = itemDetailViewModel; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Models/Item.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject.Samples.XamarinApp.Models 4 | { 5 | public class Item 6 | { 7 | public Item(string id, string text, string description) 8 | { 9 | Id = id; 10 | Text = text; 11 | Description = description; 12 | } 13 | 14 | public string Id { get; set; } 15 | public string Text { get; set; } 16 | public string Description { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /.github/workflows/wiki.yml: -------------------------------------------------------------------------------- 1 | name: Wiki 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | run: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v1 13 | # Additional steps to generate documentation in "Documentation" directory 14 | - name: Upload Documentation to Wiki 15 | uses: SwiftDocOrg/github-wiki-publish-action@rsync 16 | with: 17 | path: "docs/" 18 | env: 19 | GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} 20 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Services/IDataStore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace StrongInject.Samples.XamarinApp.Services 5 | { 6 | public interface IDataStore 7 | { 8 | Task AddItemAsync(T item); 9 | Task UpdateItemAsync(T item); 10 | Task DeleteItemAsync(string id); 11 | Task GetItemAsync(string id); 12 | Task> GetItemsAsync(bool forceRefresh = false); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Samples/Wpf/Container.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.Wpf.Models; 2 | using StrongInject.Samples.Wpf.ViewModels; 3 | 4 | namespace StrongInject.Samples.Wpf 5 | { 6 | [Register(typeof(MainWindow))] 7 | [Register(typeof(MainWindowViewModel))] 8 | [Register(typeof(UsersViewModel))] 9 | [Register(typeof(UserViewModel), Scope.InstancePerDependency)] 10 | [Register(typeof(MockDatabase), Scope.SingleInstance, typeof(IDatabase))] 11 | public partial class Container : IAsyncContainer 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/IViewOf.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.ViewModels; 2 | using Xamarin.Forms; 3 | 4 | namespace StrongInject.Samples.XamarinApp.Views 5 | { 6 | /// 7 | /// Should only be implemented by a page. 8 | /// 9 | /// 10 | public interface IViewOf : ILayout, IPageController, IVisualElementController, IElementController, IElementConfiguration where T : BaseViewModel 11 | { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /StrongInject/ValueTaskExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace StrongInject 4 | { 5 | internal static class ValueTaskExtensions 6 | { 7 | public static ValueTask AsValueTask(this ValueTask valueTask) 8 | { 9 | if (valueTask.IsCompletedSuccessfully) 10 | { 11 | valueTask.GetAwaiter().GetResult(); 12 | return default; 13 | } 14 | 15 | return new ValueTask(valueTask.AsTask()); 16 | } 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /StrongInject.Tests.Unit/AssertionExtensions.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using FluentAssertions.Primitives; 3 | 4 | namespace StrongInject.Generator.Tests.Unit 5 | { 6 | public static class AssertionExtensions 7 | { 8 | public static AndConstraint BeIgnoringLineEndings(this StringAssertions stringAssertions, string expected, string because = "", params object[] becauseArgs) 9 | { 10 | return stringAssertions.Subject.Replace("\r\n", "\n").Should().Be(expected.Replace("\r\n", "\n")); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Console/Cache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | 4 | namespace StrongInject.Samples.ConsoleApp 5 | { 6 | public class Cache where TKey : notnull 7 | { 8 | private readonly Func _factory; 9 | 10 | public Cache(Func factory) 11 | { 12 | _factory = factory; 13 | } 14 | 15 | private readonly ConcurrentDictionary _cached = new(); 16 | public TValue Get(TKey key) => _cached.GetOrAdd(key, _factory); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Samples/Wpf/StrongInject.Samples.Wpf.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net6.0-windows 6 | true 7 | false 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/LoginPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.ViewModels; 2 | using Xamarin.Forms; 3 | using Xamarin.Forms.Xaml; 4 | 5 | namespace StrongInject.Samples.XamarinApp.Views 6 | { 7 | [XamlCompilation(XamlCompilationOptions.Compile)] 8 | public partial class LoginPage : ContentPage, IViewOf 9 | { 10 | public LoginPage(LoginViewModel loginViewModel) 11 | { 12 | InitializeComponent(); 13 | BindingContext = loginViewModel; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Samples/Console/JsonConfigLoader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text.Json; 3 | using System.Threading.Tasks; 4 | 5 | namespace StrongInject.Samples.ConsoleApp 6 | { 7 | public class JsonConfigLoader : IConfigLoader 8 | { 9 | public async ValueTask LoadConfig() 10 | { 11 | await using (var fileStream = File.OpenRead("config.json")) 12 | { 13 | return await JsonSerializer.DeserializeAsync(fileStream) ?? throw new JsonException("Invalid Config"); 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Samples/Wpf/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.Wpf.ViewModels; 2 | using System.Windows; 3 | 4 | namespace StrongInject.Samples.Wpf 5 | { 6 | /// 7 | /// Interaction logic for MainWindow.xaml 8 | /// 9 | public partial class MainWindow : Window 10 | { 11 | public MainWindow(MainWindowViewModel mainWindowViewModel) 12 | { 13 | MainWindowViewModel = mainWindowViewModel; 14 | InitializeComponent(); 15 | } 16 | 17 | public MainWindowViewModel MainWindowViewModel { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/NewItemPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.Models; 2 | using StrongInject.Samples.XamarinApp.ViewModels; 3 | using Xamarin.Forms; 4 | 5 | namespace StrongInject.Samples.XamarinApp.Views 6 | { 7 | public partial class NewItemPage : ContentPage, IViewOf 8 | { 9 | public Item? Item { get; set; } 10 | 11 | public NewItemPage(NewItemViewModel newItemViewModel) 12 | { 13 | InitializeComponent(); 14 | BindingContext = newItemViewModel; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/ViewModels/AboutViewModel.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.Services; 2 | using System.Windows.Input; 3 | using Xamarin.Forms; 4 | 5 | namespace StrongInject.Samples.XamarinApp.ViewModels 6 | { 7 | public class AboutViewModel : BaseViewModel 8 | { 9 | public AboutViewModel(IBrowser browser) 10 | { 11 | Title = "About"; 12 | OpenWebCommand = new Command(async () => await browser.OpenAsync("https://aka.ms/xamarin-quickstart")); 13 | } 14 | 15 | public ICommand OpenWebCommand { get; } 16 | } 17 | } -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/layout/Tabbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /Samples/Wpf/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /StrongInject.Generator/Disposal.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace StrongInject.Generator 4 | { 5 | internal abstract record Disposal(bool IsAsync) 6 | { 7 | internal sealed record IDisposable(string VariableName, bool IsAsync) : Disposal(IsAsync); 8 | internal sealed record DisposalHelpers(string VariableName, bool IsAsync) : Disposal(IsAsync); 9 | internal sealed record FactoryDisposal(string VariableName, string FactoryName, bool IsAsync) : Disposal(IsAsync); 10 | internal sealed record DelegateDisposal(string DisposeActionsName, string DisposeActionsTypeName, bool IsAsync) : Disposal(IsAsync); 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/ItemsPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.ViewModels; 2 | using Xamarin.Forms; 3 | 4 | namespace StrongInject.Samples.XamarinApp.Views 5 | { 6 | public partial class ItemsPage : ContentPage 7 | { 8 | readonly ItemsViewModel _viewModel; 9 | 10 | public ItemsPage(ItemsViewModel itemsViewModel) 11 | { 12 | InitializeComponent(); 13 | BindingContext = _viewModel = itemsViewModel; 14 | } 15 | 16 | protected override void OnAppearing() 17 | { 18 | base.OnAppearing(); 19 | _viewModel.OnAppearing(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /StrongInject/Modules/SafeImmutableArrayModule.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | 3 | namespace StrongInject.Modules 4 | { 5 | /// 6 | /// Provides a registration for . 7 | /// 8 | /// This copies the resolved array into an . 9 | /// If you require a non-copying implementation for performance, use instead. 10 | /// 11 | public static class SafeImmutableArrayModule 12 | { 13 | [Factory(Scope.InstancePerDependency)] public static ImmutableArray CreateImmutableArray(T[] arr) => arr.ToImmutableArray(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/ItemDetailPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /StrongInject/buildTransitive/StrongInject.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /StrongInject/DecoratorOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject 4 | { 5 | /// 6 | /// Provides options to configure a decorator registration 7 | /// 8 | [Flags] 9 | public enum DecoratorOptions : long 10 | { 11 | Default = 0, 12 | 13 | /// 14 | /// Dispose the value returned by this decorator. 15 | /// 16 | /// Apply this only if the decorator itself needs disposal. 17 | /// The decorator should never dispose the underlying instance. 18 | /// If a decorator factory returns the same instance as was passed in, this must be false. 19 | /// 20 | Dispose = 1L << 1, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Samples/AspNetCore/Services/WeatherSummarizer.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Samples.AspNetCore.Services 2 | { 3 | public class WeatherSummarizer : IWeatherSummarizer 4 | { 5 | public string Summarize(int temperatureC) 6 | { 7 | return temperatureC switch 8 | { 9 | < 0 => "Freezing", 10 | < 5 => "Bracing", 11 | < 10 => "Chilly", 12 | < 15 => "Cool", 13 | < 20 => "Mild", 14 | < 25 => "Warm", 15 | < 30 => "Balmy", 16 | < 35 => "Hot", 17 | < 40 => "Sweltering", 18 | _ => "Scorching", 19 | }; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /StrongInject/IRequiresInitialization.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace StrongInject 4 | { 5 | /// 6 | /// Implement this interface to inform StrongInject that a type will need initialization once it is instantiated before it is ready to be used. 7 | /// 8 | public interface IRequiresInitialization 9 | { 10 | void Initialize(); 11 | } 12 | 13 | /// 14 | /// Implement this interface to inform StrongInject that a type will need asynchronous initialization once it is instantiated before it is ready to be used. 15 | /// 16 | public interface IRequiresAsyncInitialization 17 | { 18 | ValueTask InitializeAsync(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /StrongInject.Generator/DecoratorOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject.Generator 4 | { 5 | /// 6 | /// Provides options to configure a decorator registration 7 | /// 8 | [Flags] 9 | public enum DecoratorOptions : long 10 | { 11 | Default = 0, 12 | 13 | /// 14 | /// Dispose the value returned by this decorator. 15 | /// 16 | /// Apply this only if the decorator itself needs disposal. 17 | /// The decorator should never dispose the underlying instance. 18 | /// If a decorator factory returns the same instance as was passed in, this must be false. 19 | /// 20 | Dispose = 1L << 1, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /StrongInject.Tests.Integration/TestNullableConversion.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xunit; 3 | 4 | namespace StrongInject.Tests.Integration 5 | { 6 | public partial class TestNullableConversion 7 | { 8 | public struct B { } 9 | public struct A 10 | { 11 | public B? B { get; } 12 | public A(B? b) => B = b; 13 | } 14 | 15 | 16 | [Register(typeof(B), typeof(B?))] 17 | [Register(typeof(A))] 18 | public partial class Container : IAsyncContainer 19 | { 20 | } 21 | 22 | [Fact] 23 | public async Task Test() 24 | { 25 | await new Container().RunAsync(x => Assert.NotNull(x.B)); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.Services; 2 | using Xamarin.Forms; 3 | 4 | namespace StrongInject.Samples.XamarinApp 5 | { 6 | public partial class App : Application 7 | { 8 | 9 | public App() 10 | { 11 | InitializeComponent(); 12 | var container = new Container(); 13 | var appShellOwned = container.Resolve(); 14 | MainPage = appShellOwned.Value; 15 | } 16 | 17 | protected override void OnStart() 18 | { 19 | } 20 | 21 | protected override void OnSleep() 22 | { 23 | } 24 | 25 | protected override void OnResume() 26 | { 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Utilities/GeneratorTestsUpdater/GeneratorTestsUpdater.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | Debug;Release;Wpf;All 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | TargetFramework=netstandard2.0 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Samples/Console/KafkaProducer.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka; 2 | using System.Threading.Tasks; 3 | 4 | namespace StrongInject.Samples.ConsoleApp 5 | { 6 | public class KafkaProducer : IProducer 7 | { 8 | private readonly Confluent.Kafka.IProducer _producer; 9 | private readonly string _topic; 10 | 11 | public KafkaProducer(Confluent.Kafka.IProducer producer, string topic) 12 | { 13 | _producer = producer; 14 | _topic = topic; 15 | } 16 | 17 | public Task Produce(TKey key, TValue value) 18 | { 19 | return _producer.ProduceAsync(_topic, new Message() { Key = key, Value = value }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Assets/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories) and given a Build Action of "AndroidAsset". 3 | 4 | These files will be deployed with your package and will be accessible using Android's 5 | AssetManager, like this: 6 | 7 | public class ReadAsset : Activity 8 | { 9 | protected override void OnCreate (Bundle bundle) 10 | { 11 | base.OnCreate (bundle); 12 | 13 | InputStream input = Assets.Open ("my_asset.txt"); 14 | } 15 | } 16 | 17 | Additionally, some Android functions will automatically load asset files: 18 | 19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); 20 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Services/INavigationService.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.ViewModels; 2 | using System.Threading.Tasks; 3 | 4 | namespace StrongInject.Samples.XamarinApp.Services 5 | { 6 | public interface INavigationService 7 | { 8 | Task PopAsync(); 9 | Task GoToAsync(string route); 10 | } 11 | 12 | public interface INavigationService : INavigationService where T : BaseViewModel 13 | { 14 | Task PushAsync(); 15 | Task PopAllAndPushAsync(); 16 | } 17 | 18 | public interface IParameterizedNavigationService : INavigationService where T : BaseViewModel 19 | { 20 | Task PushAsync(T viewModel); 21 | Task PopAllAndPushAsync(T viewModel); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /StrongInject.Generator/StrongInject.Generator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | Debug;Release;Wpf;All 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /StrongInject.Tests.Unit/RoslynExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit; 6 | 7 | namespace StrongInject.Generator.Tests.Unit 8 | { 9 | public static class RoslynExtensions 10 | { 11 | public static INamedTypeSymbol AssertGetTypeByMetadataName(this Compilation compilation, string name) 12 | { 13 | var type = compilation.GetTypeByMetadataName(name); 14 | Assert.NotNull(type); 15 | return type!; 16 | } 17 | 18 | public static void Verify( 19 | this IEnumerable diagnostics, 20 | params DiagnosticResult[] expected) => DiagnosticVerifier.VerifyDiagnostics(diagnostics, expected); 21 | } 22 | } -------------------------------------------------------------------------------- /StrongInject/Modules/CollectionsModule.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.InteropServices.ComTypes; 3 | 4 | namespace StrongInject.Modules 5 | { 6 | /// 7 | /// Provides registrations for , and . 8 | /// 9 | public static class CollectionsModule 10 | { 11 | [Factory(Scope.InstancePerDependency)] public static IEnumerable CreateEnumerable(T[] arr) => arr; 12 | [Factory(Scope.InstancePerDependency)] public static IReadOnlyList CreateReadOnlyList(T[] arr) => arr; 13 | [Factory(Scope.InstancePerDependency)] public static IReadOnlyCollection CreateReadOnlyCollection(T[] arr) => arr; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /resources/logo-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Samples/Wpf/Views/UsersView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace StrongInject.Samples.Wpf.Views 17 | { 18 | /// 19 | /// Interaction logic for UsersView.xaml 20 | /// 21 | public partial class UsersView : UserControl 22 | { 23 | public UsersView() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Samples/Console/StrongInject.Samples.ConsoleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | Debug;Release;Wpf;All 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | PreserveNewest 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Samples/Wpf/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/StrongInject.Samples.XamarinApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | Debug;Release;AndroidApp;Wpf;All 7 | 10.0.17763.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/LoginPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /StrongInject.Tests.Unit/StrongInject.Tests.Unit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | preview 6 | enable 7 | Debug;Release;Wpf;All 8 | StrongInject.Generator.Tests.Unit 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /StrongInject/IContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Threading.Tasks; 4 | 5 | namespace StrongInject 6 | { 7 | /// 8 | /// Implement this interface to tell StrongInject to generate implementations for and . 9 | /// You can implement this interface multiple times for different values of , and Single Instances will be shared. 10 | /// 11 | /// 12 | public interface IContainer : IDisposable 13 | { 14 | [EditorBrowsable(EditorBrowsableState.Never)] 15 | TResult Run(Func func, TParam param); 16 | Owned Resolve(); 17 | } 18 | 19 | /// Implement this interface to tell StrongInject to generate implementations for and . 20 | /// You can implement this interface multiple times for different values of , and Single Instances will be shared. 21 | public interface IAsyncContainer : IAsyncDisposable 22 | { 23 | [EditorBrowsable(EditorBrowsableState.Never)] 24 | ValueTask RunAsync(Func> func, TParam param); 25 | ValueTask> ResolveAsync(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /StrongInject/Scope.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject 2 | { 3 | public enum Scope 4 | { 5 | /// 6 | /// Default scope. 7 | /// A single instance is shared between all dependencies created for a single resolution. 8 | /// For example if 'A' depends on 'B' and 'C', and 'B' and 'C' both depend on an instance of 'D', 9 | /// then when 'A' is resolved 'B' and 'C' will share the same instance of 'D'. 10 | /// 11 | /// Note every SingleInstance dependency defines a separate resolution, 12 | /// so if 'B' and/or 'C' are SingleInstance they would not share an instance of 'D'. 13 | /// 14 | /// Similarly every lambda defines a separate resolution, so if A depends on Func<B>, 15 | /// then each time Func<B> is invoked a fresh instance of both B and D will be created. 16 | /// 17 | InstancePerResolution = 0, 18 | 19 | /// 20 | /// A new instance is created for every usage. 21 | /// For example even if type 'B' appears twice in the constructor of 'A', 22 | /// two different instances will be passed into the constructor. 23 | /// 24 | InstancePerDependency = 1, 25 | 26 | /// 27 | /// A single instance will be shared across all dependencies, from any resolution 28 | /// 29 | SingleInstance = 2, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /StrongInject.Generator/Scope.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Generator 2 | { 3 | public enum Scope 4 | { 5 | /// 6 | /// Default scope. 7 | /// A single instance is shared between all dependencies created for a single resolution. 8 | /// For example if 'A' depends on 'B' and 'C', and 'B' and 'C' both depend on an instance of 'D', 9 | /// then when 'A' is resolved 'B' and 'C' will share the same instance of 'D'. 10 | /// 11 | /// Note every SingleInstance dependency defines a separate resolution, 12 | /// so if 'B' and/or 'C' are SingleInstance they would not share an instance of 'D'. 13 | /// 14 | /// Similarly every lambda defines a separate resolution, so if A depends on Func<B>, 15 | /// then each time Func<B> is invoked a fresh instance of both B and D will be created. 16 | /// 17 | InstancePerResolution = 0, 18 | 19 | /// 20 | /// A new instance is created for every usage. 21 | /// For example even if type 'B' appears twice in the constructor of 'A', 22 | /// two different instances will be passed into the constructor. 23 | /// 24 | InstancePerDependency = 1, 25 | 26 | /// 27 | /// A single instance will be shared across all dependencies, from any resolution 28 | /// 29 | SingleInstance = 2, 30 | } 31 | } -------------------------------------------------------------------------------- /StrongInject.Tests.Integration/TestFuncScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Xunit; 4 | 5 | namespace StrongInject.Tests.Integration 6 | { 7 | public partial class TestFuncScope 8 | { 9 | public record A(B b, C c, E e) { } 10 | public record B(D d) { } 11 | public record C(D d) { } 12 | public record D { } 13 | public record E { } 14 | 15 | [Register(typeof(E), Scope.SingleInstance)] 16 | [Register(typeof(D), Scope.InstancePerDependency)] 17 | [Register(typeof(C))] 18 | [Register(typeof(B))] 19 | [Register(typeof(A))] 20 | public partial class Container : IContainer> 21 | { 22 | } 23 | 24 | [Fact] 25 | public void Test() 26 | { 27 | var container = new Container(); 28 | var e1 = container.Run(aF => 29 | { 30 | var a1 = aF(); 31 | Assert.NotSame(a1.b.d, a1.c.d); 32 | var a2 = aF(); 33 | Assert.NotSame(a2.b.d, a2.c.d); 34 | Assert.NotSame(a1, a2); 35 | Assert.NotSame(a1.b, a2.b); 36 | Assert.NotSame(a1.c, a2.c); 37 | Assert.NotSame(a1.b.d, a2.b.d); 38 | Assert.Same(a1.e, a2.e); 39 | return a1.e; 40 | }); 41 | var e2 = container.Run(aF => aF().e); 42 | Assert.Same(e1, e2); 43 | 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /StrongInject.Extensions.DependencyInjection.Tests/StrongInject.Extensions.DependencyInjection.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | preview 6 | enable 7 | true 8 | Debug;Release;Wpf;All 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/GettingStarted.txt: -------------------------------------------------------------------------------- 1 | Welcome to Xamarin.Forms! Here are some tips to get started building your app. 2 | 3 | Building Your App UI 4 | -------------------- 5 | 6 | XAML Hot Reload quickly applies UI changes as you make them to your running app. 7 | This is the most productive way to preview and iteratively create your UI. 8 | 9 | Try it out: 10 | 11 | 1. Run the app by clicking the Start Debugging (play) button in the above toolbar. 12 | 2. Open \Views\AboutPage.xaml. 13 | Don't stop the app - keep it running while making changes. 14 | 3. Change something! Hint: change the Accent color on line 14 from "#96d1ff" to "Pink". Save the file. 15 | 4. Watch the About screen update on the device or emulator, with the logo background now pink. 16 | 17 | Keep going and try more changes! 18 | 19 | QuickStart Guide 20 | ---------------- 21 | 22 | Learn more of the fundamentals for building apps with Xamarin here: https://aka.ms/xamarin-quickstart 23 | 24 | Your App Shell 25 | -------------- 26 | 27 | This template uses Shell, an app container that reduces the complexity of your apps by providing fundamental features including: 28 | 29 | - A single place to describe the app's visual hierarchy. 30 | - Common navigation such as a flyout menu and tabs. 31 | - A URI-based navigation scheme that permits navigation to any page in the application. 32 | - An integrated search handler. 33 | 34 | Open AppShell.xaml to begin exploring. To learn more about Shell visit: https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/shell/introduction 35 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Android.App; 4 | using Android.Content.PM; 5 | using Android.Runtime; 6 | using Android.Views; 7 | using Android.Widget; 8 | using Android.OS; 9 | 10 | #nullable enable 11 | 12 | namespace StrongInject.Samples.XamarinApp.Droid 13 | { 14 | [Activity(Label = "StrongInject.Samples.XamarinApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )] 15 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity 16 | { 17 | protected override void OnCreate(Bundle savedInstanceState) 18 | { 19 | TabLayoutResource = Resource.Layout.Tabbar; 20 | ToolbarResource = Resource.Layout.Toolbar; 21 | 22 | base.OnCreate(savedInstanceState); 23 | 24 | Xamarin.Essentials.Platform.Init(this, savedInstanceState); 25 | global::Xamarin.Forms.Forms.Init(this, savedInstanceState); 26 | LoadApplication(new App()); 27 | } 28 | public override void OnRequestPermissionsResult(int requestCode, string[]? permissions, [GeneratedEnum] Android.Content.PM.Permission[]? grantResults) 29 | { 30 | Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); 31 | 32 | base.OnRequestPermissionsResult(requestCode, permissions, grantResults); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /StrongInject.Generator/Visitors/RequiresUnsafeVisitor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.Threading; 3 | 4 | namespace StrongInject.Generator.Visitors 5 | { 6 | internal class RequiresUnsafeVisitor : SimpleVisitor 7 | { 8 | private bool _requiresUnsafe = false; 9 | 10 | private RequiresUnsafeVisitor(InstanceSourcesScope containerScope, CancellationToken cancellationToken) : base(containerScope, cancellationToken) 11 | { 12 | } 13 | 14 | public static bool RequiresUnsafe(ITypeSymbol target, InstanceSourcesScope containerScope, CancellationToken cancellationToken) 15 | { 16 | var visitor = new RequiresUnsafeVisitor(containerScope, cancellationToken); 17 | var state = new State(containerScope); 18 | visitor.VisitCore(visitor.GetInstanceSource(target, state, parameterSymbol: null), state); 19 | return visitor._requiresUnsafe; 20 | } 21 | 22 | protected override bool ShouldVisitBeforeUpdateState(InstanceSource? source, State state) 23 | { 24 | if (source is null) 25 | return false; 26 | if (IsUnsafeType(source.OfType)) 27 | { 28 | _requiresUnsafe = true; 29 | ExitFast(); 30 | return false; 31 | } 32 | return base.ShouldVisitBeforeUpdateState(source, state); 33 | } 34 | 35 | private static bool IsUnsafeType(ITypeSymbol type) => type.IsPointerOrFunctionPointer() || type is IArrayTypeSymbol { ElementType: var elementType } && IsUnsafeType(elementType); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Samples/Wpf/Models/MockDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Documents; 9 | 10 | namespace StrongInject.Samples.Wpf.Models 11 | { 12 | public class MockDatabase : IDatabase 13 | { 14 | private readonly ConcurrentDictionary _users = new ConcurrentDictionary(new [] 15 | { 16 | User.CreateNew("Erik", "Hawkins"), 17 | User.CreateNew("Martine", "Guevara"), 18 | User.CreateNew("Keith", "Lin"), 19 | User.CreateNew("Jenson", "Fernandez"), 20 | User.CreateNew("Lynden", "Lancaster"), 21 | User.CreateNew("Shahid", "Fraser"), 22 | User.CreateNew("Blair", "Day"), 23 | User.CreateNew("Sofie", "Workman"), 24 | User.CreateNew("Abi", "Galloway"), 25 | User.CreateNew("Kean", "Rutledge"), 26 | }.Select(x => KeyValuePair.Create(x.Id, x))); 27 | 28 | public async Task AddOrUpdateUser(User user) 29 | { 30 | await Task.Yield(); 31 | _users.AddOrUpdate(user.Id, user, (_, _) => user); 32 | } 33 | 34 | public async Task DeleteUser(Guid userId) 35 | { 36 | await Task.Yield(); 37 | return _users.TryRemove(userId, out _); 38 | } 39 | 40 | public async Task> GetUsers() 41 | { 42 | await Task.Yield(); 43 | return _users.Values; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Samples/Wpf/README.md: -------------------------------------------------------------------------------- 1 | # StrongInject.Samples.Wpf 2 | 3 | ## Overview 4 | 5 | This sample uses StrongInject to instantiate a simple WPF application. 6 | 7 | It consists of a window showing a list of users which can be added to, edited, or deleted. 8 | 9 | ## Debugging 10 | 11 | The WPF app is not built by the Debug/Release configurations as it can only be built on windows. To debug the app change the configuration to WPF, and then run/debug as normal. 12 | 13 | ## Dependency Injection in WPF 14 | 15 | WPF classically expects user controls and view models to have a parameterless constructor, and then use a service locator instead of DI to access needed services. This is rightly [considered an anti pattern](https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/). 16 | 17 | Instead we instantiate a tree of view models using StrongInject. Each ViewModel accepts any child ViewModels directly in its constructor, or via a `Func`. Every `ViewModel` exposes the child ViewModels as properties. 18 | 19 | We resolve the `MainWindow` directly, and the `MainWindow` sets the `MainWindowViewModel` as its `DataContext` in its constructor. 20 | 21 | All UserControls use Data Binding to bind the `DataContext` of any child User Controls to the relevant ChildViewModel property on the ViewModel. 22 | 23 | ## Learning Points 24 | 25 | This app uses a relatively simple and straightforward StrongInject container. It's main learning points are how to use Dependency Injection in WPF to create all the ViewModels and services, and then bind them to the UserControls. 26 | 27 | Take a look at all the ViewModels and their constructors, and then take a look at how the DataContext is set for each Window/UserControl. 28 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/ViewModels/BaseViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace StrongInject.Samples.XamarinApp.ViewModels 7 | { 8 | public class BaseViewModel : INotifyPropertyChanged 9 | { 10 | bool _isBusy = false; 11 | public bool IsBusy 12 | { 13 | get { return _isBusy; } 14 | set { SetProperty(ref _isBusy, value); } 15 | } 16 | 17 | string _title = string.Empty; 18 | 19 | public string Title 20 | { 21 | get { return _title; } 22 | set { SetProperty(ref _title, value); } 23 | } 24 | 25 | protected bool SetProperty(ref T backingStore, T value, 26 | [CallerMemberName] string propertyName = "", 27 | Action? onChanged = null) 28 | { 29 | if (EqualityComparer.Default.Equals(backingStore, value)) 30 | return false; 31 | 32 | backingStore = value; 33 | onChanged?.Invoke(); 34 | OnPropertyChanged(propertyName); 35 | return true; 36 | } 37 | 38 | #region INotifyPropertyChanged 39 | public event PropertyChangedEventHandler? PropertyChanged; 40 | protected void OnPropertyChanged([CallerMemberName] string propertyName = "") 41 | { 42 | var changed = PropertyChanged; 43 | if (changed == null) 44 | return; 45 | 46 | changed.Invoke(this, new PropertyChangedEventArgs(propertyName)); 47 | } 48 | #endregion 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/App.xaml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 10 | #2196F3 11 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | preview 4 | enable 5 | true 6 | CS1591;RS1024 7 | AD0001 8 | true 9 | AllEnabledByDefault 10 | preview 11 | true 12 | 1.4.5 13 | *-* 14 | 15 | 16 | 17 | StrongInject 18 | StrongInject 19 | MIT 20 | https://github.com/YairHalberstadt/stronginject 21 | https://github.com/YairHalberstadt/stronginject 22 | true 23 | true 24 | snupkg 25 | logo-circle.png 26 | true 27 | 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | True 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Samples/Console/KafkaModule.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka; 2 | 3 | namespace StrongInject.Samples.ConsoleApp 4 | { 5 | public class KafkaModule 6 | { 7 | [Factory(Scope.SingleInstance)] public static ISerializer CreateSerializer() => new JsonSerializer(); 8 | [Factory(Scope.SingleInstance)] public static IDeserializer CreateDeserializer() => new JsonDeserializer(); 9 | [Factory] public static Confluent.Kafka.IConsumer CreateConfluentConsumer(IDeserializer keyDeserializer, IDeserializer valueDeserializer, Config config) 10 | { 11 | return new ConsumerBuilder(new ConsumerConfig() { BootstrapServers = config.BootstrapServers, GroupId = config.GroupId }) 12 | .SetKeyDeserializer(keyDeserializer) 13 | .SetValueDeserializer(valueDeserializer) 14 | .Build(); 15 | } 16 | [Factory] public static Confluent.Kafka.IProducer CreateConfluentProducer(ISerializer keySerializer, ISerializer valueSerializer, Config config) 17 | { 18 | return new ProducerBuilder(new ProducerConfig() { BootstrapServers = config.BootstrapServers }) 19 | .SetKeySerializer(keySerializer) 20 | .SetValueSerializer(valueSerializer) 21 | .Build(); 22 | } 23 | 24 | [Factory] public static IConsumer CreateConsumer(Confluent.Kafka.IConsumer consumer, Config config) => new KafkaConsumer(consumer, config.ConsumedTopic); 25 | [Factory] public static IProducer CreateProducer(Confluent.Kafka.IProducer producer, string topic) => new KafkaProducer(producer, topic); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Container.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Modules; 2 | using StrongInject.Samples.XamarinApp.Models; 3 | using StrongInject.Samples.XamarinApp.Services; 4 | using StrongInject.Samples.XamarinApp.ViewModels; 5 | using StrongInject.Samples.XamarinApp.Views; 6 | using System; 7 | using Xamarin.Forms; 8 | 9 | namespace StrongInject.Samples.XamarinApp 10 | { 11 | [Register(typeof(AppShell))] 12 | [Register(typeof(ItemsViewModel))] 13 | [Register(typeof(ItemsPage))] 14 | [Register(typeof(AboutViewModel))] 15 | [Register(typeof(AboutPage))] 16 | [Register(typeof(LoginViewModel))] 17 | [Register(typeof(LoginPage))] 18 | [Register(typeof(ItemDetailViewModel))] 19 | [Register(typeof(ItemDetailPage), typeof(IViewOf))] 20 | [Register(typeof(NewItemViewModel))] 21 | [Register(typeof(NewItemPage), typeof(IViewOf))] 22 | [Register(typeof(NavigationService), Scope.SingleInstance, typeof(INavigationService))] 23 | [Register(typeof(MockDataStore), Scope.SingleInstance, typeof(IDataStore))] 24 | [Register(typeof(Browser), Scope.SingleInstance, typeof(IBrowser))] 25 | [RegisterModule(typeof(LazyModule))] 26 | public partial class Container : IContainer 27 | { 28 | [Factory(Scope.SingleInstance)] INavigationService CreateNavigationService(Lazy shell, Func> createView) where T : BaseViewModel 29 | => new NavigationService(shell, createView); 30 | 31 | [Factory(Scope.SingleInstance)] 32 | IParameterizedNavigationService CreateParameterizedNavigationService(Lazy shell, Func> createView) where T : BaseViewModel 33 | => new ParameterizedNavigationService(shell, createView); 34 | 35 | [Factory] Shell GetShell() => Shell.Current; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StrongInject.Tests.Integration/Modules/CollectionTests.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Modules; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Xunit; 5 | 6 | namespace StrongInject.Tests.Integration.Modules 7 | { 8 | public partial class CollectionTests 9 | { 10 | [RegisterModule(typeof(CollectionsModule))] 11 | [Register(typeof(A), typeof(I))] 12 | [Register(typeof(B), typeof(I))] 13 | [Register(typeof(Collections))] 14 | public partial class Container : IContainer 15 | { 16 | 17 | } 18 | 19 | public record Collections( 20 | IEnumerable Enumerable1, 21 | IEnumerable Enumerable2, 22 | IReadOnlyList List1, 23 | IReadOnlyList List2, 24 | IReadOnlyCollection Collection1, 25 | IReadOnlyCollection Collection2) { } 26 | 27 | public interface I { } 28 | public class A : I { } 29 | public class B : I { } 30 | 31 | [Fact] 32 | public void TestCanResolveCollections() 33 | { 34 | var container = new Container(); 35 | using var scope = container.Resolve(); 36 | var collections = scope.Value; 37 | Assert.Equal(2, collections.Enumerable1.Count()); 38 | Assert.Equal(2, collections.Enumerable2.Count()); 39 | Assert.Equal(2, collections.List1.Count); 40 | Assert.Equal(2, collections.List2.Count); 41 | Assert.Equal(2, collections.Collection1.Count); 42 | Assert.Equal(2, collections.Collection2.Count); 43 | Assert.NotSame(collections.Enumerable1, collections.Enumerable2); 44 | Assert.NotSame(collections.List1, collections.List2); 45 | Assert.NotSame(collections.Collection1, collections.Collection2); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /StrongInject/RegisterFactoryAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject 4 | { 5 | /// 6 | /// Use this attribute to register a type implementing or as a factory for T, 7 | /// meaning T will be resolved by resolving an instance of the factory, and then calling or . 8 | /// 9 | /// It's usually simpler to write a factory method and mark it with the . 10 | /// Use this only if you need tight control over the lifetime of your factory, or if you want to control disposal of the factory target. 11 | /// 12 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] 13 | public sealed class RegisterFactoryAttribute : Attribute 14 | { 15 | /// 16 | /// 17 | /// 18 | /// The factory to register. Must implement or . 19 | /// The scope of the factory - i.e. how often should a new factory be created? 20 | /// The scope of the factory target - i.e. how often should or be called? 21 | public RegisterFactoryAttribute(Type factoryType, Scope factoryScope = Scope.InstancePerResolution, Scope factoryTargetScope = Scope.InstancePerResolution) 22 | { 23 | FactoryType = factoryType; 24 | FactoryScope = factoryScope; 25 | FactoryTargetScope = factoryTargetScope; 26 | } 27 | 28 | public Type FactoryType { get; } 29 | public Scope FactoryScope { get; } 30 | public Scope FactoryTargetScope { get; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /StrongInject/Modules/ValueTupleModule.cs: -------------------------------------------------------------------------------- 1 | namespace StrongInject.Modules 2 | { 3 | /// 4 | /// Provides registrations for tuples for all arities from 2 till 10. 5 | /// 6 | public static class ValueTupleModule 7 | { 8 | [Factory(Scope.InstancePerDependency)] public static (T1, T2) CreateValueTuple(T1 a, T2 b) => (a, b); 9 | [Factory(Scope.InstancePerDependency)] public static (T1, T2, T3) CreateValueTuple(T1 a, T2 b, T3 c) => (a, b, c); 10 | [Factory(Scope.InstancePerDependency)] public static (T1, T2, T3, T4) CreateValueTuple(T1 a, T2 b, T3 c, T4 d) => (a, b, c, d); 11 | [Factory(Scope.InstancePerDependency)] public static (T1, T2, T3, T4, T5) CreateValueTuple(T1 a, T2 b, T3 c, T4 d, T5 e) => (a, b, c, d, e); 12 | [Factory(Scope.InstancePerDependency)] public static (T1, T2, T3, T4, T5, T6) CreateValueTuple(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => (a, b, c, d, e, f); 13 | [Factory(Scope.InstancePerDependency)] public static (T1, T2, T3, T4, T5, T6, T7) CreateValueTuple(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g) => (a, b, c, d, e, f, g); 14 | [Factory(Scope.InstancePerDependency)] public static (T1, T2, T3, T4, T5, T6, T7, T8) CreateValueTuple(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) => (a, b, c, d, e, f, g, h); 15 | [Factory(Scope.InstancePerDependency)] public static (T1, T2, T3, T4, T5, T6, T7, T8, T9) CreateValueTuple(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h, T9 i) => (a, b, c, d, e, f, g, h, i); 16 | [Factory(Scope.InstancePerDependency)] public static (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) CreateValueTuple(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h, T9 i, T10 j) => (a, b, c, d, e, f, g, h, i, j); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Samples/Console/KafkaConsumer.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace StrongInject.Samples.ConsoleApp 6 | { 7 | public class KafkaConsumer : IConsumer 8 | { 9 | private readonly Confluent.Kafka.IConsumer _consumer; 10 | private readonly string _topic; 11 | 12 | public KafkaConsumer(Confluent.Kafka.IConsumer consumer, string topic) 13 | { 14 | _consumer = consumer; 15 | _topic = topic; 16 | } 17 | 18 | public async IAsyncEnumerable> Consume() 19 | { 20 | _consumer.Subscribe(_topic); 21 | 22 | while (true) 23 | { 24 | var result = _consumer.Consume(); 25 | if (result.IsPartitionEOF) 26 | { 27 | await Task.Delay(1000); 28 | } 29 | else 30 | { 31 | yield return new Commitable(result, _consumer); 32 | } 33 | } 34 | } 35 | 36 | private class Commitable : ICommitable 37 | { 38 | private readonly Confluent.Kafka.IConsumer _consumer; 39 | private readonly ConsumeResult _consumeResult; 40 | 41 | public Commitable(ConsumeResult consumeResult, Confluent.Kafka.IConsumer consumer) 42 | { 43 | _consumeResult = consumeResult; 44 | _consumer = consumer; 45 | } 46 | 47 | public TKey Key => _consumeResult.Message.Key; 48 | 49 | public TValue Value => _consumeResult.Message.Value; 50 | 51 | public void Commit() 52 | { 53 | _consumer.Commit(_consumeResult); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /StrongInject.Generator/Statement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.Collections.Immutable; 3 | 4 | namespace StrongInject.Generator 5 | { 6 | internal abstract record Statement; 7 | internal sealed record DependencyCreationStatement( 8 | string VariableName, 9 | InstanceSource Source, 10 | ImmutableArray Dependencies) : Statement; 11 | internal sealed record DelegateCreationStatement( 12 | string VariableName, 13 | DelegateSource Source, 14 | ImmutableArray InternalOperations, 15 | string InternalTargetName) : Statement 16 | { 17 | public string DisposeActionsName { get; } = "disposeActions_" + VariableName; 18 | } 19 | internal sealed record DisposeActionsCreationStatement(string VariableName, string TypeName) : Statement; 20 | internal sealed record SingleInstanceReferenceStatement(string VariableName, InstanceSource Source, bool IsAsync) : Statement; 21 | internal sealed record InitializationStatement(string? VariableName, string VariableToInitializeName, bool IsAsync) : Statement; 22 | internal sealed record AwaitStatement(string? VariableName, string VariableToAwaitName, ITypeSymbol? Type) : Statement 23 | { 24 | public string HasAwaitStartedVariableName { get; } = "hasAwaitStarted_" + VariableToAwaitName; 25 | public string HasAwaitCompletedVariableName { get; } = "hasAwaitCompleted_" + VariableToAwaitName; 26 | } 27 | 28 | internal sealed record OwnedCreationStatement( 29 | string VariableName, 30 | OwnedSource Source, 31 | bool IsAsyncLocalFunction, 32 | string LocalFunctionName) : Statement; 33 | 34 | internal sealed record OwnedCreationLocalFunctionStatement( 35 | OwnedSource Source, 36 | bool IsAsyncLocalFunction, 37 | string LocalFunctionName, 38 | ImmutableArray InternalOperations, 39 | string InternalTargetName) : Statement; 40 | } 41 | -------------------------------------------------------------------------------- /StrongInject/StrongInject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;netstandard2.1 5 | True 6 | Debug;Release;Wpf;All 7 | 8 | 9 | 10 | $(StrongInjectVersion) 11 | StrongInject 12 | compile time dependency injection for C# 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/Registration/BestRegistration.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Table Of Contents 4 | 5 | - [Best Registration](#best-registration) 6 | 7 | 8 | 9 | # Best Registration 10 | 11 | You can register multiple registrations for a type `T` which allows you to resolve `T[]` and other collections of `T`. However, when resolving just one instance of `T`, StrongInject requires that there is a *best registration* for `T`, or it will error. 12 | 13 | A *best registration* is defined as follows: 14 | 15 | 1. Any registration declared directly on a module/container is *better* than registrations declared on other modules and imported by the module/container. 16 | 2. If there is a single registration which is *better* than all other registrations it is considered the *best registration*. Else there is no *best registration*. 17 | 18 | So for example, imagine there is a Container, and two modules, ModuleA and ModuleB. 19 | 20 | If all three define a registration for SomeInterface, then the one defined on the container will always be the *best registration*. 21 | 22 | If just ModuleA and ModuleB define a registration for SomeInterface then things will depend: 23 | 24 | - If Container imports both modules, resolving SomeInterface will always result in an error (even if ModuleA imports ModuleB or vice versa). 25 | - If Container imports ModuleA, and ModuleA imports ModuleB, then ModuleA's registration will be the *best registration*. 26 | - If Container imports ModuleB, and ModuleB imports ModuleA, then ModuleB's registration will be the *best registration*. 27 | 28 | To fix errors as a result of multiple registrations for a type, the simplest thing to do is to add a single *best registration* directly to the container. If the container already has multiple registrations for the type, then move those registrations to a separate module and import them. 29 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp.Android/Resources/AboutResources.txt: -------------------------------------------------------------------------------- 1 | Images, layout descriptions, binary blobs and string dictionaries can be included 2 | in your application as resource files. Various Android APIs are designed to 3 | operate on the resource IDs instead of dealing with images, strings or binary blobs 4 | directly. 5 | 6 | For example, a sample Android app that contains a user interface layout (main.xml), 7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 8 | would keep its resources in the "Resources" directory of the application: 9 | 10 | Resources/ 11 | drawable-hdpi/ 12 | icon.png 13 | 14 | drawable-ldpi/ 15 | icon.png 16 | 17 | drawable-mdpi/ 18 | icon.png 19 | 20 | layout/ 21 | main.xml 22 | 23 | values/ 24 | strings.xml 25 | 26 | In order to get the build system to recognize Android resources, set the build action to 27 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 28 | instead operate on resource IDs. When you compile an Android application that uses resources, 29 | the build system will package the resources for distribution and generate a class called 30 | "Resource" that contains the tokens for each one of the resources included. For example, 31 | for the above Resources layout, this is what the Resource class would expose: 32 | 33 | public class Resource { 34 | public class drawable { 35 | public const int icon = 0x123; 36 | } 37 | 38 | public class layout { 39 | public const int main = 0x456; 40 | } 41 | 42 | public class strings { 43 | public const int first_string = 0xabc; 44 | public const int second_string = 0xbcd; 45 | } 46 | } 47 | 48 | You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main 49 | to reference the layout/main.xml file, or Resource.strings.first_string to reference the first 50 | string in the dictionary file values/strings.xml. 51 | -------------------------------------------------------------------------------- /StrongInject/FactoryAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject 4 | { 5 | /// 6 | /// Use this to mark a method as a factory. 7 | /// The method will be used to resolve instances of its return type. 8 | /// You can mark both normal methods and generic methods with this attribute. 9 | /// 10 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] 11 | public sealed class FactoryAttribute : Attribute 12 | { 13 | /// The scope of each instance resolved from the method - i.e. how often the method will be called. 14 | public FactoryAttribute(Scope scope = Scope.InstancePerResolution) 15 | { 16 | Scope = scope; 17 | AsTypes = Array.Empty(); 18 | } 19 | 20 | /// The scope of each instance resolved from the method - i.e. how often the method will be called. 21 | /// An optional list of types for this to be used as the factory of. 22 | /// If left empty it will be used as a factory of the return type. 23 | /// If not left empty you will have to explicitly register it as the return type if desired. 24 | /// All types must be supertypes of the return type. 25 | public FactoryAttribute(Scope scope, params Type[] asTypes) 26 | { 27 | Scope = scope; 28 | AsTypes = asTypes; 29 | } 30 | 31 | /// An optional list of types for this to be used as the factory of. 32 | /// If left empty it will be used as a factory of the return type. 33 | /// If not left empty you will have to explicitly register it as the return type if desired. 34 | /// All types must be supertypes of the return type. 35 | public FactoryAttribute(params Type[] asTypes) 36 | { 37 | AsTypes = asTypes; 38 | } 39 | 40 | public Scope Scope { get; } 41 | public Type[] AsTypes { get; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /StrongInject.Tests.Integration/Modules/ImmutableArrayTests.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Modules; 2 | using System; 3 | using System.Collections.Immutable; 4 | using System.Runtime.CompilerServices; 5 | using Xunit; 6 | 7 | namespace StrongInject.Tests.Integration.Modules 8 | { 9 | public partial class ImmutableArrayTests 10 | { 11 | [RegisterModule(typeof(SafeImmutableArrayModule))] 12 | [RegisterModule(typeof(StandardModule))] 13 | [Register(typeof(A), typeof(I))] 14 | [Register(typeof(B), typeof(I))] 15 | public partial class SafeContainer : IContainer<(ImmutableArray first, ImmutableArray second)> 16 | { 17 | 18 | } 19 | 20 | [RegisterModule(typeof(UnsafeImmutableArrayModule))] 21 | [RegisterModule(typeof(StandardModule))] 22 | [Register(typeof(A), typeof(I))] 23 | [Register(typeof(B), typeof(I))] 24 | public partial class UnsafeContainer : IContainer<(ImmutableArray first, ImmutableArray second)> 25 | { 26 | 27 | } 28 | 29 | public interface I { } 30 | public class A : I { } 31 | public class B : I { } 32 | 33 | [Fact] 34 | public void TestCanResolveSafe() 35 | { 36 | var container = new SafeContainer(); 37 | using var aScope1 = container.Resolve(); 38 | var (first, second) = aScope1.Value; 39 | Assert.Equal(2, first.Length); 40 | Assert.Equal(2, second.Length); 41 | Assert.NotSame(Unsafe.As, I[]>(ref first), Unsafe.As, I[]>(ref second)); 42 | } 43 | 44 | [Fact] 45 | public void TestCanResolveUnsafe() 46 | { 47 | var container = new UnsafeContainer(); 48 | using var aScope1 = container.Resolve(); 49 | var (first, second) = aScope1.Value; 50 | Assert.Equal(2, first.Length); 51 | Assert.Equal(2, second.Length); 52 | Assert.NotSame(Unsafe.As, I[]>(ref first), Unsafe.As, I[]>(ref second)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /StrongInject.Generator/Visitors/PartialOrderingOfSingleInstanceDependenciesVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | 6 | namespace StrongInject.Generator.Visitors 7 | { 8 | internal class PartialOrderingOfSingleInstanceDependenciesVisitor : SimpleVisitor 9 | { 10 | private List? _results; 11 | private readonly HashSet _alreadyAdded = new(); 12 | 13 | private PartialOrderingOfSingleInstanceDependenciesVisitor(InstanceSourcesScope containerScope, CancellationToken cancellationToken) : base(containerScope, cancellationToken) 14 | { 15 | } 16 | 17 | public static IEnumerable GetPartialOrdering(InstanceSourcesScope containerScope, HashSet usedSingleInstanceSources, CancellationToken cancellationToken) 18 | { 19 | var visitor = new PartialOrderingOfSingleInstanceDependenciesVisitor(containerScope, cancellationToken); 20 | IEnumerable results = Array.Empty(); 21 | foreach (var source in usedSingleInstanceSources) 22 | { 23 | visitor.VisitCore(source, new State(containerScope)); 24 | if (visitor._results is { } visitResults) 25 | results = visitResults.Concat(results); 26 | visitor._results = null; 27 | } 28 | return results; 29 | } 30 | 31 | protected override bool ShouldVisitBeforeUpdateState(InstanceSource? source, State state) 32 | { 33 | if (source is null) 34 | return false; 35 | if (source.Scope == Scope.SingleInstance && source is not (InstanceFieldOrProperty or ForwardedInstanceSource)) 36 | { 37 | if (_alreadyAdded.Add(source)) 38 | { 39 | (_results ??= new()).Add(source); 40 | } 41 | else 42 | { 43 | return false; 44 | } 45 | } 46 | return base.ShouldVisitBeforeUpdateState(source, state); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/ViewModels/NewItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.Models; 2 | using StrongInject.Samples.XamarinApp.Services; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Windows.Input; 7 | using Xamarin.Forms; 8 | 9 | namespace StrongInject.Samples.XamarinApp.ViewModels 10 | { 11 | public class NewItemViewModel : BaseViewModel 12 | { 13 | private string _text = ""; 14 | private string _description = ""; 15 | private readonly IDataStore _dataStore; 16 | private readonly INavigationService _navigationService; 17 | 18 | public NewItemViewModel(IDataStore dataStore, INavigationService navigationService) 19 | { 20 | SaveCommand = new Command(OnSave, ValidateSave); 21 | CancelCommand = new Command(OnCancel); 22 | PropertyChanged += 23 | (_, __) => SaveCommand.ChangeCanExecute(); 24 | _dataStore = dataStore; 25 | _navigationService = navigationService; 26 | } 27 | 28 | private bool ValidateSave() 29 | { 30 | return !string.IsNullOrWhiteSpace(_text) 31 | && !string.IsNullOrWhiteSpace(_description); 32 | } 33 | 34 | public string Text 35 | { 36 | get => _text; 37 | set => SetProperty(ref _text, value); 38 | } 39 | 40 | public string Description 41 | { 42 | get => _description; 43 | set => SetProperty(ref _description, value); 44 | } 45 | 46 | public Command SaveCommand { get; } 47 | public Command CancelCommand { get; } 48 | 49 | private async void OnCancel() 50 | { 51 | await _navigationService.PopAsync(); 52 | } 53 | 54 | private async void OnSave() 55 | { 56 | Item newItem = new Item( 57 | id: Guid.NewGuid().ToString(), 58 | text: Text, 59 | description: Description 60 | ); 61 | 62 | await _dataStore.AddItemAsync(newItem); 63 | await _navigationService.PopAsync(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Samples/Wpf/ViewModels/UserViewModel.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.Wpf.Models; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Threading.Tasks; 5 | 6 | namespace StrongInject.Samples.Wpf.ViewModels 7 | { 8 | public class UserViewModel : INotifyPropertyChanged 9 | { 10 | public UserViewModel(User user, IDatabase database) 11 | { 12 | _firstName = user.FirstName; 13 | _lastName = user.LastName; 14 | _id = user.Id; 15 | _database = database; 16 | } 17 | 18 | public bool SaveChanges { get; set; } = true; 19 | 20 | private string _firstName; 21 | private string _lastName; 22 | private Guid _id; 23 | private readonly IDatabase _database; 24 | 25 | public event PropertyChangedEventHandler? PropertyChanged; 26 | 27 | public string FirstName 28 | { 29 | get => _firstName; 30 | set 31 | { 32 | if (_firstName == value) 33 | { 34 | return; 35 | } 36 | _firstName = value; 37 | RaisePropertyChanged(nameof(FirstName)); 38 | RaisePropertyChanged(nameof(FullName)); 39 | } 40 | } 41 | 42 | public string LastName 43 | { 44 | get => _lastName; 45 | set 46 | { 47 | if (_lastName == value) 48 | { 49 | return; 50 | } 51 | _lastName = value; 52 | RaisePropertyChanged(nameof(LastName)); 53 | RaisePropertyChanged(nameof(FullName)); 54 | } 55 | } 56 | 57 | public string FullName => string.Concat(_firstName, " ", _lastName); 58 | 59 | private void RaisePropertyChanged(string propertyName) 60 | { 61 | if (SaveChanges) 62 | { 63 | _ = Save(); 64 | } 65 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 66 | } 67 | 68 | public Task Delete() => _database.DeleteUser(_id); 69 | public Task Save() => _database.AddOrUpdateUser(new User(_id, FirstName, LastName)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /StrongInject/IFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace StrongInject 4 | { 5 | /// 6 | /// A type implementing this interface can be registered as a factory for . 7 | /// It can be registered either via the , 8 | /// or by marking a field/property with the and configuring its . 9 | /// 10 | /// In general it's easier to use factory methods instead (methods marked with the . 11 | /// Only use this if you either need control over the lifetime of the factory, 12 | /// or you need to control disposal of the created type. 13 | /// 14 | /// For example, you may want to implement this if you would like to cache and reuse instances after they've been released. 15 | /// 16 | /// 17 | public interface IFactory 18 | { 19 | T Create(); 20 | 21 | void Release(T instance) 22 | #if !NETSTANDARD2_0 23 | => Helpers.Dispose(instance) 24 | #endif 25 | ; 26 | } 27 | 28 | /// 29 | /// A type implementing this interface can be registered as an async factory for . 30 | /// It can be registered either via the , 31 | /// or by marking a field/property with the and configuring its . 32 | /// 33 | /// In general it's easier to use factory methods instead (methods marked with the . 34 | /// Only use this if you either need control over the lifetime of the factory, 35 | /// or you need to control disposal of the created type. 36 | /// 37 | /// For example, you may want to implement this if you would like to cache and reuse instances after they've been released. 38 | /// 39 | /// 40 | public interface IAsyncFactory 41 | { 42 | ValueTask CreateAsync(); 43 | 44 | ValueTask ReleaseAsync(T instance) 45 | #if !NETSTANDARD2_0 46 | => Helpers.DisposeAsync(instance) 47 | #endif 48 | ; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /StrongInject.Generator/Visitors/RequiresAsyncVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | 4 | namespace StrongInject.Generator.Visitors 5 | { 6 | internal class RequiresAsyncChecker 7 | { 8 | private readonly InstanceSourcesScope _containerScope; 9 | private readonly CancellationToken _cancellationToken; 10 | private readonly Dictionary _cache = new(); 11 | 12 | public RequiresAsyncChecker(InstanceSourcesScope containerScope, CancellationToken cancellationToken) 13 | { 14 | _containerScope = containerScope; 15 | _cancellationToken = cancellationToken; 16 | } 17 | 18 | public bool RequiresAsync(InstanceSource source) 19 | { 20 | return _cache.GetOrCreate(source, (_containerScope, _cancellationToken), static (i, s) => Visitor.RequiresAsync(i, s._containerScope, s._cancellationToken)); 21 | } 22 | 23 | private class Visitor : SimpleVisitor 24 | { 25 | private bool _requiresAsync = false; 26 | 27 | private Visitor(InstanceSourcesScope containerScope, CancellationToken cancellationToken) : base(containerScope, cancellationToken) 28 | { 29 | } 30 | 31 | public static bool RequiresAsync(InstanceSource source, InstanceSourcesScope containerScope, CancellationToken cancellationToken) 32 | { 33 | var visitor = new Visitor(containerScope, cancellationToken); 34 | visitor.VisitCore(source, new State(containerScope)); 35 | return visitor._requiresAsync; 36 | } 37 | 38 | protected override bool ShouldVisitBeforeUpdateState(InstanceSource? source, State state) 39 | { 40 | if (source is null) 41 | return false; 42 | if (source is DelegateSource { IsAsync: true }) 43 | return false; 44 | if (source.IsAsync) 45 | { 46 | _requiresAsync = true; 47 | ExitFast(); 48 | return false; 49 | } 50 | 51 | return base.ShouldVisitBeforeUpdateState(source, state); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /StrongInject.Generator/Visitors/SingleInstanceVariablesToCreateEarlyVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | 4 | namespace StrongInject.Generator.Visitors 5 | { 6 | /// 7 | /// Calculates which single instance variables require an async context to be resolved, 8 | /// but are used in a sync delegate. 9 | /// In order to allow them to be resolved, they must be resolved in a parent async scope, 10 | /// and then captured by the delegate. 11 | /// 12 | internal class SingleInstanceVariablesToCreateEarlyVisitor : SimpleVisitor 13 | { 14 | private readonly RequiresAsyncChecker _requiresAsyncChecker; 15 | private readonly List _singleInstanceVariablesToCreateEarly = new(); 16 | 17 | private SingleInstanceVariablesToCreateEarlyVisitor(RequiresAsyncChecker requiresAsyncChecker, InstanceSourcesScope containerScope, CancellationToken cancellationToken) : base(containerScope, cancellationToken) 18 | { 19 | _requiresAsyncChecker = requiresAsyncChecker; 20 | } 21 | 22 | public static List CalculateVariables(RequiresAsyncChecker requiresAsyncChecker, InstanceSource source, InstanceSourcesScope currentScope, InstanceSourcesScope containerScope, CancellationToken cancellationToken) 23 | { 24 | var visitor = new SingleInstanceVariablesToCreateEarlyVisitor(requiresAsyncChecker, containerScope, cancellationToken); 25 | visitor.VisitCore(source, new State(currentScope)); 26 | return visitor._singleInstanceVariablesToCreateEarly; 27 | } 28 | 29 | protected override bool ShouldVisitBeforeUpdateState(InstanceSource? source, State state) 30 | { 31 | if (source is null) 32 | return false; 33 | if (source is DelegateSource { IsAsync: true }) 34 | return false; 35 | if (source.Scope == Scope.SingleInstance) 36 | { 37 | if (_requiresAsyncChecker.RequiresAsync(source)) 38 | { 39 | _singleInstanceVariablesToCreateEarly.Add(source); 40 | } 41 | return false; 42 | } 43 | return base.ShouldVisitBeforeUpdateState(source, state); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Samples/AspNetCore/README.md: -------------------------------------------------------------------------------- 1 | # StrongInject.Samples.AspNetCore 2 | 3 | ## Overview 4 | 5 | This sample demonstrates a simple rest API with two controllers. Both controllers are instantiated using StrongInject. 6 | 7 | The weather forecast controller is reached at https://localhost:44383/weatherforecast?location= and returns a (randomly generated) weather forecast for that location. 8 | 9 | The users controller is reached at https://localhost:44383/users and returns a list of users. 10 | 11 | ## Learning Points 12 | 13 | This app demonstrates how to integrate StrongInject with ASP.NET Core using the StrongInject.Extensions.DependencyInjection.AspNetCore package. 14 | 15 | It also demonstrates a number of other key features of StrongInject: 16 | 17 | 1. Usages of Scopes to control lifetimes. For example Controllers should be `InstancePerDependency` whilst caches should be `SingleInstance`. 18 | 2. Passing `IServiceProvider` as a parameter to the container, and using it to resolve `ILogger`, allowing for two way integration with other IOC containers. 19 | 3. Whilst `StrongInject` supports async resolution, Microsoft.Extensions.DependencyInjection does not. `DatabaseUsersCache` can only be prepared asynchronously so a different technique is used where requests on it become asynchronous instead. 20 | 4. Usage of a generic factory method to register `ILogger` for all `T` at once. 21 | 22 | ## Notes 23 | 24 | If you intend to use StrongInject to resolve all your controllers, then call `services.AddControllers().ResolveControllersThroughServiceProvider()` in `StartUp`. This tells ASP.NET Core to resolve all controllers through the service provider, but doesn't auto register them with the service provider. You can then register them yourselves manually. 25 | 26 | If however you want to auto register all controllers with the service provider, and only override some of them with StrongInject, then you will need to call `services.AddControllers().AddControllersAsServices()` instead. This not only tells ASP.NET Core to resolve all controllers through the service provider, but also auto registers them as well. You will then need to make sure you remove these auto registrations when you manually overwrite the default registration by calling `services.ReplaceWithTransientServiceUsingContainer()` (or any of the related `ReplaceWithXServiceUsingContainer` methods). 27 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Services/NavigationService.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.ViewModels; 2 | using StrongInject.Samples.XamarinApp.Views; 3 | using System; 4 | using System.Threading.Tasks; 5 | using Xamarin.Forms; 6 | 7 | namespace StrongInject.Samples.XamarinApp.Services 8 | { 9 | public class NavigationService : INavigationService 10 | { 11 | private readonly Lazy _shell; 12 | protected INavigation Navigation => _shell.Value.Navigation; 13 | 14 | public NavigationService(Lazy shell) 15 | { 16 | _shell = shell; 17 | } 18 | 19 | public Task PopAsync() 20 | { 21 | return Navigation.PopAsync(); 22 | } 23 | 24 | public Task GoToAsync(string route) 25 | { 26 | return _shell.Value.GoToAsync(route); 27 | } 28 | } 29 | 30 | public class NavigationService : NavigationService, INavigationService where T : BaseViewModel 31 | { 32 | private readonly Func> _createView; 33 | 34 | public NavigationService(Lazy shell, Func> createView) : base(shell) 35 | { 36 | _createView = createView; 37 | } 38 | 39 | public Task PushAsync() 40 | { 41 | return Navigation.PushAsync((Page)_createView()); 42 | } 43 | 44 | public async Task PopAllAndPushAsync() 45 | { 46 | await Navigation.PopToRootAsync(); 47 | await Navigation.PushAsync((Page)_createView()); 48 | } 49 | } 50 | 51 | public class ParameterizedNavigationService : NavigationService, IParameterizedNavigationService where T : BaseViewModel 52 | { 53 | private readonly Func> _createView; 54 | 55 | public ParameterizedNavigationService(Lazy shell, Func> createView) : base(shell) 56 | { 57 | _createView = createView; 58 | } 59 | 60 | public Task PushAsync(T viewModel) 61 | { 62 | return Navigation.PushAsync((Page)_createView(viewModel)); 63 | } 64 | 65 | public async Task PopAllAndPushAsync(T viewModel) 66 | { 67 | await Navigation.PopToRootAsync(); 68 | await Navigation.PushAsync((Page)_createView(viewModel)); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /StrongInject.Tests.Unit/OwnedTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xunit; 3 | 4 | namespace StrongInject.Generator.Tests.Unit 5 | { 6 | public static class OwnedTests 7 | { 8 | [Fact] 9 | public static void OwnedCanBeImplicitlyCastToWiderT() 10 | { 11 | var narrowerType = new Owned("", dispose: () => { }); 12 | IOwned widerType = narrowerType; 13 | 14 | // This should survive any refactoring of the types or APIs. 15 | // (E.g. Replacing IOwned with owned.Cast() returning an Owned wrapper would not be acceptable.) 16 | Assert.Same(narrowerType, widerType); 17 | } 18 | 19 | [Fact] 20 | public static void AsyncOwnedCanBeImplicitlyCastToWiderT() 21 | { 22 | var narrowerType = new AsyncOwned("", dispose: () => ValueTask.CompletedTask); 23 | IAsyncOwned widerType = narrowerType; 24 | 25 | // This should survive any refactoring of the types or APIs. 26 | // (E.g. Replacing IOwned with owned.Cast() returning an Owned wrapper would not be acceptable.) 27 | Assert.Same(narrowerType, widerType); 28 | } 29 | 30 | [Fact] 31 | public static void OwnedCanBeUsedWithNullDisposeAction() 32 | { 33 | var owned = new Owned("", dispose: null); 34 | 35 | // If a check is ever added in the future to return null or throw if Value is accessed after disposal, 36 | // the null action should not cause Owned to think that disposal has already happened. 37 | Assert.Equal("", owned.Value); 38 | 39 | owned.Dispose(); 40 | } 41 | 42 | [Fact] 43 | public static void AsyncOwnedCanBeUsedWithNullDisposeAction() 44 | { 45 | var owned = new AsyncOwned("", dispose: null); 46 | 47 | // If a check is ever added in the future to return null or throw if Value is accessed after disposal, 48 | // the null action should not cause Owned to think that disposal has already happened. 49 | Assert.Equal("", owned.Value); 50 | 51 | // This should complete instantly since there is nothing to do. 52 | Assert.True(owned.DisposeAsync().IsCompletedSuccessfully); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /StrongInject/RegisterDecoratorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StrongInject 4 | { 5 | /// 6 | /// Use this attribute to register a decorator for a type. 7 | /// When resolving an instance of the type, once the decorated type has been resolved as normal it will be wrapped by an instance of the decorator. 8 | /// 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] 10 | public sealed class RegisterDecoratorAttribute : Attribute 11 | { 12 | /// 13 | /// 14 | /// 15 | /// The type to use as a decorator. Must be a subtype of . 16 | /// The type which will be decorated (wrapped) by . 17 | /// 18 | public RegisterDecoratorAttribute(Type type, Type decoratedType, DecoratorOptions decoratorOptions = DecoratorOptions.Default) 19 | { 20 | Type = type; 21 | DecoratedType = decoratedType; 22 | DecoratorOptions = decoratorOptions; 23 | } 24 | 25 | public Type Type { get; } 26 | public Type DecoratedType { get; } 27 | public DecoratorOptions DecoratorOptions { get; } 28 | } 29 | 30 | /// 31 | /// Use this attribute to register a decorator for a type. 32 | /// When resolving an instance of the type, once the decorated type has been resolved as normal it will be wrapped by an instance of the decorator. 33 | /// The type to use as a decorator. Must be a subtype of . 34 | /// The type which will be decorated (wrapped) by . 35 | /// 36 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] 37 | public sealed class RegisterDecoratorAttribute : Attribute where TDecorator : TDecorated 38 | { 39 | public RegisterDecoratorAttribute(DecoratorOptions decoratorOptions = DecoratorOptions.Default) 40 | { 41 | DecoratorOptions = decoratorOptions; 42 | } 43 | 44 | public DecoratorOptions DecoratorOptions { get; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Services/MockDataStore.cs: -------------------------------------------------------------------------------- 1 | using StrongInject.Samples.XamarinApp.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace StrongInject.Samples.XamarinApp.Services 8 | { 9 | public class MockDataStore : IDataStore 10 | { 11 | readonly List _items; 12 | 13 | public MockDataStore() 14 | { 15 | _items = new List() 16 | { 17 | new Item(id: Guid.NewGuid().ToString(), text: "First item", description: "This is an item description." ), 18 | new Item(id: Guid.NewGuid().ToString(), text: "Second item", description: "This is an item description." ), 19 | new Item(id: Guid.NewGuid().ToString(), text: "Third item", description: "This is an item description." ), 20 | new Item(id: Guid.NewGuid().ToString(), text: "Fourth item", description: "This is an item description." ), 21 | new Item(id: Guid.NewGuid().ToString(), text: "Fifth item", description: "This is an item description." ), 22 | new Item(id: Guid.NewGuid().ToString(), text: "Sixth item", description: "This is an item description." ), 23 | }; 24 | } 25 | 26 | public async Task AddItemAsync(Item item) 27 | { 28 | _items.Add(item); 29 | 30 | return await Task.FromResult(true); 31 | } 32 | 33 | public async Task UpdateItemAsync(Item item) 34 | { 35 | var oldItem = _items.Where((Item arg) => arg.Id == item.Id).FirstOrDefault(); 36 | _items.Remove(oldItem); 37 | _items.Add(item); 38 | 39 | return await Task.FromResult(true); 40 | } 41 | 42 | public async Task DeleteItemAsync(string id) 43 | { 44 | var oldItem = _items.Where((Item arg) => arg.Id == id).FirstOrDefault(); 45 | _items.Remove(oldItem); 46 | 47 | return await Task.FromResult(true); 48 | } 49 | 50 | public async Task GetItemAsync(string id) 51 | { 52 | return await Task.FromResult(_items.FirstOrDefault(s => s.Id == id)); 53 | } 54 | 55 | public async Task> GetItemsAsync(bool forceRefresh = false) 56 | { 57 | return await Task.FromResult(_items); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Samples/Xamarin/README.md: -------------------------------------------------------------------------------- 1 | # StrongInject.Samples.XamarinApp 2 | 3 | ## Overview 4 | 5 | This sample modifies the default Xamarin.Forms sample app to use DI via StrongInject. No functionality has been added or removed, but the code style has been changed to be consistent with StrongInject. 6 | 7 | It consists of an "About" tab, an "Items" tab which lets you navigate to each item and add new items, and a "Login" page. 8 | 9 | The implementation presents just one way to do navigation whilst injecting pages and view models. Other techniques are possible. 10 | 11 | ## Debugging 12 | 13 | The Android app is not built by the Debug/Release configurations as the dotnet CLI doesn't support xamarin. To debug the app change the configuration to AndroidApp, and then run/debug as normal. 14 | 15 | ## Navigation 16 | 17 | In this sample all pages and view models are created by the StrongInject container. 18 | 19 | When a page/ViewModel wants to navigate to a different page it takes a dependency on `INavigationService`, `INavigationService` or `IParameterizedNavigationService` depending on its exact needs. 20 | 21 | To avoid ViewModels controlling which page is used for which ViewModel, `INavigationService` and `IParameterizedNavigationService` navigate to a specific ViewModel rather than a specific page. By registering your page as `IViewOf` in the container, you can configure which pages are linked to which ViewModel. This means you could theoretically have two containers constructing the exact same ViewModels but assigning them to different pages. 22 | 23 | AppShell.xaml delares a number of routes, and binds them to a DataTemplate returning an injected `Func`. This shows how you can use routes, and still make sure that you are injecting all your pages. 24 | 25 | ## Learning Points 26 | 27 | This app demonstrates a number of key features and techniques using StrongInject: 28 | 29 | 1. The Shell is set only after the container is run, but many services in the container depend on the Shell. We use `Lazy`, provided by the builtin `LazyModule` to allow this to work. 30 | 2. We use factories to register generic types, as well as to register a static field. 31 | 3. A marker interface `IViewOf` is used to help StrongInject link up the correct Page with the correct ViewModel. 32 | 4. Funcs are used to lazily create pages and ViewModels, rather than creating them all at startup. 33 | 5. Funcs are used to parameterize a ViewModel, so that the `ItemsViewModel` can create a different `ItemDetailViewModel` for each `Item`. 34 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/ItemsPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 21 | 22 | 23 | 24 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Samples/Xamarin/StrongInject.Samples.XamarinApp/Views/AboutPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | #96d1ff 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |