From 6acf50692deddbff8d1e27464edc5d6246be51eb Mon Sep 17 00:00:00 2001
From: raymondhuy177 <raymondhuy177@gmail.com>
Date: Sun, 17 Jan 2021 21:18:20 +0700
Subject: [PATCH 1/5] add AsOpenTypesOf method

---
 ...enGenericScanningRegistrationExtensions.cs | 76 +++++++++++++++++++
 ...nExtensions.OpenGenericAssemblyScanning.cs | 50 ++++++++++++
 .../OpenGenericScanningRegistrationTests.cs   | 54 +++++++++++++
 3 files changed, 180 insertions(+)

diff --git a/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs b/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
index c70bb9902..1cd9c2122 100644
--- a/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
+++ b/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
@@ -169,5 +169,81 @@ private static void ConfigureFrom<TActivatorData, TScanStyle, TRegistrationBuild
                 action(type, scanned);
             }
         }
+
+        /// <summary>
+        /// Configures the scanning registration builder to register all open types of the specified open generic.
+        /// </summary>
+        /// <typeparam name="TLimit">The limit type.</typeparam>
+        /// <typeparam name="TRegistrationStyle">The registration style.</typeparam>
+        /// <param name="registration">The registration builder.</param>
+        /// <param name="openGenericServiceType">The open generic to register open types of.</param>
+        /// <returns>The registration builder.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
+            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+                IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
+                Type openGenericServiceType)
+        {
+            if (openGenericServiceType == null)
+            {
+                throw new ArgumentNullException(nameof(openGenericServiceType));
+            }
+
+            return registration
+                .Where(candidateType => candidateType.IsOpenGenericTypeOf(openGenericServiceType))
+                .As(candidateType => (Service)new TypedService(candidateType));
+        }
+
+        /// <summary>
+        /// Configures the scanning registration builder to register all open types of the specified open generic as a keyed service.
+        /// </summary>
+        /// <typeparam name="TLimit">The limit type.</typeparam>
+        /// <typeparam name="TRegistrationStyle">The registration style.</typeparam>
+        /// <param name="registration">The registration builder.</param>
+        /// <param name="openGenericServiceType">The open generic to register open types of.</param>
+        /// <param name="serviceKey">The service key.</param>
+        /// <returns>The registration builder.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
+            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+                IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
+                Type openGenericServiceType,
+                object serviceKey)
+        {
+            if (openGenericServiceType == null)
+            {
+                throw new ArgumentNullException(nameof(openGenericServiceType));
+            }
+
+            if (serviceKey == null)
+            {
+                throw new ArgumentNullException(nameof(serviceKey));
+            }
+
+            return AsOpenTypesOf(registration, openGenericServiceType, t => serviceKey);
+        }
+
+        /// <summary>
+        /// Configures the scanning registration builder to register all open types of the specified open generic as a keyed service.
+        /// </summary>
+        /// <typeparam name="TLimit">The limit type.</typeparam>
+        /// <typeparam name="TRegistrationStyle">The registration style.</typeparam>
+        /// <param name="registration">The registration builder.</param>
+        /// <param name="openGenericServiceType">The open generic to register open types of.</param>
+        /// <param name="serviceKeyMapping">A function to determine the service key for a given type.</param>
+        /// <returns>The registration builder.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
+            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+                IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
+                Type openGenericServiceType,
+                Func<Type, object> serviceKeyMapping)
+        {
+            if (openGenericServiceType == null)
+            {
+                throw new ArgumentNullException(nameof(openGenericServiceType));
+            }
+
+            return registration
+                .Where(candidateType => candidateType.IsOpenGenericTypeOf(openGenericServiceType))
+                .As(candidateType => (Service)new KeyedService(serviceKeyMapping(candidateType), candidateType));
+        }
     }
 }
diff --git a/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs b/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
index 7ce130fed..9ef3b4473 100644
--- a/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
+++ b/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
@@ -202,5 +202,55 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
             registration.ActivatorData.Filters.Add(predicate);
             return registration;
         }
+
+        /// <summary>
+        /// Specifies that an open generic type from a scanned assembly is registered if it implements an interface
+        /// that opens the provided open generic interface type.
+        /// </summary>
+        /// <typeparam name="TLimit">Registration limit type.</typeparam>
+        /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
+        /// <param name="registration">Registration to set service mapping on.</param>
+        /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
+        /// <returns>Registration builder allowing the registration to be configured.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
+            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+                this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType)
+        {
+            return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType);
+        }
+
+        /// <summary>
+        /// Specifies that an open generic type from a scanned assembly is registered if it implements an interface
+        /// that opens the provided open generic interface type.
+        /// </summary>
+        /// <typeparam name="TLimit">Registration limit type.</typeparam>
+        /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
+        /// <param name="registration">Registration to set service mapping on.</param>
+        /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
+        /// <param name="serviceKey">Key of the service.</param>
+        /// <returns>Registration builder allowing the registration to be configured.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
+            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+                this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType, object serviceKey)
+        {
+            return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType, serviceKey);
+        }
+
+        /// <summary>
+        /// Specifies that an open generic type from a scanned assembly is registered if it implements an interface
+        /// that opens the provided open generic interface type.
+        /// </summary>
+        /// <typeparam name="TLimit">Registration limit type.</typeparam>
+        /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
+        /// <param name="registration">Registration to set service mapping on.</param>
+        /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
+        /// <param name="serviceKeyMapping">Function mapping types to service keys.</param>
+        /// <returns>Registration builder allowing the registration to be configured.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
+            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+                this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType, Func<Type, object> serviceKeyMapping)
+        {
+            return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType, serviceKeyMapping);
+        }
     }
 }
