From 0eac7df49c0916b2b7b9e32f912e064cd1dea335 Mon Sep 17 00:00:00 2001 From: "leonid.umanskiy" Date: Thu, 4 Apr 2019 11:08:15 -0700 Subject: [PATCH 1/3] Added functionality for multi-contract bindings --- .../KrakenIoc.Testing/V1/ContainerTests.cs | 240 ++++++++++++++++++ KrakenIoc/KrakenIoc/Core/V1/Binding.cs | 82 ++++-- KrakenIoc/KrakenIoc/Core/V1/Container.cs | 102 +++++--- .../KrakenIoc/Core/V1/Interfaces/IBinding.cs | 35 ++- .../Core/V1/Interfaces/IContainer.cs | 8 + KrakenIoc/KrakenIoc/KrakenIoc.csproj | 2 +- .../KrakenIoc/Properties/AssemblyInfo.cs | 4 +- 7 files changed, 411 insertions(+), 62 deletions(-) diff --git a/KrakenIoc/KrakenIoc.Testing/V1/ContainerTests.cs b/KrakenIoc/KrakenIoc.Testing/V1/ContainerTests.cs index 4e694f0..fb3897e 100644 --- a/KrakenIoc/KrakenIoc.Testing/V1/ContainerTests.cs +++ b/KrakenIoc/KrakenIoc.Testing/V1/ContainerTests.cs @@ -327,6 +327,36 @@ public void ThrowsExceptionWhenLateBoundTypeDoesNotImplementBinderType() #region Test 2 - DoesResolveSingleton + #region Interface & Implemenation + internal interface ISomeInterface { } + + internal interface IAnotherInterface { } + + internal class SomeTypeImplementsTwoInterfaces : ISomeInterface, IAnotherInterface + { + + } + + internal interface ISomeTypeNine + { + ISomeInterface Some { get; } + IAnotherInterface Another { get; } + } + + internal class SomeTypeNine : ISomeTypeNine + { + public ISomeInterface Some { get; set; } + public IAnotherInterface Another { get; set; } + + public SomeTypeNine(ISomeInterface some, IAnotherInterface another) + { + Some = some; + Another = another; + } + } + + #endregion + [TestMethod] public void DoesResolveSingleton() { @@ -349,6 +379,33 @@ public void DoesResolveSingleton() container = null; } + + [TestMethod] + public void DoesResolveSingletonWhenMultipleInterfacesBound() + { + Container container = new Container(); + + // Transient & Singleton binding... + container.Bind().To(); + + container.Bind(typeof(ISomeInterface), typeof(IAnotherInterface)).To().AsSingleton(); + + // Resolve SomeTypeNine... should be injected + ISomeTypeNine consumer = container.Resolve(); + + Assert.IsNotNull(consumer.Some, "did not inject ISomeInterface"); + Assert.IsNotNull(consumer.Another, "did not inject IAnotherInterface"); + Assert.AreEqual(consumer.Some, consumer.Another, "did not inject singleton"); + + // Resolve singleton + ISomeInterface some = container.Resolve(); + IAnotherInterface another = container.Resolve(); + + Assert.AreEqual(consumer.Some, some, "did not inject singleton"); + Assert.AreEqual(consumer.Some, another, "did not inject singleton"); + + container = null; + } #endregion #region Test 3 - DoesNotAllowConstructorCircularDependency @@ -722,6 +779,16 @@ interface ISomeTypeTest9Factory : IFactory IInjectContext ItemInjectContext { get; } } + interface ISomeTypeImplementsTwoInterfacesFactory : IFactory { } + + interface ISomeTypeImplementsTwoInterfacesNonGenericFactory : IFactory { } + + interface ISomeTypeTest9FactoryNonGeneric : IFactory + { + int NumCreated { get; } + IInjectContext ItemInjectContext { get; } + } + class SomeTypeTest9Factory : Factory, ISomeTypeTest9Factory { public int NumCreated { get; set; } @@ -735,6 +802,45 @@ public override ISomeTypeTest9 Create(IInjectContext injectContext) } } + class SomeTypeImplementsTwoInterfacesFactory : Factory, ISomeTypeImplementsTwoInterfacesFactory + { + public int NumCreated { get; set; } + public IInjectContext ItemInjectContext { get; private set; } + + public override SomeTypeImplementsTwoInterfaces Create(IInjectContext injectContext) + { + NumCreated++; + ItemInjectContext = injectContext; + return new SomeTypeImplementsTwoInterfaces(); + } + } + + class SomeTypeImplementsTwoInterfacesNonGenericFactory : ISomeTypeImplementsTwoInterfacesNonGenericFactory + { + public int NumCreated { get; set; } + public IInjectContext ItemInjectContext { get; private set; } + + public object Create(IInjectContext injectContext) + { + NumCreated++; + ItemInjectContext = injectContext; + return new SomeTypeImplementsTwoInterfaces(); + } + } + + class SomeTypeTest9FactoryNonGeneric : ISomeTypeTest9FactoryNonGeneric + { + public int NumCreated { get; set; } + public IInjectContext ItemInjectContext { get; private set; } + + public object Create(IInjectContext injectContext) + { + NumCreated++; + ItemInjectContext = injectContext; + return new SomeTypeTest9(); + } + } + interface ISomeTypeTest9_B { } class SomeTypeTest9_B : ISomeTypeTest9_B @@ -776,6 +882,98 @@ public void DoesResolveFromFactoryAsSingleton() Assert.AreEqual(1, factory.NumCreated, "did not resolve once"); // must not be 2 } + [TestMethod] + public void DoesResolveFromFactoryWhenMultipleInterfacesAreBoundAsTransient() + { + Container container = new Container(); + var factory = new SomeTypeImplementsTwoInterfacesFactory(); + + container.Bind(typeof(ISomeInterface), typeof(IAnotherInterface)) + .To() + .FromFactory().AsTransient(); + + container.Bind(factory).To().AsSingleton(); + + ISomeInterface someInterface = container.Resolve(); + IAnotherInterface anotherInterface = container.Resolve(); + + Assert.AreEqual(2, factory.NumCreated, "did not resolve exactly twice using factory"); + } + + [TestMethod] + public void DoesResolveFromFactoryWhenMultipleInterfacesAreBoundAsSingleton() + { + Container container = new Container(); + var factory = new SomeTypeImplementsTwoInterfacesFactory(); + + container.Bind(typeof(ISomeInterface), typeof(IAnotherInterface)) + .To() + .FromFactory().AsSingleton(); + + container.Bind(factory).To().AsSingleton(); + + ISomeInterface someInterface = container.Resolve(); + IAnotherInterface anotherInterface = container.Resolve(); + + Assert.AreEqual(1, factory.NumCreated, "did not resolve exactly once using factory"); + + Assert.AreEqual(someInterface, anotherInterface, "Resolved interfaces do not point to the same object reference"); + } + + [TestMethod] + public void DoesResolveFromNonGenericFactory() + { + Container container = new Container(); + ISomeTypeTest9FactoryNonGeneric factory = new SomeTypeTest9FactoryNonGeneric(); + + container.Bind().To().FromFactory().AsTransient(); + container.Bind(factory).To().AsSingleton(); + + ISomeTypeTest9 someTypeTest9 = container.Resolve(); + + Assert.AreEqual(1, factory.NumCreated, "did not resolve using factory"); + } + + [TestMethod] + public void DoesResolveFromNonGenericFactoryWhenMultipleInterfacesAreBoundAsTransient() + { + Container container = new Container(); + var factory = new SomeTypeImplementsTwoInterfacesNonGenericFactory(); + + container.Bind(typeof(ISomeInterface), typeof(IAnotherInterface)) + .To() + .FromFactory() + .AsTransient(); + + container.Bind(factory).To().AsSingleton(); + + ISomeInterface someInterface = container.Resolve(); + IAnotherInterface anotherInterface = container.Resolve(); + + Assert.AreEqual(2, factory.NumCreated, "did not resolve exactly twice using factory"); + } + + [TestMethod] + public void DoesResolveFromNonGenericFactoryWhenMultipleInterfacesAreBoundAsSingleton() + { + Container container = new Container(); + var factory = new SomeTypeImplementsTwoInterfacesNonGenericFactory(); + + container.Bind(typeof(ISomeInterface), typeof(IAnotherInterface)) + .To() + .FromFactory() + .AsSingleton(); + + container.Bind(factory).To().AsSingleton(); + + ISomeInterface someInterface = container.Resolve(); + IAnotherInterface anotherInterface = container.Resolve(); + + Assert.AreEqual(1, factory.NumCreated, "did not resolve exactly once using factory"); + + Assert.AreEqual(someInterface, anotherInterface, "Resolved interfaces do not point to the same object reference"); + } + [TestMethod] public void DoesResolveFromFactoryWithInheritedContainer() { @@ -819,6 +1017,48 @@ public void DoesResolveFromFactoryMethod() Assert.AreEqual(2, numResolved, "did not resolve twice"); } + + [TestMethod] + public void DoesResolveFromFactoryMethodWhenMultipleInterfacesAreBoundAsTransient() + { + Container container = new Container(); + + int numResolved = 0; + + container.Bind(typeof(ISomeInterface), typeof(IAnotherInterface)).To().FromFactoryMethod(delegate (IInjectContext injectContext) + { + numResolved++; + return new SomeTypeImplementsTwoInterfaces(); + + }).AsTransient(); + + ISomeInterface someInterface = container.Resolve(); + IAnotherInterface anotherInterface = container.Resolve(); + + Assert.AreEqual(2, numResolved, "did not resolve exactly twice"); + Assert.AreNotEqual(someInterface, anotherInterface, "two objects should not be equal"); + } + + [TestMethod] + public void DoesResolveFromFactoryMethodWhenMultipleInterfacesAreBoundAsSingleton() + { + Container container = new Container(); + + int numResolved = 0; + + container.Bind(typeof(ISomeInterface), typeof(IAnotherInterface)).To().FromFactoryMethod(delegate (IInjectContext injectContext) + { + numResolved++; + return new SomeTypeImplementsTwoInterfaces(); + + }).AsSingleton(); + + ISomeInterface someInterface = container.Resolve(); + IAnotherInterface anotherInterface = container.Resolve(); + + Assert.AreEqual(1, numResolved, "did not resolve exactly once"); + Assert.AreEqual(someInterface, anotherInterface, "two objects should be equal"); + } [TestMethod] public void DoesResolveFromFactoryMethodWithInheritedContainer() diff --git a/KrakenIoc/KrakenIoc/Core/V1/Binding.cs b/KrakenIoc/KrakenIoc/Core/V1/Binding.cs index e2b2015..d869009 100644 --- a/KrakenIoc/KrakenIoc/Core/V1/Binding.cs +++ b/KrakenIoc/KrakenIoc/Core/V1/Binding.cs @@ -16,16 +16,48 @@ public partial class Binding : IBinding public event Action Resolved; + /// + /// Type of the binding - Transient vs Singleton + /// public BindingType BindingType { get; set; } - public Type BinderType { get; set; } + + /// + /// Binder (contract/interface) types. + /// Array of interfaces bound to one implementation + /// + public Type[] BinderTypes { get; set; } + + /// + /// Bound (concrete) type + /// public Type BoundType { get; set; } + + /// + /// Bound objects - values to resolve + /// public List BoundObjects { get; set; } + + /// + /// Container + /// public IContainer Container { get; set; } + + /// + /// Factory type - used when object is created FromFactory + /// public Type FactoryType { get; set; } + + /// + /// Factory method delegate - used when object is created FromFactoryMethod + /// public FactoryMethod FactoryMethod { get; set; } + /// + /// Cached factory instance + /// private IFactory _cachedFactory = null; + public object Category { get @@ -36,7 +68,7 @@ public object Category { if (_category != null) { - throw new TypeCategoryAlreadyBoundException(BinderType, _category); + throw new TypeCategoryAlreadyBoundException(BoundType, _category); } _category = value; @@ -208,9 +240,12 @@ public void Dissolve() /// The concrete implementation type. public IBinding To() { - if (!BinderType.IsAssignableFrom(typeof(T))) + foreach (var interfaceType in BinderTypes) { - throw new InvalidBindingException($"Can not bind ${BinderType} type to type {typeof(T)}, {typeof(T)} does not implement {BinderType}"); + if (!interfaceType.IsAssignableFrom(typeof(T))) + { + throw new InvalidBindingException($"Can not bind ${interfaceType} type to type {typeof(T)}, {typeof(T)} does not implement {interfaceType}"); + } } BoundType = typeof(T); @@ -220,7 +255,7 @@ public IBinding To() public void Inherit(IBinding binding) { - BinderType = binding.BinderType; + BinderTypes = binding.BinderTypes; BindingType = binding.BindingType; BoundType = binding.BoundType; Category = binding.Category; @@ -233,7 +268,7 @@ public void Inherit(IBinding binding) public void CloneFrom(IBinding binding) { - BinderType = binding.BinderType; + BinderTypes = binding.BinderTypes; BindingType = binding.BindingType; BoundType = binding.BoundType; Category = binding.Category; @@ -247,29 +282,40 @@ public IBinding WithCategory(object category) return this; } - - public IBinding FromFactory() where TFactory : IFactory + + public IBinding FromFactory() where TFactory : IFactory { - if(typeof(T) != BinderType) - { - throw new InvalidBindingException($"Can not bind ${BinderType} type to resolve from Factory of type ${typeof(TFactory)}"); - } - - if(FactoryMethod != null) + if (FactoryMethod != null) { - throw new InvalidBindingException($"Can not bind with FromFactory, binding already uses FromMethod"); + throw new InvalidBindingException($"Can not bind with FromFactory, binding already uses FromFactoryMethod"); } - + FactoryType = typeof(TFactory); return this; } + + public IBinding FromFactory() where TFactory : IFactory + { + foreach(var interfaceType in BinderTypes) + { + if (!interfaceType.IsAssignableFrom(typeof(T))) + { + throw new InvalidBindingException($"Can not bind ${interfaceType} type to resolve from Factory of type ${typeof(TFactory)}"); + } + } + + return FromFactory(); + } public IBinding FromFactoryMethod(FactoryMethod factoryMethod) { - if(!BinderType.IsAssignableFrom(typeof(T))) + foreach (var interfaceType in BinderTypes) { - throw new InvalidBindingException($"Can not bind ${BinderType} type to resolve from Factory Method of type ${typeof(FactoryMethod)}"); + if (!interfaceType.IsAssignableFrom(typeof(T))) + { + throw new InvalidBindingException($"Can not bind ${interfaceType} type to resolve from Factory Method of type ${typeof(FactoryMethod)}"); + } } if(FactoryType != null) diff --git a/KrakenIoc/KrakenIoc/Core/V1/Container.cs b/KrakenIoc/KrakenIoc/Core/V1/Container.cs index 893bbe5..9e81efb 100644 --- a/KrakenIoc/KrakenIoc/Core/V1/Container.cs +++ b/KrakenIoc/KrakenIoc/Core/V1/Container.cs @@ -19,6 +19,7 @@ public class Container : IContainer, IDisposable public LogHandler LogHandler { get; set; } public Container() { } + public Container(IInjector injector) { Injector = injector; @@ -105,7 +106,7 @@ public IBinding Bind(Type type) Binding binding = new Binding { - BinderType = typeof(T), + BinderTypes = new Type[] { typeof(T) }, BoundType = type, Container = this }; @@ -113,7 +114,38 @@ public IBinding Bind(Type type) _bindings[typeof(T)].Add(binding); return binding; - + } + + public IBinding Bind(params Type[] interfaceTypes) + { + if(interfaceTypes == null) + { + throw new InvalidBindingException($"Can not bind multiple interfaces, provided array of interface types is null"); + } + + if(interfaceTypes.Length == 0) + { + throw new InvalidBindingException($"Can not bind multiple interfaces, provided array of interface types is empty"); + } + + Binding binding = new Binding + { + BinderTypes = interfaceTypes, + BoundType = interfaceTypes[0], // By default, bind to a first interface type. Override using To syntax + Container = this + }; + + foreach(var interfaceType in interfaceTypes) + { + if (!_bindings.ContainsKey(interfaceType)) + { + _bindings.Add(interfaceType, new BindingCollection()); + } + + _bindings[interfaceType].Add(binding); + } + + return binding; } private void LogError(string format, params object[] args) @@ -457,61 +489,66 @@ public void Inherit(IContainer container) private void AddInheritedBinding(Binding binding) { - if (!_inheritedBindings.ContainsKey(binding.BinderType)) + foreach (var interfaceType in binding.BinderTypes) { - _inheritedBindings.Add(binding.BinderType, new BindingCollection()); - } + if (!_inheritedBindings.ContainsKey(interfaceType)) + { + _inheritedBindings.Add(interfaceType, new BindingCollection()); + } - var bindingCollection = _inheritedBindings[binding.BinderType]; + var bindingCollection = _inheritedBindings[interfaceType]; - IBinding existingBinding = bindingCollection.GetBindingWithCategory(binding.Category); + IBinding existingBinding = bindingCollection.GetBindingWithCategory(binding.Category); - if (existingBinding == null) - { - bindingCollection.Add(binding); - } - else - { - if (binding.Category == null) + if (existingBinding == null) { - throw new TypeAlreadyBoundException(binding.BinderType); + bindingCollection.Add(binding); } else { - throw new TypeCategoryAlreadyBoundException(binding.BinderType, binding.Category); + if (binding.Category == null) + { + throw new TypeAlreadyBoundException(interfaceType); + } + else + { + throw new TypeCategoryAlreadyBoundException(interfaceType, binding.Category); + } } } } private void AddClonedBinding(Binding binding) { - if (!_clonedBindings.ContainsKey(binding.BinderType)) + foreach(var interfaceType in binding.BinderTypes) { - _clonedBindings.Add(binding.BinderType, new BindingCollection()); - } + if (!_clonedBindings.ContainsKey(interfaceType)) + { + _clonedBindings.Add(interfaceType, new BindingCollection()); + } - var bindingCollection = _clonedBindings[binding.BinderType]; + var bindingCollection = _clonedBindings[interfaceType]; - IBinding existingBinding = bindingCollection.GetBindingWithCategory(binding.Category); + IBinding existingBinding = bindingCollection.GetBindingWithCategory(binding.Category); - if (existingBinding == null) - { - bindingCollection.Add(binding); - } - else - { - if (binding.Category == null) + if (existingBinding == null) { - throw new TypeAlreadyBoundException(binding.BinderType); + bindingCollection.Add(binding); } else { - throw new TypeCategoryAlreadyBoundException(binding.BinderType, binding.Category); + if (binding.Category == null) + { + throw new TypeAlreadyBoundException(interfaceType); + } + else + { + throw new TypeCategoryAlreadyBoundException(interfaceType, binding.Category); + } } } } - - + public void Bootstrap() where T : IBootstrap { Bootstrap(typeof(T)); @@ -523,5 +560,4 @@ public void Bootstrap(Type type) bootstrap?.SetupBindings(this); } } - } \ No newline at end of file diff --git a/KrakenIoc/KrakenIoc/Core/V1/Interfaces/IBinding.cs b/KrakenIoc/KrakenIoc/Core/V1/Interfaces/IBinding.cs index 0eee6b9..7dd76bb 100644 --- a/KrakenIoc/KrakenIoc/Core/V1/Interfaces/IBinding.cs +++ b/KrakenIoc/KrakenIoc/Core/V1/Interfaces/IBinding.cs @@ -3,7 +3,13 @@ namespace AOFL.KrakenIoc.Core.V1.Interfaces { - public delegate T FactoryMethod(IInjectContext injectionContext); + /// + /// Delegate that resolves instance of type T + /// + /// + /// + /// + public delegate T FactoryMethod(IInjectContext injectionContext); /// /// Represents a binding between an interface @@ -27,14 +33,16 @@ public partial interface IBinding /// The lifetime-scope of the binding. /// BindingType BindingType { get; set; } + /// - /// The concrete type that is bound. + /// Array of contracts (interfaces) that this implementation is bound to /// - Type BoundType { get; set; } + Type[] BinderTypes { get; set; } + /// - /// The interface that is binding an implementation. + /// The concrete type that is bound. /// - Type BinderType { get; set; } + Type BoundType { get; set; } /// /// Category of the binding @@ -66,15 +74,26 @@ public partial interface IBinding IBinding WithCategory(object category); /// - /// T will be resolved using Factory class + /// Instance will be resolved using Factory class. + /// Less strict verion of FromFactory that provides resolve-time validation but allows for dynamic logic in a factory. /// /// Factory /// Type being resolved /// - IBinding FromFactory() where TFactory : IFactory; + IBinding FromFactory() where TFactory : IFactory; /// - /// T will be resolved using Factory Method + /// T will be resolved using Factory class. + /// Generic version of FromFactory that provides more binding-time validation + /// + /// Factory + /// Type being resolved + /// + IBinding FromFactory() where TFactory : IFactory; + + /// + /// T will be resolved using Factory Method. + /// T will be validated a the binding time. /// /// Type being resolved /// Factory method diff --git a/KrakenIoc/KrakenIoc/Core/V1/Interfaces/IContainer.cs b/KrakenIoc/KrakenIoc/Core/V1/Interfaces/IContainer.cs index 4a5ea1b..f67d39c 100644 --- a/KrakenIoc/KrakenIoc/Core/V1/Interfaces/IContainer.cs +++ b/KrakenIoc/KrakenIoc/Core/V1/Interfaces/IContainer.cs @@ -98,6 +98,14 @@ public interface IContainer : IDisposable /// IBinding Bind(Type type); + /// + /// Binds multiple interface types against the implementation + /// Type. + /// + /// Interface types + /// + IBinding Bind(params Type[] interfaceTypes); + /// /// Returns an instance of the specified type. /// diff --git a/KrakenIoc/KrakenIoc/KrakenIoc.csproj b/KrakenIoc/KrakenIoc/KrakenIoc.csproj index 04f89a1..60118e2 100644 --- a/KrakenIoc/KrakenIoc/KrakenIoc.csproj +++ b/KrakenIoc/KrakenIoc/KrakenIoc.csproj @@ -58,6 +58,7 @@ + @@ -65,7 +66,6 @@ - diff --git a/KrakenIoc/KrakenIoc/Properties/AssemblyInfo.cs b/KrakenIoc/KrakenIoc/Properties/AssemblyInfo.cs index 5777681..04e6726 100644 --- a/KrakenIoc/KrakenIoc/Properties/AssemblyInfo.cs +++ b/KrakenIoc/KrakenIoc/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.11")] -[assembly: AssemblyFileVersion("2.5.11")] +[assembly: AssemblyVersion("2.6.0")] +[assembly: AssemblyFileVersion("2.6.0")] From c2a1faa864e0b55fa5fc77e76504af79a229432d Mon Sep 17 00:00:00 2001 From: "leonid.umanskiy" Date: Fri, 5 Apr 2019 17:56:55 -0700 Subject: [PATCH 2/3] Updating README.md --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index b0674da..4d7372e 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,12 @@ When abstractions are required, you can pass the implementation type to the `To` container.Bind().To().AsSingleton(); ``` +### Binding Multiple Interfaces to a single Implementation +You can bind multiple interfaces to the same implementation. That way, you can have multiple interfaces referring to the same singleton: +```csharp +container.Bind(typeof(IWebService), typeof(IProductWebService), typeof(IWebConnectionService)).To().AsSingleton(); +``` + ### Binding & Resolving with Categories It is often useful to map different types of implementations. You can do this with categories. ```csharp @@ -107,6 +113,14 @@ container.Bind().FromFactoryMethod((injectContext) => { }); ``` +As an alternative, you can decide to bind a non-generic factory. This is useful when you bind multiple interfaces to one implementations. + +```csharp +// From a factory type... +container.Bind(typeof(IWebService), typeof(IProductWebService), typeof(IWebConnectionService)).To().FromFactory().AsSingleton(); +``` + + ### Bootstraps `Bootstrap`s provide a modular way to bind your services and features. ```csharp From eb53fceeeca3fc0626a802f0dd53eb188d74ced4 Mon Sep 17 00:00:00 2001 From: "leonid.umanskiy" Date: Fri, 5 Apr 2019 17:59:40 -0700 Subject: [PATCH 3/3] Updating KrakenIoc.nuspec --- KrakenIoc/KrakenIoc/KrakenIoc.nuspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/KrakenIoc/KrakenIoc/KrakenIoc.nuspec b/KrakenIoc/KrakenIoc/KrakenIoc.nuspec index 5421f2d..16fcc07 100644 --- a/KrakenIoc/KrakenIoc/KrakenIoc.nuspec +++ b/KrakenIoc/KrakenIoc/KrakenIoc.nuspec @@ -14,7 +14,8 @@ $description$ - - Fixes binding incompatible types (both early and late binds) + - Implemented multi-contract bindings: Bind(typeof(IService), typeof(IMyService)) + - Implemented non-generic factory bindings: FromFactory<INonGenericFactory> $copyright$