├── 1.md ├── 2.md └── README.md /1.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | - [Generics types as a factory](#generics-types-as-a-factory) 3 | - [Lazy initialization of services](#lazy-initialization-of-services) 4 | - [Single implementation multiple interfaces](#single-implementation-multiple-interfaces) 5 | - [Creating instances of types from an IServiceProvider](#creating-instances-of-types-from-an-iserviceprovider) 6 | - [Caching singletons in generic types](#caching-singletons-in-generic-types) 7 | - [Resolving services when using IOptions\](#resolving-services-when-using-ioptionst) 8 | 9 | ## Generics types as a factory 10 | 11 | This pattern is used in **Microsoft.Extensions.\*** and in **Microsoft.AspNetCore.\***. The idea is that you can use a generic type as a factory instead of a function. The type argument is the 12 | type you want to instantiate. Consider the example below where we have an `IServiceFactory` that can resolve the `TService` from the DI container or creates an instance if it's 13 | not in the container. 14 | 15 | ```C# 16 | public interface IServiceFactory 17 | { 18 | TService Service { get; } 19 | } 20 | 21 | public class ServiceFactory : IServiceFactory 22 | { 23 | public ServiceFactory(IServiceProvider service) 24 | { 25 | Service = (TService)service.GetService(typeof(TService)) ?? ActivatorUtilities.CreateInstance(service); 26 | } 27 | 28 | public TService Service { get; } 29 | } 30 | ``` 31 | 32 | The constructor has access to any service *and* the generic type being requested. These open generic services are registered like this: 33 | 34 | ```C# 35 | public void ConfigureServices(IServiceCollection services) 36 | { 37 | services.AddTransient(typeof(IServiceFactory<>), typeof(ServiceFactory<>)); 38 | } 39 | ``` 40 | 41 | ## Lazy initialization of services 42 | 43 | Sometimes it's necessary to create services later than a constructor. The usual workaround for this is to inject an `IServiceProvider` into the constructor of the service that needs to lazily create the instance. 44 | 45 | ```C# 46 | public class Service 47 | { 48 | private readonly IServiceProvider _serviceProvider; 49 | public Service(IServiceProvider serviceProvider) 50 | { 51 | _serviceProvider = serviceProvider; 52 | } 53 | 54 | public IFoo CreateFoo() => _serviceProvider.GetRequiredService(); 55 | public IBar CreateBar() => _serviceProvider.GetRequiredService(); 56 | } 57 | ``` 58 | 59 | If the types are known ahead of time, we can build a custom service provider lazy type to encapsulate the service locator pattern: 60 | 61 | ```C# 62 | public interface ILazy 63 | { 64 | T Value { get; } 65 | } 66 | 67 | public class LazyFactory : ILazy 68 | { 69 | private readonly Lazy _lazy; 70 | 71 | public LazyFactory(IServiceProvider service) 72 | { 73 | _lazy = new Lazy(() => service.GetRequiredService()); 74 | } 75 | 76 | public T Value => _lazy.Value; 77 | } 78 | 79 | public class Service 80 | { 81 | private readonly ILazy _foo; 82 | private readonly ILazy _bar; 83 | public Service(ILazy foo, ILazy bar) 84 | { 85 | _foo = foo; 86 | _bar = bar; 87 | } 88 | 89 | public IFoo CreateFoo() => _foo.Value; 90 | public IBar CreateBar() => _bar.Value; 91 | } 92 | ``` 93 | 94 | Registered like this: 95 | 96 | ```C# 97 | public void ConfigureServices(IServiceCollection services) 98 | { 99 | // We register this as transient so it handles both singleton and scoped services 100 | // as the IServiceProvider injected will have the relevant lifetime. 101 | services.AddTransient(typeof(ILazy<>), typeof(LazyFactory<>)); 102 | } 103 | ``` 104 | 105 | `Lazy` could also be used as-is if added with a concrete type at service registration time: 106 | 107 | ```C# 108 | public void ConfigureServices(IServiceCollection services) 109 | { 110 | services.AddTransient>(sp => new Lazy(() => sp.GetRequiredService()); 111 | services.AddTransient>(sp => new Lazy(() => sp.GetRequiredService()); 112 | } 113 | ``` 114 | 115 | ## Single implementation multiple interfaces 116 | 117 | Let's say you had a type that implemented multiple interfaces and you wanted to expose it using the DI container. The built-in `IServiceCollection` type doesn't natively support this but it's easy to emulate using the following pattern. 118 | 119 | ```C# 120 | public class FooAndBar : IFoo, IBar 121 | { 122 | // Imagine a useful implementation 123 | } 124 | ``` 125 | 126 | ```C# 127 | public void ConfigureServices(IServiceCollection services) 128 | { 129 | services.AddSingleton(); 130 | services.AddSingleton(sp => sp.GetRequiredService()); 131 | services.AddSingleton(sp => sp.GetRequiredService()); 132 | } 133 | ``` 134 | 135 | This will let me resolve `FooAndBar`, `IFoo` and `IBar` and it will give me the same instance. 136 | 137 | ## Creating instances of types from an IServiceProvider 138 | 139 | Usually you need to register a type in order to instantiate instances of a type from the DI container, somebody needs to call `IServiceProvider.GetService`. This means that the service needs to be registered in the container. This may not be desirable if these types are discovered dynamically (like controllers in MVC). There's a useful utility called [ActivatorUtilities](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.activatorutilities?view=dotnet-plat-ext-5.0) that can be used as a factory for types that haven't been registered, but have dependencies that are registered in the DI container. 140 | 141 | ```C# 142 | public class MyDependency 143 | { 144 | public MyDependency(ILogger logger) 145 | { 146 | } 147 | } 148 | ``` 149 | 150 | ```C# 151 | public class MyDependencyFactory 152 | { 153 | private readonly IServiceProvider _serviceProvider; 154 | public MyDependencyFactory(IServiceProvider serviceProvider) 155 | { 156 | _serviceProvider = serviceProvider; 157 | } 158 | public MyDependency GetInstance() 159 | { 160 | return ActivatorUtilities.CreateInstance(_serviceProvider); 161 | } 162 | } 163 | ``` 164 | 165 | You can build a more optimized version of this uses the [`CreateFactory`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.activatorutilities.createfactory?view=dotnet-plat-ext-5.0#Microsoft_Extensions_DependencyInjection_ActivatorUtilities_CreateFactory_System_Type_System_Type___). This will pre-calculate the constructor 166 | based on the types passed and build a factory for it. 167 | 168 | ```C# 169 | public class MyDependencyFactory 170 | { 171 | private readonly IServiceProvider _serviceProvider; 172 | private readonly ObjectFactory _factory; 173 | public MyDependencyFactory(IServiceProvider serviceProvider) 174 | { 175 | _serviceProvider = serviceProvider; 176 | _factory = ActivatorUtilities.CreateFactory(typeof(MyDependency), Type.EmptyTypes); 177 | } 178 | public MyDependency GetInstance() 179 | { 180 | return (MyDependency) _factory(_serviceProvider, null); 181 | } 182 | } 183 | ``` 184 | 185 | **NOTE: Disposable instances created with this API will not be disposed by the DI container.** 186 | 187 | ## Caching singletons in generic types 188 | 189 | If you need to cache an instance of something based on type, then you can store it on a static field of a generic type. 190 | 191 | ```C# 192 | public class Factory 193 | { 194 | public T Create() 195 | where T : new() 196 | { 197 | return Cache.Instance; 198 | } 199 | 200 | private static class Cache 201 | where T : new() 202 | { 203 | public static readonly T Instance = new(); 204 | } 205 | } 206 | ``` 207 | 208 | You can use the JIT to cache instances on your behalf instead of a slower `ConcurrentDictionary`. It can also be used to cache the expensive object creation process: 209 | 210 | ```C# 211 | public class Factory 212 | { 213 | public T Create() 214 | where T : new() 215 | { 216 | return Cache.Instance; 217 | } 218 | 219 | private static class Cache 220 | where T : new() 221 | { 222 | public static readonly T Instance = CallExpensiveMethodToBuildANewInstance(); 223 | 224 | private static T CallExpensiveMethodToBuildANewInstance() 225 | { 226 | // Imagine a really complex process to construct T 227 | return instance. 228 | } 229 | } 230 | } 231 | ``` 232 | 233 | ## Resolving services when using IOptions\ 234 | 235 | The options pattern is used in **Microsoft.Extensions.\*** and in **Microsoft.AspNetCore.\***. It allows anyone to register a callback to mutate a POCO used to configure a library. Sometimes you'd like access to services when configuring these options. There are multiple ways to do this: 236 | 237 | ```C# 238 | public class LibraryOptions 239 | { 240 | public int Setting { get; set; } 241 | } 242 | 243 | public class MyConfigureOptions : IConfigureOptions 244 | { 245 | private readonly ISomeService _service; 246 | public MyConfigureOptions(ISomeService service) 247 | { 248 | _service = service; 249 | } 250 | 251 | public void Configure(LibraryOptions options) 252 | { 253 | options.Setting = _service.ComputeSetting(); 254 | } 255 | } 256 | ``` 257 | 258 | Followed by the registration. 259 | 260 | ```C# 261 | public void ConfigureServices(IServiceCollection services) 262 | { 263 | services.AddSingleton, MyConfigureOptions>(); 264 | } 265 | ``` 266 | 267 | That's a bit verbose, so we added some helpers to make it easier. 268 | 269 | ```C# 270 | public void ConfigureServices(IServiceCollection services) 271 | { 272 | services.AddOptions() 273 | .Configure((options, service) => 274 | { 275 | options.Setting = service.ComputeSetting(); 276 | }); 277 | } 278 | ``` 279 | -------------------------------------------------------------------------------- /2.md: -------------------------------------------------------------------------------- 1 | ## Asynchronous Socket Server 2 | 3 | Below is a modern asynchronous server using modern .NET APIs: 4 | - Task based APIs for asynchronous accept/read/write 5 | - Range syntax for slicing the buffer 6 | 7 | ```C# 8 | using var listenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); 9 | listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, 8080)); 10 | 11 | Console.WriteLine($"Listening on {listenSocket.LocalEndPoint}"); 12 | 13 | listenSocket.Listen(); 14 | 15 | while (true) 16 | { 17 | // Wait for a new connection to arrive 18 | var connection = await listenSocket.AcceptAsync(); 19 | 20 | // We got a new connection spawn a task to so that we can echo the contents of the connection 21 | _ = Task.Run(async () => 22 | { 23 | var buffer = new byte[4096]; 24 | try 25 | { 26 | while (true) 27 | { 28 | int read = await connection.ReceiveAsync(buffer, SocketFlags.None); 29 | if (read == 0) 30 | { 31 | break; 32 | } 33 | await connection.SendAsync(buffer[..read], SocketFlags.None); 34 | } 35 | } 36 | finally 37 | { 38 | connection.Dispose(); 39 | } 40 | }); 41 | } 42 | ``` 43 | 44 | ## Asynchronous Socket Client 45 | 46 | Below is a modern asynchronous client using modern .NET APIs: 47 | - Uses Task based APIs to establish the connection. 48 | - Creates a `NetworkStream` over the `Socket` in order to use `CopyToAsync`, a helper that makes it easy to copy content from one `Stream` to another. 49 | 50 | ```C# 51 | using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); 52 | await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 8080)); 53 | 54 | Console.WriteLine("Type into the console to echo the contents"); 55 | 56 | var ns = new NetworkStream(socket); 57 | var readTask = Console.OpenStandardInput().CopyToAsync(ns); 58 | var writeTask = ns.CopyToAsync(Console.OpenStandardOutput()); 59 | 60 | // Quit if any of the tasks complete 61 | await Task.WhenAny(readTask, writeTask); 62 | ``` 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .NET coding patterns 2 | 3 | A collection of interesting coding patterns in no particular order. 4 | --------------------------------------------------------------------------------