diff --git a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
index 620e3f723..1a5a89b66 100644
--- a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
+++ b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
@@ -1,6 +1,7 @@
 // Copyright (c) Autofac Project. All rights reserved.
 // Licensed under the MIT License. See LICENSE in the project root for license information.
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
@@ -97,5 +98,58 @@ public void WhenExceptionsProvideConfigurationComponentConfiguredAppropriately()
             var a2 = c.Resolve<RedoOpenGenericCommand<int>>();
             Assert.Same(a1, a2);
         }
+
+        [Fact]
+        public void AsOpenTypesOfNullTypeProvidedThrowsException()
+        {
+            var cb = new ContainerBuilder();
+            Assert.Throws<ArgumentNullException>(() => cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly).
+                AsOpenTypesOf(null));
+        }
+
+        [Fact]
+        public void AsOpenTypesOfOpenGenericInterfaceTypeProvidedOpenGenericTypesRegistered()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AsOpenTypesOf(typeof(ICommand<>));
+            var c = cb.Build();
+
+            Assert.NotNull(c.Resolve<RedoOpenGenericCommand<int>>());
+        }
+
+        [Fact]
+        public void AsOpenTypesOfOpenGenericAbstractClassTypeProvidedOpenGenericTypesRegistered()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AsOpenTypesOf(typeof(CommandBase<>));
+            var c = cb.Build();
+
+            Assert.NotNull(c.Resolve<RedoOpenGenericCommand<int>>());
+        }
+
+        [Fact]
+        public void AsOpenTypesOfWithServiceKeyShouldAssignKeyToAllRegistrations()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AsOpenTypesOf(typeof(ICommand<>), "command");
+            var c = cb.Build();
+
+            Assert.Throws<ComponentNotRegisteredException>(() => c.Resolve<DeleteOpenGenericCommand<int>>());
+            Assert.NotNull(c.ResolveKeyed<RedoOpenGenericCommand<int>>("command"));
+        }
+
+        [Fact]
+        public void AsOpenTypesOfWithServiceKeyMappingShouldAssignKeyResultToEachRegistration()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AsOpenTypesOf(typeof(ICommand<>), t => t);
+            var c = cb.Build();
+
+            Assert.NotNull(c.ResolveKeyed<RedoOpenGenericCommand<int>>(typeof(RedoOpenGenericCommand<>)));
+        }
     }
 }

From 5f13fe20d69e1678bd09fae66cb773fe9dde1a33 Mon Sep 17 00:00:00 2001
From: raymondhuy177 <raymondhuy177@gmail.com>
Date: Sat, 23 Jan 2021 16:54:06 +0700
Subject: [PATCH 2/5] add WithMetadata, WithMetadataFrom,
 AsImplementedInterfaces method

---
 ...enGenericScanningRegistrationExtensions.cs | 60 ++++++++++++
 ...RegistrationExtensions.AssemblyScanning.cs |  8 ++
 ...nExtensions.OpenGenericAssemblyScanning.cs | 64 +++++++++++++
 src/Autofac/Util/TypeExtensions.cs            |  1 -
 .../OpenGenericScannedComponentWithName.cs    | 10 ++
 .../OpenGenericAComponent.cs                  |  9 ++
 .../OpenGenericScanningRegistrationTests.cs   | 92 +++++++++++++++++++
 7 files changed, 243 insertions(+), 1 deletion(-)
 create mode 100644 test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/OpenGenericScannedComponentWithName.cs
 create mode 100644 test/Autofac.Test.Scenarios.ScannedAssembly/OpenGenericAComponent.cs

diff --git a/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs b/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
index 1cd9c2122..ef68ba6de 100644
--- a/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
+++ b/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Reflection;
 using Autofac.Builder;
@@ -245,5 +246,64 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
                 .Where(candidateType => candidateType.IsOpenGenericTypeOf(openGenericServiceType))
                 .As(candidateType => (Service)new KeyedService(serviceKeyMapping(candidateType), candidateType));
         }
