Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ApplyConfiguration() overloads to ModelBuilder that scans assemblies #13088

Closed
rmja opened this issue Aug 23, 2018 · 4 comments
Closed

Add ApplyConfiguration() overloads to ModelBuilder that scans assemblies #13088

rmja opened this issue Aug 23, 2018 · 4 comments
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Milestone

Comments

@rmja
Copy link

rmja commented Aug 23, 2018

It would be neat with an overload on ModelBuilder that scanned assemblies for IEntityTypeConfiguration and IQueryTypeConfiguration classes. The use would be:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfiguration(GetType().Assembly, someOtherAssemblyWithEntityTypes);
        }

I currently use this extension method to accomplish this, but it uses reflection to do its job. (It only supports IEntityTypeConfiguration and not IQueryTypeConfiguration):

public static class EntityTypeConfigurationModelBuilderExtensions
    {
        private static readonly ConcurrentDictionary<Type, Func<ModelBuilder, ModelBuilder>> _applyEntityTypeConfigurationDelegates = new ConcurrentDictionary<Type, Func<ModelBuilder, ModelBuilder>>();
        private static readonly ConcurrentDictionary<Assembly, Type[]> _assemblyConfiguratorTypes = new ConcurrentDictionary<Assembly, Type[]>();

        /// <summary>
        /// Apply configuration for the entity configurator of <paramref name="configuratorType"/>.
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="configuratorType">The configurator implementing <see cref="IEntityTypeConfiguration{TEntity}"/></param>
        public static void ApplyEntityTypeConfiguration(this ModelBuilder builder, Type configuratorType)
        {
            var applyConfigurationDelegate = _applyEntityTypeConfigurationDelegates.GetOrAdd(configuratorType, GetApplyEntityTypeConfigurationDelegate);

            applyConfigurationDelegate(builder);
        }

        private static Func<ModelBuilder, ModelBuilder> GetApplyEntityTypeConfigurationDelegate(Type configuratorType)
        {
            var configurator = Activator.CreateInstance(configuratorType);

            var genericConfiguratorInterfaceType = configuratorType.GetInterfaces().Single(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>));
            var entityType = genericConfiguratorInterfaceType.GetGenericArguments()[0];

            var modelBuilderInstance = Expression.Parameter(typeof(ModelBuilder), "modelBuilder");

            return Expression.Lambda<Func<ModelBuilder, ModelBuilder>>(
                Expression.Call(
                    modelBuilderInstance,
                    typeof(ModelBuilder).GetMethods().Single(IsApplyEntityTypeConfigurationMethod).MakeGenericMethod(entityType),
                    Expression.Convert(Expression.Constant(configurator), typeof(IEntityTypeConfiguration<>).MakeGenericType(entityType))),
                modelBuilderInstance).Compile();
        }

        private static bool IsApplyEntityTypeConfigurationMethod(MethodInfo methodInfo)
        {
            if (methodInfo.Name == nameof(ModelBuilder.ApplyConfiguration))
            {
                var parameters = methodInfo.GetParameters();

                return parameters.Length == 1 && parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>);
            }

            return false;
        }

        /// <summary>
        /// Apply configuration for all entity configurators defined in <paramref name="assemblies"/>.
        /// </summary>
        /// <param name="builder">The model builder</param>
        /// <param name="assemblies">The assemblies to scan for <see cref="IEntityTypeConfiguration{TEntity}"></see></param>
        public static void ApplyConfiguration(this ModelBuilder builder, params Assembly[] assemblies)
        {
            foreach (var assembly in assemblies)
            {
                var entityConfiguratorTypes = _assemblyConfiguratorTypes.GetOrAdd(assembly, GetEntityTypeConfiguratorTypes);

                foreach (var type in entityConfiguratorTypes)
                {
                    builder.ApplyEntityTypeConfiguration(type);
                }
            }
        }

        private static Type[] GetEntityTypeConfiguratorTypes(Assembly assembly) => assembly.ExportedTypes.Where(IsEntityConfigurator).ToArray();

        private static bool IsEntityConfigurator(Type type) => type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>));
    }
@ajcvickers
Copy link
Contributor

Duplicate of #13014, but bringing this back to triage since we are seeing many requests for it.

@ajcvickers
Copy link
Contributor

We discussed this again in triage and since it seems to be a very popular request we will consider adding some API to discover and register IEntityTypeConfigurations from a given assembly. This would always be opt-in, meaning that to use it an explicit call must be made, and should take the Assembly to look in as a parameter.

@ajcvickers ajcvickers added this to the Backlog milestone Aug 29, 2018
@ajcvickers ajcvickers added the help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. label Aug 29, 2018
@DSilence
Copy link
Contributor

DSilence commented Sep 4, 2018

I'm willing to take a look at this if nobody minds.

DSilence added a commit to DSilence/EntityFrameworkCore that referenced this issue Sep 12, 2018
DSilence added a commit to DSilence/EntityFrameworkCore that referenced this issue Sep 12, 2018
DSilence added a commit to DSilence/EntityFrameworkCore that referenced this issue Sep 12, 2018
DSilence added a commit to DSilence/EntityFrameworkCore that referenced this issue Sep 12, 2018
DSilence added a commit to DSilence/EntityFrameworkCore that referenced this issue Oct 5, 2018
…oads to ModelBuilder that scans assemblies.
ajcvickers pushed a commit that referenced this issue Oct 5, 2018
@ajcvickers ajcvickers modified the milestones: Backlog, 2.2.0 Oct 5, 2018
@ajcvickers ajcvickers added closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. and removed help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. labels Oct 5, 2018
@ajcvickers
Copy link
Contributor

@DSilence Thanks for contributing this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Projects
None yet
Development

No branches or pull requests

3 participants