Skip to content

Commit 1624b62

Browse files
committed
Add project files.
1 parent 758dc07 commit 1624b62

File tree

11 files changed

+413
-0
lines changed

11 files changed

+413
-0
lines changed

Icon.png

998 KB
Loading

LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) [year] [fullname]
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# SimpleInjection
2+
3+
A lightweight dependency injection library for C# that combines simple DI container functionality with powerful source generation for content management.
4+
5+
## Features
6+
7+
### 🚀 Simple Dependency Injection
8+
- **Attribute-based registration** - Mark classes with `[Singleton]`, `[Scoped]`, or `[Transient]`
9+
- **Automatic service discovery** - No manual registration required
10+
- **Constructor injection** - Automatic dependency resolution
11+
- **Scope management** - Built-in scoped service lifetime management
12+
13+
### ⚡ Content Source Generation
14+
- **Automatic enum generation** - Creates enums from your content collections
15+
- **Type-safe access** - Generated helper methods for accessing content by enum or index
16+
- **Performance optimized** - Uses `NamedComparer<T>` for fast dictionary lookups
17+
- **Roslyn analyzers** - Enforces best practices and catches common mistakes
18+
19+
## Quick Start
20+
21+
### 1. Install the Package
22+
```bash
23+
dotnet add package SimpleInjection
24+
```
25+
26+
### 2. Dependency Injection Usage
27+
28+
Mark your classes with lifetime attributes:
29+
30+
```csharp
31+
[Singleton]
32+
public class DatabaseService
33+
{
34+
public void Connect() => Console.WriteLine("Connected to database");
35+
}
36+
37+
[Scoped]
38+
public class UserService
39+
{
40+
private readonly DatabaseService _database;
41+
42+
public UserService(DatabaseService database)
43+
{
44+
_database = database;
45+
}
46+
47+
public void GetUser() => _database.Connect();
48+
}
49+
```
50+
51+
Initialize and use the host:
52+
53+
```csharp
54+
var host = Host.Initialize();
55+
56+
// Get singleton services directly
57+
var dbService = host.Get<DatabaseService>();
58+
59+
// Create scopes for scoped services
60+
using var scope = host.CreateScope();
61+
var userService = scope.Get<UserService>();
62+
```
63+
64+
### 3. Content Generation Usage
65+
66+
Define your content classes:
67+
68+
```csharp
69+
// Your content item must implement INamed
70+
public record Material(string Name, string Color, int Durability) : INamed;
71+
72+
// Your content collection must implement IContent<T>
73+
[Singleton]
74+
public partial class Materials : IContent<Material>
75+
{
76+
public Material[] All { get; } =
77+
[
78+
new("Steel", "Gray", 100),
79+
new("Wood", "Brown", 50),
80+
new("Gold", "Yellow", 25)
81+
];
82+
}
83+
```
84+
85+
The source generator automatically creates:
86+
87+
```csharp
88+
// Generated enum
89+
public enum MaterialsType
90+
{
91+
Steel,
92+
Wood,
93+
Gold
94+
}
95+
96+
// Generated helper methods
97+
public partial class Materials
98+
{
99+
public Material Get(MaterialsType type) => All[(int)type];
100+
public Material this[MaterialsType type] => All[(int)type];
101+
public Material GetById(int id) => All[id];
102+
public Material Steel => All[0];
103+
public Material Wood => All[1];
104+
public Material Gold => All[2];
105+
}
106+
```
107+
108+
Use the generated code:
109+
110+
```csharp
111+
var materials = host.Get<Materials>();
112+
113+
// Type-safe access using enums
114+
var steel = materials[MaterialsType.Steel];
115+
var wood = materials.Get(MaterialsType.Wood);
116+
117+
// Direct property access
118+
var gold = materials.Gold;
119+
120+
// Index-based access
121+
var firstMaterial = materials.GetById(0);
122+
```
123+
124+
## Advanced Features
125+
126+
### Performance Optimizations
127+
128+
The library includes `NamedComparer<T>` for efficient dictionary operations with `INamed` keys:
129+
130+
```csharp
131+
// Use ToNamedDictionary extension method
132+
var materialDict = materials.All.ToNamedDictionary(m => m.Durability);
133+
134+
// Or explicitly specify the comparer
135+
var dict = new Dictionary<Material, int>(new NamedComparer<Material>());
136+
```
137+
138+
### SubContent Collections
139+
140+
For hierarchical content organization:
141+
142+
```csharp
143+
public class WeaponStats : ISubContent<Material, int>
144+
{
145+
public Dictionary<Material, int> ByKey { get; }
146+
public int this[Material material] => ByKey[material];
147+
}
148+
```
149+
150+
### Roslyn Analyzers
151+
152+
The package includes analyzers that help you:
153+
- **NC001**: Ensures `Dictionary<TKey, TValue>` uses `NamedComparer<T>` for `INamed` keys
154+
- **TND001**: Suggests using `ToNamedDictionary()` instead of `ToDictionary()` for `INamed` keys
155+
156+
## Requirements
157+
158+
- .NET 8.0 or .NET 9.0
159+
- C# with nullable reference types enabled (recommended)
160+
161+
## How It Works
162+
163+
1. **Service Discovery**: The host scans all loaded assemblies for classes marked with lifetime attributes
164+
2. **Dependency Resolution**: Constructor parameters are automatically resolved from registered services
165+
3. **Source Generation**: The generator scans for classes implementing `IContent<T>` and generates enums and helper methods
166+
4. **Code Analysis**: Roslyn analyzers ensure best practices for dictionary usage with named keys
167+
168+
## Best Practices
169+
170+
- Use `[Singleton]` for stateless services and shared resources
171+
- Use `[Scoped]` for services that should be unique per operation/request
172+
- Use `[Transient]` for lightweight, stateless services that need fresh instances
173+
- Always implement `INamed` for content objects to enable source generation
174+
- Use the generated enums for type-safe content access
175+
- Leverage `ToNamedDictionary()` for performance when working with `INamed` collections
176+
177+
## License
178+
179+
MIT License - see the license file for details.