+
+        /// <summary>
+        /// Specify how an open generic type from a scanned assembly provides metadata.
+        /// </summary>
+        /// <typeparam name="TLimit">Registration limit type.</typeparam>
+        /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
+        /// <param name="registration">Registration to set metadata on.</param>
+        /// <param name="metadataMapping">A function mapping the type to a list of metadata items.</param>
+        /// <returns>Registration builder allowing the registration to be configured.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
+            WithMetadata<TLimit, TRegistrationStyle>(
+                this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
+                Func<Type, IEnumerable<KeyValuePair<string, object?>>> metadataMapping)
+        {
+            if (registration == null)
+            {
+                throw new ArgumentNullException(nameof(registration));
+            }
+
+            registration.ActivatorData.ConfigurationActions.Add((t, rb) => rb.WithMetadata(metadataMapping(t)));
+            return registration;
+        }
+
+        /// <summary>
+        /// Use the properties of an attribute (or interface implemented by an attribute) on the scanned type
+        /// to provide metadata values.
+        /// </summary>
+        /// <remarks>Inherited attributes are supported; however, there must be at most one matching attribute
+        /// in the inheritance chain.</remarks>
+        /// <typeparam name="TAttribute">The attribute applied to the scanned type.</typeparam>
+        /// <param name="registration">Registration to set metadata on.</param>
+        /// <returns>Registration builder allowing the registration to be configured.</returns>
+        public static IRegistrationBuilder<object, OpenGenericScanningActivatorData, DynamicRegistrationStyle>
+            WithMetadataFrom<TAttribute>(
+                this IRegistrationBuilder<object, OpenGenericScanningActivatorData, DynamicRegistrationStyle> registration)
+        {
+            var attrType = typeof(TAttribute);
+            var metadataProperties = attrType
+                .GetRuntimeProperties()
+                .Where(pi => pi.CanRead);
+
+            return registration.WithMetadata(t =>
+            {
+                var attrs = t.GetCustomAttributes(true).OfType<TAttribute>().ToList();
+
+                if (attrs.Count == 0)
+                {
+                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, RegistrationExtensionsResources.MetadataAttributeNotFound, typeof(TAttribute), t));
+                }
+
+                if (attrs.Count != 1)
+                {
+                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, RegistrationExtensionsResources.MultipleMetadataAttributesSameType, typeof(TAttribute), t));
+                }
+
+                var attr = attrs[0];
+                return metadataProperties.Select(p => new KeyValuePair<string, object?>(p.Name, p.GetValue(attr, null)));
+            });
+        }
     }
 }
diff --git a/src/Autofac/RegistrationExtensions.AssemblyScanning.cs b/src/Autofac/RegistrationExtensions.AssemblyScanning.cs
index 2e4112ffc..f7efe15e5 100644
--- a/src/Autofac/RegistrationExtensions.AssemblyScanning.cs
+++ b/src/Autofac/RegistrationExtensions.AssemblyScanning.cs
@@ -245,6 +245,14 @@ private static Type[] GetImplementedInterfaces(Type type)
             return type.IsInterface ? interfaces.AppendItem(type).ToArray() : interfaces.ToArray();
         }
 
+        private static Type[] GetOpenGenericImplementedInterfaces(this Type @this)
+        {
+            return @this.GetInterfaces()
+                .Where(it => it.IsGenericType)
+                .Select(it => it.GetGenericTypeDefinition())
+                .ToArray();
+        }
+
         /// <summary>
         /// Specifies that the components being registered should only be made the default for services
         /// that have not already been registered.
diff --git a/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs b/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
index 9ef3b4473..a9ca1e1b2 100644
--- a/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
+++ b/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
@@ -252,5 +252,69 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
         {
             return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType, serviceKeyMapping);
         }
+
+        /// <summary>
+        /// Filters the scanned open generic types to include only those in the namespace of the provided type
+        /// or one of its sub-namespaces.
+        /// </summary>
+        /// <param name="registration">Registration to filter types from.</param>
+        /// <typeparam name="T">A type in the target namespace.</typeparam>
+        /// <returns>Registration builder allowing the registration to be configured.</returns>
+        public static IRegistrationBuilder<object, OpenGenericScanningActivatorData, DynamicRegistrationStyle>
+            InNamespaceOf<T>(this IRegistrationBuilder<object, OpenGenericScanningActivatorData, DynamicRegistrationStyle> registration)
+        {
+            if (registration == null)
+            {
+                throw new ArgumentNullException(nameof(registration));
+            }
+
+            // Namespace is always non-null for concrete type parameters.
+            return registration.InNamespace(typeof(T).Namespace!);
+        }
+
+        /// <summary>
+        /// Filters the scanned types to include only those in the provided namespace
+        /// or one of its sub-namespaces.
+        /// </summary>
+        /// <typeparam name="TLimit">Registration limit type.</typeparam>
+        /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
+        /// <param name="registration">Registration to filter types from.</param>
+        /// <param name="ns">The namespace from which types will be selected.</param>
+        /// <returns>Registration builder allowing the registration to be configured.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
+            InNamespace<TLimit, TRegistrationStyle>(
+                this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
+                string ns)
+        {
+            if (registration == null)
+            {
+                throw new ArgumentNullException(nameof(registration));
+            }
+
+            if (ns == null)
+            {
+                throw new ArgumentNullException(nameof(ns));
+            }
+
+            return registration.Where(t => t.IsInNamespace(ns));
+        }
+
+        /// <summary>
+        /// Specifies that an open generic type from a scanned assembly is registered as providing all of its
+        /// implemented interfaces.
+        /// </summary>
+        /// <typeparam name="TLimit">Registration limit type.</typeparam>
+        /// <param name="registration">Registration to set service mapping on.</param>
+        /// <returns>Registration builder allowing the registration to be configured.</returns>
+        public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, DynamicRegistrationStyle>
+            AsImplementedInterfaces<TLimit>(this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, DynamicRegistrationStyle> registration)
+        {
+            if (registration == null)
+            {
+                throw new ArgumentNullException(nameof(registration));
+            }
+
+            return registration.As(t => t.GetOpenGenericImplementedInterfaces());
+        }
     }
 }
