Skip to content

Latest commit

 

History

History
222 lines (189 loc) · 6.62 KB

File metadata and controls

222 lines (189 loc) · 6.62 KB

Simplified lifetime-specific bindings

You can use the Transient<>(), Singleton<>(), PerResolve<>(), etc. methods. In this case binding will be performed for the implementation type itself, and if the implementation is not an abstract type or structure, for all abstract but NOT special types that are directly implemented. This keeps lifetime configuration concise while preserving explicit lifetime semantics.

using System.Collections;
using Pure.DI;

// Specifies to create a partial class "Composition"
DI.Setup(nameof(Composition))
    // The equivalent of the following:
    // .Bind<IOrderRepository, IOrderNotification, OrderManager>()
    //   .As(Lifetime.PerBlock)
    //   .To<OrderManager>()
    .PerBlock<OrderManager>()
    // The equivalent of the following:
    // .Bind<IShop, Shop>()
    //   .As(Lifetime.Transient)
    //   .To<Shop>()
    // .Bind<IOrderNameFormatter, OrderNameFormatter>()
    //   .As(Lifetime.Transient)
    //   .To<OrderNameFormatter>()
    .Transient<Shop, OrderNameFormatter>()

    // Specifies to create a property "MyShop"
    .Root<IShop>("MyShop");

var composition = new Composition();
var shop = composition.MyShop;

interface IManager;

class ManagerBase : IManager;

interface IOrderRepository;

interface IOrderNotification;

class OrderManager(IOrderNameFormatter orderNameFormatter) :
    ManagerBase,
    IOrderRepository,
    IOrderNotification,
    IDisposable,
    IEnumerable<string>
{
    public void Dispose() {}

    public IEnumerator<string> GetEnumerator() =>
        new List<string>
        {
            orderNameFormatter.Format(1),
            orderNameFormatter.Format(2)
        }.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

interface IOrderNameFormatter
{
    string Format(int orderId);
}

class OrderNameFormatter : IOrderNameFormatter
{
    public string Format(int orderId) => $"Order #{orderId}";
}

interface IShop;

class Shop(
    OrderManager manager,
    IOrderRepository repository,
    IOrderNotification notification)
    : IShop;
Running this code sample locally
dotnet --list-sdk
  • Create a net10.0 (or later) console application
dotnet new console -n Sample
  • Add a reference to the NuGet package
dotnet add package Pure.DI
  • Copy the example code into the Program.cs file

You are ready to run the example 🚀

dotnet run

These methods perform the binding with appropriate lifetime:

  • with the implementation type itself
  • and if it is NOT an abstract type or structure
    • with all abstract types that it directly implements
    • exceptions are special types

Special types will not be added to bindings:

  • System.Object
  • System.Enum
  • System.MulticastDelegate
  • System.Delegate
  • System.Collections.IEnumerable
  • System.Collections.Generic.IEnumerable<T>
  • System.Collections.Generic.IList<T>
  • System.Collections.Generic.ICollection<T>
  • System.Collections.IEnumerator
  • System.Collections.Generic.IEnumerator<T>
  • System.Collections.Generic.IReadOnlyList<T>
  • System.Collections.Generic.IReadOnlyCollection<T>
  • System.IDisposable
  • System.IAsyncResult
  • System.AsyncCallback

If you want to add your own special type, use the SpecialType<T>() call.

For class OrderManager, the PerBlock<OrderManager>() binding will be equivalent to the Bind<IOrderRepository, IOrderNotification, OrderManager>().As(Lifetime.PerBlock).To<OrderManager>() binding. The types IDisposable, IEnumerable<string> did not get into the binding because they are special from the list above. ManagerBase did not get into the binding because it is not abstract. IManager is not included because it is not implemented directly by class OrderManager.

yes OrderManager implementation type itself
yes IOrderRepository directly implements
yes IOrderNotification directly implements
no IDisposable special type
no IEnumerable<string> special type
no ManagerBase non-abstract
no IManager is not directly implemented by class OrderManager
Limitations: lifetime-specific shortcuts still rely on inferred contracts, so review inferred bindings carefully.
Common pitfalls:
  • Applying singleton shortcuts to stateful services without thread-safety guarantees.
  • Assuming shortcut APIs bypass special-type exclusion rules. See also: Transient, Simplified binding.

The following partial class will be generated:

partial class Composition
{
  public IShop MyShop
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get
    {
      var perBlockOrderManager348 = new OrderManager(new OrderNameFormatter());
      return new Shop(perBlockOrderManager348, perBlockOrderManager348, perBlockOrderManager348);
    }
  }
}

Class diagram:

---
 config:
  maxTextSize: 2147483647
  maxEdges: 2147483647
  class:
   hideEmptyMembersBox: true
---
classDiagram
	OrderManager --|> IOrderRepository
	OrderManager --|> IOrderNotification
	OrderManager --|> IEnumerableᐸStringᐳ
	Shop --|> IShop
	OrderNameFormatter --|> IOrderNameFormatter
	Composition ..> Shop : IShop MyShop
	OrderManager *--  OrderNameFormatter : IOrderNameFormatter
	Shop o-- "PerBlock" OrderManager : OrderManager
	Shop o-- "PerBlock" OrderManager : IOrderRepository
	Shop o-- "PerBlock" OrderManager : IOrderNotification
	namespace Pure.DI.UsageTests.Basics.SimplifiedLifetimeBindingScenario {
		class Composition {
		<<partial>>
		+IShop MyShop
		}
		class IOrderNameFormatter {
			<<interface>>
		}
		class IOrderNotification {
			<<interface>>
		}
		class IOrderRepository {
			<<interface>>
		}
		class IShop {
			<<interface>>
		}
		class OrderManager {
				<<class>>
			+OrderManager(IOrderNameFormatter orderNameFormatter)
		}
		class OrderNameFormatter {
				<<class>>
			+OrderNameFormatter()
		}
		class Shop {
				<<class>>
			+Shop(OrderManager manager, IOrderRepository repository, IOrderNotification notification)
		}
	}
	namespace System.Collections.Generic {
		class IEnumerableᐸStringᐳ {
			<<interface>>
		}
	}
Loading