SimpleMVVM.sln

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36203.30 d17.14
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleMVVM", "SimpleMVVM\SimpleMVVM.csproj", "{BA71A860-3099-4D56-AF32-28E84C602E74}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ViewModelGenerator", "ViewModelGenerator\ViewModelGenerator.csproj", "{529954DF-428A-4403-8D15-3B0B266223D6}"
9+
EndProject
10+
Global
11+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Debug|Any CPU = Debug|Any CPU
13+
Release|Any CPU = Release|Any CPU
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{BA71A860-3099-4D56-AF32-28E84C602E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17+
{BA71A860-3099-4D56-AF32-28E84C602E74}.Debug|Any CPU.Build.0 = Debug|Any CPU
18+
{BA71A860-3099-4D56-AF32-28E84C602E74}.Release|Any CPU.ActiveCfg = Release|Any CPU
19+
{BA71A860-3099-4D56-AF32-28E84C602E74}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{529954DF-428A-4403-8D15-3B0B266223D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{529954DF-428A-4403-8D15-3B0B266223D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{529954DF-428A-4403-8D15-3B0B266223D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{529954DF-428A-4403-8D15-3B0B266223D6}.Release|Any CPU.Build.0 = Release|Any CPU
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
GlobalSection(ExtensibilityGlobals) = postSolution
29+
SolutionGuid = {CC544EF8-C1B0-4BCE-A9D8-7AE39318D16D}
30+
EndGlobalSection
31+
EndGlobal

SimpleMVVM/BaseCommand.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Windows.Input;
2+
3+
namespace SimpleMVVM;
4+
5+
/// <summary>
6+
/// Provides a base implementation of the <see cref="ICommand"/> interface for WPF MVVM scenarios.
7+
/// Handles the <see cref="CanExecuteChanged"/> event using <see cref="CommandManager.RequerySuggested"/>.
8+
/// Override <see cref="CanExecute"/> and <see cref="Execute"/> in derived classes to implement command logic.
9+
/// </summary>
10+
public class BaseCommand : ICommand
11+
{
12+
/// <inheritdoc/>
13+
public event EventHandler? CanExecuteChanged
14+
{
15+
add => CommandManager.RequerySuggested += value;
16+
remove => CommandManager.RequerySuggested -= value;
17+
}
18+
19+
/// <summary>
20+
/// Determines whether the command can execute in its current state.
21+
/// </summary>
22+
/// <param name="parameter">Data used by the command. If the command does not require data, this object can be set to null.</param>
23+
/// <returns>true if this command can be executed; otherwise, false.</returns>
24+
public virtual bool CanExecute(object? parameter) => true;
25+
26+
/// <summary>
27+
/// Defines the method to be called when the command is invoked.
28+
/// </summary>
29+
/// <param name="parameter">Data used by the command. If the command does not require data, this object can be set to null.</param>
30+
/// <exception cref="NotImplementedException">Thrown if not overridden in a derived class.</exception>
31+
public virtual void Execute(object? parameter) => throw new NotImplementedException();
32+
}

SimpleMVVM/BaseViewModel.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System.ComponentModel;
2+
using System.Runtime.CompilerServices;
3+
using System.Windows;
4+
5+
namespace SimpleMVVM;
6+
7+
/// <summary>
8+
/// Provides a base implementation of <see cref="INotifyPropertyChanged"/> for view models in a WPF MVVM application.
9+
/// Includes property change notification, design mode detection, and a common <c>IsInUse</c> property.
10+
/// </summary>
11+
public class BaseViewModel : INotifyPropertyChanged
12+
{
13+
/// <summary>
14+
/// Indicates whether the view model is running in live (non-design) mode.
15+
/// </summary>
16+
protected readonly bool _isLive = !DesignerProperties.GetIsInDesignMode(new DependencyObject());
17+
18+
/// <inheritdoc/>
19+
public event PropertyChangedEventHandler? PropertyChanged;
20+
21+
/// <summary>
22+
/// Sets the property and raises the <see cref="PropertyChanged"/> event if the value changes.
23+
/// </summary>
24+
/// <typeparam name="T">The type of the property.</typeparam>
25+
/// <param name="reference">A reference to the backing field.</param>
26+
/// <param name="value">The new value.</param>
27+
/// <param name="propertyName">The name of the property. This is optional and is automatically provided by the compiler.</param>
28+
protected void SetProperty<T>(ref T reference, T value, [CallerMemberName] string propertyName = "")
29+
{
30+
if (Equals(reference, value)) return;
31+
reference = value;
32+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
33+
}
34+
35+
/// <summary>
36+
/// Raises the <see cref="PropertyChanged"/> event for the specified property.
37+
/// </summary>
38+
/// <param name="propertyName">The name of the property. This is optional and is automatically provided by the compiler.</param>
39+
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
40+
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
41+
}

SimpleMVVM/SimpleMVVM.csproj

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net9.0-windows;net8.0-windows;</TargetFrameworks>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<UseWPF>true</UseWPF>
8+
9+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
10+
<PackageId>SimpleMVVM</PackageId>
11+
<Version>0.9.0</Version>
12+
<Authors>Derek Gooding</Authors>
13+
<Company>Derek Gooding</Company>
14+
<Description>
15+
A lightweight dependency injection container with automatic service discovery and powerful source generation for content management.
16+
Features attribute-based registration ([Singleton], [Scoped], [Transient]), automatic enum generation from collections,
17+
type-safe content access, and Roslyn analyzers for performance optimization.
18+
</Description>
19+
<PackageTags>
20+
dependency-injection;DI;IoC;container;source-generator;roslyn;analyzer;
21+
content-management;enum-generation;performance;csharp;dotnet;
22+
singleton;scoped;transient;service-locator;automatic-registration;
23+
type-safety;code-generation;lightweight;minimal;simple
24+
</PackageTags>
25+
26+
<Summary>
27+
Lightweight DI container with source generation - automatic service discovery,
28+
enum generation from content collections, and performance-optimized analyzers.
29+
</Summary>
30+
31+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
32+
<PackageReadmeFile>README.md</PackageReadmeFile>
33+
<PackageProjectUrl>https://github.com/DerekGooding/SimpleInjection</PackageProjectUrl>
34+
<RepositoryUrl>https://github.com/DerekGooding/SimpleInjection</RepositoryUrl>
35+
<PackageBugTrackerUrl>https://github.com/DerekGooding/SimpleInjection/issues</PackageBugTrackerUrl>
36+
<RepositoryType>git</RepositoryType>
37+
<RepositoryBranch>main</RepositoryBranch>
38+
<RepositoryCommit>$(GitCommitId)</RepositoryCommit>
39+
40+
<PackageIcon>Icon.png</PackageIcon>
41+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
42+
<DocumentationFile>bin\SimpleMVVM.xml</DocumentationFile>
43+
44+
<Copyright>Copyright © Derek Gooding 2025</Copyright>
45+
<Product>SimpleMVVM</Product>
46+
<AssemblyTitle>SimpleMVVM - Lightweight windows only WPF MVVM with Source Generation</AssemblyTitle>
47+
<AssemblyDescription>$(Description)</AssemblyDescription>
48+
<NeutralLanguage>en-US</NeutralLanguage>
49+
50+
<DevelopmentDependency>false</DevelopmentDependency>
51+
<IsTool>false</IsTool>
52+
53+
<PackageType>Dependency</PackageType>
54+
<MinClientVersion>4.1.0</MinClientVersion>
55+
<RequireLicenseAcceptance>false</RequireLicenseAcceptance>
56+
57+
</PropertyGroup>
58+
59+
<ItemGroup>
60+
<None Include="..\Icon.png" Pack="true" PackagePath="" />
61+
<None Include="..\README.md" Pack="true" PackagePath="" />
62+
<None Include="$(MSBuildThisFileDirectory)../ViewModelGenerator/bin/Release/netstandard2.0/ViewModelGenerator.dll"
63+
Pack="true"
64+
PackagePath="analyzers/dotnet/cs/"
65+
Condition="Exists('$(MSBuildThisFileDirectory)../ViewModelGenerator/bin/Release/netstandard2.0/ViewModelGenerator.dll')" />
66+
</ItemGroup>
67+
68+
</Project>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
; Shipped analyzer releases
2+
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
; Unshipped analyzer release
2+
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
3+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace ViewModelGenerator;
4+
5+
[Generator]
6+
public class ViewModelGenerator : IIncrementalGenerator
7+
{
8+
public void Initialize(IncrementalGeneratorInitializationContext context)
9+
=> throw new System.NotImplementedException();
10+
}

0 commit comments

Comments
 (0)