diff --git a/src/Autofac/Util/TypeExtensions.cs b/src/Autofac/Util/TypeExtensions.cs
index ac2b9d3e6..795ddcced 100644
--- a/src/Autofac/Util/TypeExtensions.cs
+++ b/src/Autofac/Util/TypeExtensions.cs
@@ -223,7 +223,6 @@ private static bool CheckBaseTypeIsOpenGenericTypeOf(this Type @this, Type type)
 
         private static bool CheckInterfacesAreOpenGenericTypeOf(this Type @this, Type type)
         {
-            var interfaces = @this.GetInterfaces().ToList();
             return @this.GetInterfaces()
                 .Any(it => it.IsGenericType
                     ? it.GetGenericTypeDefinition().IsOpenGenericTypeOf(type)
diff --git a/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/OpenGenericScannedComponentWithName.cs b/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/OpenGenericScannedComponentWithName.cs
new file mode 100644
index 000000000..db83d64f4
--- /dev/null
+++ b/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/OpenGenericScannedComponentWithName.cs
@@ -0,0 +1,10 @@
+// Copyright (c) Autofac Project. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace Autofac.Test.Scenarios.ScannedAssembly.MetadataAttributeScanningScenario
+{
+    [Name("My Name")]
+    public class OpenGenericScannedComponentWithName<T>
+    {
+    }
+}
diff --git a/test/Autofac.Test.Scenarios.ScannedAssembly/OpenGenericAComponent.cs b/test/Autofac.Test.Scenarios.ScannedAssembly/OpenGenericAComponent.cs
new file mode 100644
index 000000000..6f7b47fdc
--- /dev/null
+++ b/test/Autofac.Test.Scenarios.ScannedAssembly/OpenGenericAComponent.cs
@@ -0,0 +1,9 @@
+// Copyright (c) Autofac Project. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace Autofac.Test.Scenarios.ScannedAssembly
+{
+    public class OpenGenericAComponent<T>
+    {
+    }
+}
diff --git a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
index 1a5a89b66..c243ffe77 100644
--- a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
+++ b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
@@ -8,8 +8,10 @@
 using Autofac.Core;
 using Autofac.Core.Lifetime;
 using Autofac.Core.Registration;
+using Autofac.Features.Metadata;
 using Autofac.Features.Scanning;
 using Autofac.Test.Scenarios.ScannedAssembly;
+using Autofac.Test.Scenarios.ScannedAssembly.MetadataAttributeScanningScenario;
 using Xunit;
 
 namespace Autofac.Test.Features.Scanning
@@ -151,5 +153,95 @@ public void AsOpenTypesOfWithServiceKeyMappingShouldAssignKeyResultToEachRegistr
 
             Assert.NotNull(c.ResolveKeyed<RedoOpenGenericCommand<int>>(typeof(RedoOpenGenericCommand<>)));
         }
+
+        [Fact]
+        public void AsImplementedInterfacesRegistersImplementedInterfaces()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AsImplementedInterfaces();
+            var c = cb.Build();
+
+            Assert.NotNull(c.Resolve<IOpenGenericAService<int>>());
+        }
+
+        [Fact]
+        public void WhenFilterAppliedDefaultSelfRegistrationOmitted()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AsImplementedInterfaces();
+            var c = cb.Build();
+
+            Assert.Throws<ComponentNotRegisteredException>(() => c.Resolve<OpenGenericAComponent<int>>());
+        }
+
+        [Fact]
+        public void AsSelfExposesConcreteTypeAsService()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AsImplementedInterfaces()
+                .AsSelf();
+            var c = cb.Build();
+
+            Assert.NotNull(c.Resolve<OpenGenericAComponent<int>>());
+        }
+
+        [Fact]
+        public void WhenMetadataMappingAppliedValuesCalculatedFromType()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .WithMetadata(t => t.GetMethods().ToDictionary(m => m.Name, m => (object)m.ReturnType));
+
+            var c = cb.Build();
+            var s = c.Resolve<Meta<RedoOpenGenericCommand<int>>>();
+
+            Assert.True(s.Metadata.ContainsKey("Execute"));
+        }
+
+        [Fact]
+        public void MetadataCanBeScannedFromAMatchingAttributeInterface()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .Where(t => t == typeof(OpenGenericScannedComponentWithName<>))
+                .WithMetadataFrom<IHaveName>();
+
+            var c = cb.Build();
+
+            c.ComponentRegistry.TryGetRegistration(new TypedService(typeof(OpenGenericScannedComponentWithName<string>)), out IComponentRegistration r);
+
+            r.Metadata.TryGetValue("Name", out object name);
+
+            Assert.Equal("My Name", name);
+        }
+
+        [Fact]
+        public void InNamespaceLimitsServicesToBeRegistered()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .InNamespace("Autofac.Test.Scenarios.ScannedAssembly.MetadataAttributeScanningScenario");
+
+            var c = cb.Build();
+
+            Assert.NotNull(c.Resolve<OpenGenericScannedComponentWithName<int>>());
+            Assert.Throws<ComponentNotRegisteredException>(() => c.Resolve<OpenGenericAComponent<int>>());
+        }
+
+        [Fact]
+        public void InNamespaceOfLimitsServicesToBeRegistered()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .InNamespaceOf<IHaveName>();
+
+            var c = cb.Build();
+
+            Assert.NotNull(c.Resolve<OpenGenericScannedComponentWithName<int>>());
+            Assert.Throws<ComponentNotRegisteredException>(() => c.Resolve<OpenGenericAComponent<int>>());
+        }
     }
 }

From ec726238404f92fba622e4aafbda7fd7a2da41e0 Mon Sep 17 00:00:00 2001
From: raymondhuy177 <raymondhuy177@gmail.com>
Date: Sat, 30 Jan 2021 22:50:30 +0700
Subject: [PATCH 3/5] Change method name

---
 ...enGenericScanningRegistrationExtensions.cs |  6 ++---
 ...nExtensions.OpenGenericAssemblyScanning.cs | 23 ++++++++-----------
 .../OpenGenericScanningRegistrationTests.cs   | 22 +++++++++---------
 3 files changed, 24 insertions(+), 27 deletions(-)

diff --git a/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs b/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
index ef68ba6de..7f94ad3d4 100644
--- a/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
+++ b/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
@@ -172,15 +172,15 @@ private static void ConfigureFrom<TActivatorData, TScanStyle, TRegistrationBuild
         }
 
         /// <summary>
-        /// Configures the scanning registration builder to register all open types of the specified open generic.
+        /// Filters the scanned types to include only those assignable to the provided.
         /// </summary>
         /// <typeparam name="TLimit">The limit type.</typeparam>
         /// <typeparam name="TRegistrationStyle">The registration style.</typeparam>
         /// <param name="registration">The registration builder.</param>
-        /// <param name="openGenericServiceType">The open generic to register open types of.</param>
+        /// <param name="openGenericServiceType">The type or interface which all classes must be assignable from.</param>
         /// <returns>The registration builder.</returns>
         public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
-            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+            AssignableTo<TLimit, TRegistrationStyle>(
                 IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
                 Type openGenericServiceType)
         {
diff --git a/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs b/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
index a9ca1e1b2..0ca357a7c 100644
--- a/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
+++ b/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
@@ -204,50 +204,47 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
         }
 
         /// <summary>
-        /// Specifies that an open generic type from a scanned assembly is registered if it implements an interface
-        /// that opens the provided open generic interface type.
+        /// Filters the scanned types to include only those assignable to the provided.
         /// </summary>
         /// <typeparam name="TLimit">Registration limit type.</typeparam>
         /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
         /// <param name="registration">Registration to set service mapping on.</param>
-        /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
+        /// <param name="openGenericServiceType">The type or interface which all classes must be assignable from.</param>
         /// <returns>Registration builder allowing the registration to be configured.</returns>
         public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
-            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+            AssignableTo<TLimit, TRegistrationStyle>(
                 this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType)
         {
-            return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType);
+            return ScanningRegistrationExtensions.AssignableTo(registration, openGenericServiceType);
         }
 
         /// <summary>
-        /// Specifies that an open generic type from a scanned assembly is registered if it implements an interface
-        /// that opens the provided open generic interface type.
+        /// Filters the scanned types to include only those assignable to the provided.
         /// </summary>
         /// <typeparam name="TLimit">Registration limit type.</typeparam>
         /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
         /// <param name="registration">Registration to set service mapping on.</param>
-        /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
+        /// <param name="openGenericServiceType">The type or interface which all classes must be assignable from.</param>
         /// <param name="serviceKey">Key of the service.</param>
         /// <returns>Registration builder allowing the registration to be configured.</returns>
         public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
-            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+            AssignableTo<TLimit, TRegistrationStyle>(
                 this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType, object serviceKey)
         {
             return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType, serviceKey);
         }
 
         /// <summary>
-        /// Specifies that an open generic type from a scanned assembly is registered if it implements an interface
-        /// that opens the provided open generic interface type.
+        /// Filters the scanned types to include only those assignable to the provided.
         /// </summary>
         /// <typeparam name="TLimit">Registration limit type.</typeparam>
         /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
         /// <param name="registration">Registration to set service mapping on.</param>
-        /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
+        /// <param name="openGenericServiceType">The type or interface which all classes must be assignable from.</param>
         /// <param name="serviceKeyMapping">Function mapping types to service keys.</param>
         /// <returns>Registration builder allowing the registration to be configured.</returns>
         public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
-            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+            AssignableTo<TLimit, TRegistrationStyle>(
                 this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType, Func<Type, object> serviceKeyMapping)
         {
             return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType, serviceKeyMapping);
diff --git a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
index c243ffe77..18b711697 100644
--- a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
+++ b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
@@ -102,41 +102,41 @@ public void WhenExceptionsProvideConfigurationComponentConfiguredAppropriately()
         }
 
         [Fact]
-        public void AsOpenTypesOfNullTypeProvidedThrowsException()
+        public void AssignableToNullTypeProvidedThrowsException()
         {
             var cb = new ContainerBuilder();
-            Assert.Throws<ArgumentNullException>(() => cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly).
-                AsOpenTypesOf(null));
+            Assert.Throws<ArgumentNullException>(() => cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AssignableTo(null));
         }
 
         [Fact]
-        public void AsOpenTypesOfOpenGenericInterfaceTypeProvidedOpenGenericTypesRegistered()
+        public void AssignableToOpenGenericInterfaceTypeProvidedOpenGenericTypesRegistered()
         {
             var cb = new ContainerBuilder();
             cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
-                .AsOpenTypesOf(typeof(ICommand<>));
+                .AssignableTo(typeof(ICommand<>));
             var c = cb.Build();
 
             Assert.NotNull(c.Resolve<RedoOpenGenericCommand<int>>());
         }
 
         [Fact]
-        public void AsOpenTypesOfOpenGenericAbstractClassTypeProvidedOpenGenericTypesRegistered()
+        public void AssignableToOpenGenericAbstractClassTypeProvidedOpenGenericTypesRegistered()
         {
             var cb = new ContainerBuilder();
             cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
-                .AsOpenTypesOf(typeof(CommandBase<>));
+                .AssignableTo(typeof(CommandBase<>));
             var c = cb.Build();
 
             Assert.NotNull(c.Resolve<RedoOpenGenericCommand<int>>());
         }
 
         [Fact]
-        public void AsOpenTypesOfWithServiceKeyShouldAssignKeyToAllRegistrations()
+        public void AssignableToWithServiceKeyShouldAssignKeyToAllRegistrations()
         {
             var cb = new ContainerBuilder();
             cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
-                .AsOpenTypesOf(typeof(ICommand<>), "command");
+                .AssignableTo(typeof(ICommand<>), "command");
             var c = cb.Build();
 
             Assert.Throws<ComponentNotRegisteredException>(() => c.Resolve<DeleteOpenGenericCommand<int>>());
@@ -144,11 +144,11 @@ public void AsOpenTypesOfWithServiceKeyShouldAssignKeyToAllRegistrations()
         }
 
         [Fact]
-        public void AsOpenTypesOfWithServiceKeyMappingShouldAssignKeyResultToEachRegistration()
+        public void AssignableToWithServiceKeyMappingShouldAssignKeyResultToEachRegistration()
         {
             var cb = new ContainerBuilder();
             cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
-                .AsOpenTypesOf(typeof(ICommand<>), t => t);
+                .AssignableTo(typeof(ICommand<>), t => t);
             var c = cb.Build();
 
             Assert.NotNull(c.ResolveKeyed<RedoOpenGenericCommand<int>>(typeof(RedoOpenGenericCommand<>)));

From 67e08776f26ee4b1037704b4310f4c0df8d25529 Mon Sep 17 00:00:00 2001
From: raymondhuy177 <raymondhuy177@gmail.com>
Date: Sun, 31 Jan 2021 00:36:48 +0700
Subject: [PATCH 4/5] add missing testcase

---
 ...enGenericScanningRegistrationExtensions.cs | 19 +++----
 ...nExtensions.OpenGenericAssemblyScanning.cs | 21 ++------
 .../DuplicatedNameAttribute.cs                | 17 +++++++
 ...enericScannedComponentWithMultipleNames.cs | 11 ++++
 .../OpenGenericScanningRegistrationTests.cs   | 50 +++++++++++++++++++
 5 files changed, 88 insertions(+), 30 deletions(-)
 create mode 100644 test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/DuplicatedNameAttribute.cs
 create mode 100644 test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/OpenGenericScannedComponentWithMultipleNames.cs

diff --git a/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs b/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
index 7f94ad3d4..ea68d35e1 100644
--- a/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
+++ b/src/Autofac/Features/Scanning/OpenGenericScanningRegistrationExtensions.cs
@@ -195,16 +195,16 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
         }
 
         /// <summary>
-        /// Configures the scanning registration builder to register all open types of the specified open generic as a keyed service.
+        /// Filters the scanned types to include only those assignable to the provided.
         /// </summary>
         /// <typeparam name="TLimit">The limit type.</typeparam>
         /// <typeparam name="TRegistrationStyle">The registration style.</typeparam>
         /// <param name="registration">The registration builder.</param>
-        /// <param name="openGenericServiceType">The open generic to register open types of.</param>
+        /// <param name="openGenericServiceType">The type or interface which all classes must be assignable from.</param>
         /// <param name="serviceKey">The service key.</param>
         /// <returns>The registration builder.</returns>
         public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
-            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+            AssignableTo<TLimit, TRegistrationStyle>(
                 IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
                 Type openGenericServiceType,
                 object serviceKey)
@@ -219,20 +219,20 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
                 throw new ArgumentNullException(nameof(serviceKey));
             }
 
-            return AsOpenTypesOf(registration, openGenericServiceType, t => serviceKey);
+            return AssignableTo(registration, openGenericServiceType, t => serviceKey);
         }
 
         /// <summary>
-        /// Configures the scanning registration builder to register all open types of the specified open generic as a keyed service.
+        /// Filters the scanned types to include only those assignable to the provided.
         /// </summary>
         /// <typeparam name="TLimit">The limit type.</typeparam>
         /// <typeparam name="TRegistrationStyle">The registration style.</typeparam>
         /// <param name="registration">The registration builder.</param>
-        /// <param name="openGenericServiceType">The open generic to register open types of.</param>
+        /// <param name="openGenericServiceType">The type or interface which all classes must be assignable from.</param>
         /// <param name="serviceKeyMapping">A function to determine the service key for a given type.</param>
         /// <returns>The registration builder.</returns>
         public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle>
-            AsOpenTypesOf<TLimit, TRegistrationStyle>(
+            AssignableTo<TLimit, TRegistrationStyle>(
                 IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
                 Type openGenericServiceType,
                 Func<Type, object> serviceKeyMapping)
@@ -260,11 +260,6 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
                 this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
                 Func<Type, IEnumerable<KeyValuePair<string, object?>>> metadataMapping)
         {
-            if (registration == null)
-            {
-                throw new ArgumentNullException(nameof(registration));
-            }
-
             registration.ActivatorData.ConfigurationActions.Add((t, rb) => rb.WithMetadata(metadataMapping(t)));
             return registration;
         }
diff --git a/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs b/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
index 0ca357a7c..947f3ff51 100644
--- a/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
+++ b/src/Autofac/RegistrationExtensions.OpenGenericAssemblyScanning.cs
@@ -231,7 +231,7 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
             AssignableTo<TLimit, TRegistrationStyle>(
                 this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType, object serviceKey)
         {
-            return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType, serviceKey);
+            return ScanningRegistrationExtensions.AssignableTo(registration, openGenericServiceType, serviceKey);
         }
 
         /// <summary>
@@ -247,7 +247,7 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
             AssignableTo<TLimit, TRegistrationStyle>(
                 this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType, Func<Type, object> serviceKeyMapping)
         {
-            return ScanningRegistrationExtensions.AsOpenTypesOf(registration, openGenericServiceType, serviceKeyMapping);
+            return ScanningRegistrationExtensions.AssignableTo(registration, openGenericServiceType, serviceKeyMapping);
         }
 
         /// <summary>
@@ -260,11 +260,6 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
         public static IRegistrationBuilder<object, OpenGenericScanningActivatorData, DynamicRegistrationStyle>
             InNamespaceOf<T>(this IRegistrationBuilder<object, OpenGenericScanningActivatorData, DynamicRegistrationStyle> registration)
         {
-            if (registration == null)
-            {
-                throw new ArgumentNullException(nameof(registration));
-            }
-
             // Namespace is always non-null for concrete type parameters.
             return registration.InNamespace(typeof(T).Namespace!);
         }
@@ -283,12 +278,7 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
                 this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRegistrationStyle> registration,
                 string ns)
         {
-            if (registration == null)
-            {
-                throw new ArgumentNullException(nameof(registration));
-            }
-
-            if (ns == null)
+            if (string.IsNullOrEmpty(ns))
             {
                 throw new ArgumentNullException(nameof(ns));
             }
@@ -306,11 +296,6 @@ public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, TRe
         public static IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, DynamicRegistrationStyle>
             AsImplementedInterfaces<TLimit>(this IRegistrationBuilder<TLimit, OpenGenericScanningActivatorData, DynamicRegistrationStyle> registration)
         {
-            if (registration == null)
-            {
-                throw new ArgumentNullException(nameof(registration));
-            }
-
             return registration.As(t => t.GetOpenGenericImplementedInterfaces());
         }
     }
diff --git a/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/DuplicatedNameAttribute.cs b/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/DuplicatedNameAttribute.cs
new file mode 100644
index 000000000..2c6c9cf98
--- /dev/null
+++ b/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/DuplicatedNameAttribute.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Autofac Project. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System;
+
+namespace Autofac.Test.Scenarios.ScannedAssembly.MetadataAttributeScanningScenario
+{
+    public class DuplicatedNameAttribute : Attribute, IHaveName
+    {
+        public DuplicatedNameAttribute(string name)
+        {
+            Name = name ?? throw new ArgumentNullException("name");
+        }
+
+        public string Name { get; }
+    }
+}
diff --git a/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/OpenGenericScannedComponentWithMultipleNames.cs b/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/OpenGenericScannedComponentWithMultipleNames.cs
new file mode 100644
index 000000000..47f50a06b
--- /dev/null
+++ b/test/Autofac.Test.Scenarios.ScannedAssembly/MetadataAttributeScanningScenario/OpenGenericScannedComponentWithMultipleNames.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Autofac Project. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace Autofac.Test.Scenarios.ScannedAssembly.MetadataAttributeScanningScenario
+{
+    [Name("My Name")]
+    [DuplicatedName("My Name 2")]
+    public class OpenGenericScannedComponentWithMultipleNames<T>
+    {
+    }
+}
diff --git a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
index 18b711697..785b61c2b 100644
--- a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
+++ b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Reflection;
 using Autofac.Core;
@@ -107,6 +108,15 @@ public void AssignableToNullTypeProvidedThrowsException()
             var cb = new ContainerBuilder();
             Assert.Throws<ArgumentNullException>(() => cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
                 .AssignableTo(null));
+
+            Assert.Throws<ArgumentNullException>(() => cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AssignableTo(typeof(RedoOpenGenericCommand<>), (object)null));
+
+            Assert.Throws<ArgumentNullException>(() => cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AssignableTo(null, "serviceKey"));
+
+            Assert.Throws<ArgumentNullException>(() => cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AssignableTo(null, t => t));
         }
 
         [Fact]
@@ -201,6 +211,36 @@ public void WhenMetadataMappingAppliedValuesCalculatedFromType()
             Assert.True(s.Metadata.ContainsKey("Execute"));
         }
 
+        [Fact]
+        public void WhenMetadataNotFoundThrowException()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                    .Where(t => t == typeof(OpenGenericScannedComponentWithName<>))
+                    .WithMetadataFrom<ICloseCommand>();
+
+            var ex = Assert.Throws<ArgumentException>(() => cb.Build());
+
+            Assert.Equal(
+                string.Format(CultureInfo.CurrentCulture, RegistrationExtensionsResources.MetadataAttributeNotFound, typeof(ICloseCommand), typeof(OpenGenericScannedComponentWithName<>)),
+                ex.Message);
+        }
+
+        [Fact]
+        public void WhenMultipleMetadataAttributesSameTypeThrowException()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                    .Where(t => t == typeof(OpenGenericScannedComponentWithMultipleNames<>))
+                    .WithMetadataFrom<IHaveName>();
+
+            var ex = Assert.Throws<ArgumentException>(() => cb.Build());
+
+            Assert.Equal(
+                string.Format(CultureInfo.CurrentCulture, RegistrationExtensionsResources.MultipleMetadataAttributesSameType, typeof(IHaveName), typeof(OpenGenericScannedComponentWithMultipleNames<>)),
+                ex.Message);
+        }
+
         [Fact]
         public void MetadataCanBeScannedFromAMatchingAttributeInterface()
         {
@@ -218,6 +258,16 @@ public void MetadataCanBeScannedFromAMatchingAttributeInterface()
             Assert.Equal("My Name", name);
         }
 
+        [Fact]
+        public void InNamespaceNullProvidedThrowException()
+        {
+            var cb = new ContainerBuilder();
+            var ex = Assert.Throws<ArgumentNullException>(() =>
+                cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly).InNamespace(ns: ""));
+
+            Assert.Equal("ns", ex.ParamName);
+        }
+
         [Fact]
         public void InNamespaceLimitsServicesToBeRegistered()
         {

From 3282ad8a2b7902efde24ccc5c52ff891fc70a303 Mon Sep 17 00:00:00 2001
From: raymondhuy177 <raymondhuy177@gmail.com>
Date: Wed, 10 Feb 2021 17:31:46 +0700
Subject: [PATCH 5/5] add test case

---
 test/Autofac.Test/Assertions.cs               | 22 ++++++++++++++++
 .../OpenGenericScanningRegistrationTests.cs   | 25 +++++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git a/test/Autofac.Test/Assertions.cs b/test/Autofac.Test/Assertions.cs
index 6c3d69b7a..2104c0aa5 100644
--- a/test/Autofac.Test/Assertions.cs
+++ b/test/Autofac.Test/Assertions.cs
@@ -4,7 +4,11 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Reflection;
+using Autofac.Builder;
 using Autofac.Core;
+using Autofac.Features.Indexed;
+using Autofac.Features.OpenGenerics;
 using Xunit;
 
 namespace Autofac.Test
@@ -92,6 +96,24 @@ public static void AssertComponentRegistrationOrder<TService, TFirstComponent, T
             Assert.True(foundLast);
         }
 
+        public static bool RegisteredAnyOpenGenericTypeFromScanningAssembly(this IComponentContext context)
+        {
+            return context.ComponentRegistry.Sources.Any(source =>
+            {
+                if (source is OpenGenericRegistrationSource)
+                {
+                    var activatorData = typeof(OpenGenericRegistrationSource)
+                        .GetField("_activatorData", BindingFlags.NonPublic | BindingFlags.Instance)
+                        .GetValue(source) as ReflectionActivatorData;
+                    return activatorData.ImplementationType != typeof(KeyedServiceIndex<,>);
+                }
+                else
+                {
+                    return false;
+                }
+            });
+        }
+
         private static IEnumerable<Type> LookForComponents(this IEnumerable<IComponentRegistration> registrations, IEnumerable<Type> types)
         {
             return registrations
diff --git a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
index 785b61c2b..aad90c9cc 100644
--- a/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
+++ b/test/Autofac.Test/Features/Scanning/OpenGenericScanningRegistrationTests.cs
@@ -10,6 +10,7 @@
 using Autofac.Core.Lifetime;
 using Autofac.Core.Registration;
 using Autofac.Features.Metadata;
+using Autofac.Features.OpenGenerics;
 using Autofac.Features.Scanning;
 using Autofac.Test.Scenarios.ScannedAssembly;
 using Autofac.Test.Scenarios.ScannedAssembly.MetadataAttributeScanningScenario;
@@ -119,6 +120,30 @@ public void AssignableToNullTypeProvidedThrowsException()
                 .AssignableTo(null, t => t));
         }
 
+        [Theory]
+        [InlineData(typeof(ICloseCommand))]
+        [InlineData(typeof(CloseCommand))]
+        public void AssignableToClosedTypeProvidedNoneOpenGenericSourceRegistered(Type closedType)
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AssignableTo(closedType);
+            var c = cb.Build();
+
+            Assert.False(c.RegisteredAnyOpenGenericTypeFromScanningAssembly());
+        }
+
+        [Fact]
+        public void ServiceIsNotAssignableToIsNotRegistered()
+        {
+            var cb = new ContainerBuilder();
+            cb.RegisterAssemblyOpenGenericTypes(typeof(ICommand<>).GetTypeInfo().Assembly)
+                .AssignableTo(typeof(RedoOpenGenericCommand<>));
+            var c = cb.Build();
+
+            Assert.Throws<ComponentNotRegisteredException>(() => c.Resolve<DeleteOpenGenericCommand<int>>());
+        }
+
         [Fact]
         public void AssignableToOpenGenericInterfaceTypeProvidedOpenGenericTypesRegistered()
         {