diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxiesConventionSetCustomizer.cs b/src/EFCore.Proxies/Proxies/Internal/ProxiesConventionSetCustomizer.cs index f2be3c0e7aa..e41a4e7bb85 100644 --- a/src/EFCore.Proxies/Proxies/Internal/ProxiesConventionSetCustomizer.cs +++ b/src/EFCore.Proxies/Proxies/Internal/ProxiesConventionSetCustomizer.cs @@ -4,9 +4,9 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Conventions; using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.EntityFrameworkCore.Proxies.Internal @@ -32,6 +32,7 @@ public class ProxiesConventionSetCustomizer : IConventionSetCustomizer private readonly IConstructorBindingFactory _constructorBindingFactory; private readonly IProxyFactory _proxyFactory; private readonly IDiagnosticsLogger _logger; + private readonly LazyLoaderParameterBindingFactoryDependencies _lazyLoaderParameterBindingFactoryDependencies; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -43,12 +44,14 @@ public ProxiesConventionSetCustomizer( [NotNull] IDbContextOptions options, [NotNull] IConstructorBindingFactory constructorBindingFactory, [NotNull] IProxyFactory proxyFactory, - [NotNull] IDiagnosticsLogger logger) + [NotNull] IDiagnosticsLogger logger, + [NotNull] LazyLoaderParameterBindingFactoryDependencies lazyLoaderParameterBindingFactoryDependencies) { _options = options; _constructorBindingFactory = constructorBindingFactory; _proxyFactory = proxyFactory; _logger = logger; + _lazyLoaderParameterBindingFactoryDependencies = lazyLoaderParameterBindingFactoryDependencies; } /// @@ -61,6 +64,7 @@ public virtual ConventionSet ModifyConventions(ConventionSet conventionSet) { conventionSet.ModelBuiltConventions.Add( new ProxyBindingRewriter( + _lazyLoaderParameterBindingFactoryDependencies, _proxyFactory, _constructorBindingFactory, _logger, diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxiesOptionsExtension.cs b/src/EFCore.Proxies/Proxies/Internal/ProxiesOptionsExtension.cs index bc57788584d..6c5b47117aa 100644 --- a/src/EFCore.Proxies/Proxies/Internal/ProxiesOptionsExtension.cs +++ b/src/EFCore.Proxies/Proxies/Internal/ProxiesOptionsExtension.cs @@ -9,7 +9,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.EntityFrameworkCore.Proxies.Internal diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs index 871e69ac829..bbf1edea21b 100644 --- a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs +++ b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs @@ -30,6 +30,7 @@ private static readonly PropertyInfo _lazyLoaderProperty = typeof(IProxyLazyLoader).GetProperty(nameof(IProxyLazyLoader.LazyLoader)); private readonly ConstructorBindingConvention _directBindingConvention; + private readonly LazyLoaderParameterBindingFactoryDependencies _lazyLoaderParameterBindingFactoryDependencies; private readonly IProxyFactory _proxyFactory; private readonly ProxiesOptionsExtension _options; @@ -40,12 +41,14 @@ private static readonly PropertyInfo _lazyLoaderProperty /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public ProxyBindingRewriter( + [NotNull] LazyLoaderParameterBindingFactoryDependencies lazyLoaderParameterBindingFactoryDependencies, [NotNull] IProxyFactory proxyFactory, [NotNull] IConstructorBindingFactory bindingFactory, [NotNull] IDiagnosticsLogger logger, [CanBeNull] ProxiesOptionsExtension options) { _directBindingConvention = new ConstructorBindingConvention(bindingFactory, logger); + _lazyLoaderParameterBindingFactoryDependencies = lazyLoaderParameterBindingFactoryDependencies; _proxyFactory = proxyFactory; _options = options; } @@ -83,7 +86,7 @@ public virtual InternalModelBuilder Apply(InternalModelBuilder modelBuilder) { serviceProperty = entityType.AddServiceProperty(_lazyLoaderProperty, ConfigurationSource.Convention); serviceProperty.SetParameterBinding( - (ServiceParameterBinding)new LazyLoaderParameterBindingFactory().Bind( + (ServiceParameterBinding)new LazyLoaderParameterBindingFactory(_lazyLoaderParameterBindingFactoryDependencies).Bind( entityType, typeof(ILazyLoader), nameof(IProxyLazyLoader.LazyLoader))); @@ -104,7 +107,7 @@ public virtual InternalModelBuilder Apply(InternalModelBuilder modelBuilder) new List { new EntityTypeParameterBinding(), - new DefaultServiceParameterBinding(typeof(ILazyLoader), typeof(ILazyLoader), serviceProperty), + new DependencyInjectionParameterBinding(typeof(ILazyLoader), typeof(ILazyLoader), serviceProperty), new ObjectArrayParameterBinding(binding.ParameterBindings) }, proxyType); diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs index 25c6731203b..e9a207e1f57 100644 --- a/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs +++ b/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs @@ -5,7 +5,6 @@ using Castle.DynamicProxy; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; namespace Microsoft.EntityFrameworkCore.Proxies.Internal diff --git a/src/EFCore.Proxies/ProxiesServiceCollectionExtensions.cs b/src/EFCore.Proxies/ProxiesServiceCollectionExtensions.cs index 33c647440e9..d6a9f69965d 100644 --- a/src/EFCore.Proxies/ProxiesServiceCollectionExtensions.cs +++ b/src/EFCore.Proxies/ProxiesServiceCollectionExtensions.cs @@ -4,7 +4,6 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; using Microsoft.EntityFrameworkCore.Proxies.Internal; using Microsoft.EntityFrameworkCore.Utilities; diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs index bb9582adc4d..f444b76e348 100644 --- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs +++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs @@ -315,6 +315,7 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices() .TryAddSingleton(new DiagnosticListener(DbLoggerCategory.Name)); ServiceCollectionMap.GetInfrastructure() + .AddDependencySingleton() .AddDependencySingleton() .AddDependencySingleton() .AddDependencySingleton() diff --git a/src/EFCore/Metadata/ConstructorBinding.cs b/src/EFCore/Metadata/ConstructorBinding.cs new file mode 100644 index 00000000000..2cbebde1427 --- /dev/null +++ b/src/EFCore/Metadata/ConstructorBinding.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Defines how to create an entity instance through the binding of EF model properties to, for + /// example, constructor parameters or parameters of a factory method. + /// + public abstract class ConstructorBinding + { + /// + /// Creates a new instance. + /// + /// The parameter bindings to use. + protected ConstructorBinding( + [NotNull] IReadOnlyList parameterBindings) + { + Check.NotNull(parameterBindings, nameof(parameterBindings)); + + ParameterBindings = parameterBindings; + } + + /// + /// Creates an expression tree that represents creating an entity instance from the given binding + /// information. For example, this might be a to call a constructor, + /// or a to call a factory method. + /// + /// Information needed to create the expression. + /// The expression tree. + public abstract Expression CreateConstructorExpression(ParameterBindingInfo bindingInfo); + + /// + /// The collection of instances used. + /// + public virtual IReadOnlyList ParameterBindings { get; } + + /// + /// The type that will be created from the expression tree created for this binding. + /// + public abstract Type RuntimeType { get; } + } +} diff --git a/src/EFCore/Metadata/ContextParameterBinding.cs b/src/EFCore/Metadata/ContextParameterBinding.cs new file mode 100644 index 00000000000..b38c7038a09 --- /dev/null +++ b/src/EFCore/Metadata/ContextParameterBinding.cs @@ -0,0 +1,54 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Describes the binding of a , which may or may not also have and associated + /// , to a parameter in a constructor, factory method, or similar. + /// + public class ContextParameterBinding : ServiceParameterBinding + { + /// + /// Creates a new instance for the given service type. + /// + /// The CLR type. + /// The associated , or null. + public ContextParameterBinding( + [NotNull] Type contextType, + [CanBeNull] IPropertyBase serviceProperty = null) + : base(contextType, contextType, serviceProperty) + { + } + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The expression representing the materialization context. + /// The expression representing the constant. + /// The expression tree. + public override Expression BindToParameter( + Expression materializationExpression, + Expression entityTypeExpression) + { + Check.NotNull(materializationExpression, nameof(materializationExpression)); + Check.NotNull(entityTypeExpression, nameof(entityTypeExpression)); + + var propertyExpression + = Expression.Property( + materializationExpression, + MaterializationContext.ContextProperty); + + return ServiceType != typeof(DbContext) + ? (Expression)Expression.TypeAs(propertyExpression, ServiceType) + : propertyExpression; + } + } +} diff --git a/src/EFCore/Metadata/CoreAnnotationNames.cs b/src/EFCore/Metadata/CoreAnnotationNames.cs index 48a1406a757..933013cb7b1 100644 --- a/src/EFCore/Metadata/CoreAnnotationNames.cs +++ b/src/EFCore/Metadata/CoreAnnotationNames.cs @@ -51,7 +51,7 @@ public static class CoreAnnotationNames public const string OwnedTypes = "OwnedTypes"; /// - /// Indicates the to use for the annotated item. + /// Indicates the to use for the annotated item. /// public const string ConstructorBinding = "ConstructorBinding"; diff --git a/src/EFCore/Metadata/DependencyInjectionMethodParameterBinding.cs b/src/EFCore/Metadata/DependencyInjectionMethodParameterBinding.cs new file mode 100644 index 00000000000..0aeb45e563d --- /dev/null +++ b/src/EFCore/Metadata/DependencyInjectionMethodParameterBinding.cs @@ -0,0 +1,91 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Describes the binding from a method on an EF internal dependency injection service, which may or may not + /// also have and associated , to a parameter in a constructor, + /// factory method, or similar. + /// + public class DependencyInjectionMethodParameterBinding : DependencyInjectionParameterBinding + { + /// + /// Creates a new instance for the given method + /// of the given service type. + /// + /// The parameter CLR type. + /// The service CLR types, as resolved from dependency injection + /// The method of the service to bind to. + /// The associated , or null. + public DependencyInjectionMethodParameterBinding( + [NotNull] Type parameterType, + [NotNull] Type serviceType, + [NotNull] MethodInfo method, + [CanBeNull] IPropertyBase serviceProperty = null) + : base(parameterType, serviceType, serviceProperty) + { + Check.NotNull(method, nameof(method)); + + Method = method; + } + + /// + /// The method being bound to, as defined on the dependency injection service interface. + /// + public virtual MethodInfo Method { get; } + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The expression representing the materialization context. + /// The expression representing the constant. + /// The expression tree. + public override Expression BindToParameter( + Expression materializationExpression, + Expression entityTypeExpression) + { + Check.NotNull(materializationExpression, nameof(materializationExpression)); + Check.NotNull(entityTypeExpression, nameof(entityTypeExpression)); + + var parameters = Method.GetParameters().Select( + (p, i) => Expression.Parameter(p.ParameterType, "param" + i)).ToArray(); + + var serviceVariable = Expression.Variable(ServiceType, "service"); + var delegateVariable = Expression.Variable(ParameterType, "delegate"); + + return Expression.Block( + new[] + { + serviceVariable, delegateVariable + }, + new List + { + Expression.Assign( + serviceVariable, + base.BindToParameter(materializationExpression, entityTypeExpression)), + Expression.Assign( + delegateVariable, + Expression.Condition( + Expression.ReferenceEqual(serviceVariable, Expression.Constant(null)), + Expression.Constant(null, ParameterType), + Expression.Lambda( + Expression.Call( + serviceVariable, + Method, + parameters), + parameters))), + delegateVariable + }); + } + } +} diff --git a/src/EFCore/Metadata/DependencyInjectionParameterBinding.cs b/src/EFCore/Metadata/DependencyInjectionParameterBinding.cs new file mode 100644 index 00000000000..264bed84e16 --- /dev/null +++ b/src/EFCore/Metadata/DependencyInjectionParameterBinding.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Describes the binding from an EF internal dependency injection service, which may or may not + /// also have and associated , to a parameter in a constructor, + /// factory method, or similar. + /// + public class DependencyInjectionParameterBinding : ServiceParameterBinding + { + private static readonly MethodInfo _getServiceMethod + = typeof(InternalAccessorExtensions).GetMethod(nameof(InternalAccessorExtensions.GetService)); + + /// + /// Creates a new instance for the given service type. + /// + /// The parameter CLR type. + /// The service CLR types, as resolved from dependency injection + /// The associated , or null. + public DependencyInjectionParameterBinding( + [NotNull] Type parameterType, + [NotNull] Type serviceType, + [CanBeNull] IPropertyBase serviceProperty = null) + : base(parameterType, serviceType, serviceProperty) + { + } + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The expression representing the materialization context. + /// The expression representing the constant. + /// The expression tree. + public override Expression BindToParameter( + Expression materializationExpression, + Expression entityTypeExpression) + { + Check.NotNull(materializationExpression, nameof(materializationExpression)); + Check.NotNull(entityTypeExpression, nameof(entityTypeExpression)); + + return Expression.Call( + _getServiceMethod.MakeGenericMethod(ServiceType), + Expression.Convert( + Expression.Property( + materializationExpression, + MaterializationContext.ContextProperty), + typeof(IInfrastructure))); + } + } +} diff --git a/src/EFCore/Metadata/DirectConstructorBinding.cs b/src/EFCore/Metadata/DirectConstructorBinding.cs new file mode 100644 index 00000000000..058076e1af2 --- /dev/null +++ b/src/EFCore/Metadata/DirectConstructorBinding.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Defines the binding of parameters to a CLR for an entity type. + /// + public class DirectConstructorBinding : ConstructorBinding + { + /// + /// Creates a new instance. + /// + /// The constructor to use. + /// The parameters to bind. + public DirectConstructorBinding( + [NotNull] ConstructorInfo constructor, + [NotNull] IReadOnlyList parameterBindings) + : base(parameterBindings) + { + Check.NotNull(constructor, nameof(constructor)); + + Constructor = constructor; + } + + /// + /// The bound . + /// + public virtual ConstructorInfo Constructor { get; } + + /// + /// Creates a that represents creating an entity instance using the given + /// constructor. + /// + /// Information needed to create the expression. + /// The expression tree. + public override Expression CreateConstructorExpression(ParameterBindingInfo bindingInfo) + => Expression.New( + Constructor, + ParameterBindings.Select(b => b.BindToParameter(bindingInfo))); + + /// + /// The type that will be created from the expression tree created for this binding. + /// + public override Type RuntimeType => Constructor.DeclaringType; + } +} diff --git a/src/EFCore/Metadata/EntityTypeParameterBinding.cs b/src/EFCore/Metadata/EntityTypeParameterBinding.cs new file mode 100644 index 00000000000..6f24da64b05 --- /dev/null +++ b/src/EFCore/Metadata/EntityTypeParameterBinding.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Describes the binding of a , which may or may not also have and associated + /// , to a parameter in a constructor, factory method, or similar. + /// + public class EntityTypeParameterBinding : ServiceParameterBinding + { + /// + /// Creates a new instance for the given service type. + /// + /// The associated , or null. + public EntityTypeParameterBinding([CanBeNull] IPropertyBase serviceProperty = null) + : base(typeof(IEntityType), typeof(IEntityType), serviceProperty) + { + } + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The expression representing the materialization context. + /// The expression representing the constant. + /// The expression tree. + public override Expression BindToParameter( + Expression materializationExpression, + Expression entityTypeExpression) + => Check.NotNull(entityTypeExpression, nameof(entityTypeExpression)); + } +} diff --git a/src/EFCore/Metadata/FactoryMethodConstructorBinding.cs b/src/EFCore/Metadata/FactoryMethodConstructorBinding.cs new file mode 100644 index 00000000000..af9c0bd9e76 --- /dev/null +++ b/src/EFCore/Metadata/FactoryMethodConstructorBinding.cs @@ -0,0 +1,92 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Defines the binding of parameters to a factory method. + /// + public class FactoryMethodConstructorBinding : ConstructorBinding + { + private readonly object _factoryInstance; + private readonly MethodInfo _factoryMethod; + + /// + /// Creates a new instance for a static factory method. + /// + /// The factory method to bind to. + /// The parameters to use. + /// The CLR type of the instance created by the factory method. + public FactoryMethodConstructorBinding( + [NotNull] MethodInfo factoryMethod, + [NotNull] IReadOnlyList parameterBindings, + [NotNull] Type runtimeType) + : base(parameterBindings) + { + Check.NotNull(factoryMethod, nameof(factoryMethod)); + Check.NotNull(runtimeType, nameof(runtimeType)); + + _factoryMethod = factoryMethod; + RuntimeType = runtimeType; + } + + /// + /// Creates a new instance for a static factory method. + /// + /// The object on which the factory method should be called. + /// The factory method to bind to. + /// The parameters to use. + /// The CLR type of the instance created by the factory method. + public FactoryMethodConstructorBinding( + [NotNull] object factoryInstance, + [NotNull] MethodInfo factoryMethod, + [NotNull] IReadOnlyList parameterBindings, + [NotNull] Type runtimeType) + : this(factoryMethod, parameterBindings, runtimeType) + { + Check.NotNull(factoryInstance, nameof(factoryInstance)); + + _factoryInstance = factoryInstance; + } + + /// + /// Creates a using the given method. + /// + /// Information needed to create the expression. + /// The expression tree. + public override Expression CreateConstructorExpression(ParameterBindingInfo bindingInfo) + { + var arguments = ParameterBindings.Select(b => b.BindToParameter(bindingInfo)); + + Expression expression + = _factoryInstance == null + ? Expression.Call( + _factoryMethod, + arguments) + : Expression.Call( + Expression.Constant(_factoryInstance), + _factoryMethod, + arguments); + + if (_factoryMethod.ReturnType != RuntimeType) + { + expression = Expression.Convert(expression, RuntimeType); + } + + return expression; + } + + /// + /// The type that will be created from the expression tree created for this binding. + /// + public override Type RuntimeType { get; } + } +} diff --git a/src/EFCore/Metadata/Internal/IConstructorBindingFactory.cs b/src/EFCore/Metadata/IConstructorBindingFactory.cs similarity index 53% rename from src/EFCore/Metadata/Internal/IConstructorBindingFactory.cs rename to src/EFCore/Metadata/IConstructorBindingFactory.cs index 668ca73b972..31c9e04ef06 100644 --- a/src/EFCore/Metadata/Internal/IConstructorBindingFactory.cs +++ b/src/EFCore/Metadata/IConstructorBindingFactory.cs @@ -6,14 +6,12 @@ using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.EntityFrameworkCore.Metadata.Internal +namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// A factory for finding and creating instances for + /// a given CLR constructor. /// /// /// The service lifetime is . This means a single instance @@ -24,11 +22,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal public interface IConstructorBindingFactory { /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// Attempts to create a for the given and + /// /// + /// The entity type. + /// The constructor to use. + /// The binding, or null if none could be created. + /// The parameters that could not be bound. + /// True if a binding was created; false otherwise. bool TryBindConstructor( [NotNull] IMutableEntityType entityType, [NotNull] ConstructorInfo constructor, diff --git a/src/EFCore/Metadata/IConventionServiceProperty.cs b/src/EFCore/Metadata/IConventionServiceProperty.cs index b543fbb46f6..f86475ba778 100644 --- a/src/EFCore/Metadata/IConventionServiceProperty.cs +++ b/src/EFCore/Metadata/IConventionServiceProperty.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace Microsoft.EntityFrameworkCore.Metadata { @@ -32,5 +33,18 @@ public interface IConventionServiceProperty : IServiceProperty, IConventionPrope /// /// The configuration source. ConfigurationSource GetConfigurationSource(); + + /// + /// Sets the for this property. + /// + /// The parameter binding. + /// Indicates whether the configuration was specified using a data annotation. + void SetParameterBinding(ServiceParameterBinding parameterBinding, bool fromDataAnnotation = false); + + /// + /// Returns the configuration source for . + /// + /// The configuration source for . + ConfigurationSource? GetParameterBindingConfigurationSource(); } } diff --git a/src/EFCore/Metadata/IMutableServiceProperty.cs b/src/EFCore/Metadata/IMutableServiceProperty.cs index 6e9d2598a16..bb27fdd4a40 100644 --- a/src/EFCore/Metadata/IMutableServiceProperty.cs +++ b/src/EFCore/Metadata/IMutableServiceProperty.cs @@ -1,6 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + namespace Microsoft.EntityFrameworkCore.Metadata { /// @@ -19,5 +22,10 @@ public interface IMutableServiceProperty : IServiceProperty, IMutablePropertyBas /// Gets the type that this property belongs to. /// new IMutableEntityType DeclaringEntityType { get; } + + /// + /// The for this property. + /// + new ServiceParameterBinding ParameterBinding { get; [param: CanBeNull] set; } } } diff --git a/src/EFCore/Metadata/IParameterBindingFactories.cs b/src/EFCore/Metadata/IParameterBindingFactories.cs new file mode 100644 index 00000000000..27604d852c0 --- /dev/null +++ b/src/EFCore/Metadata/IParameterBindingFactories.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// + /// Allows a to be found from those registered in the + /// internal service provider. + /// + /// + /// The service lifetime is . This means a single instance + /// is used by many instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public interface IParameterBindingFactories + { + /// + /// Attempts to find a that can bind to a parameter with the + /// given type and name. + /// + /// The parameter type. + /// The parameter name. + /// The found factory, or null if none could be found. + IParameterBindingFactory FindFactory([NotNull] Type type, [NotNull] string name); + } +} diff --git a/src/EFCore/Metadata/IParameterBindingFactory.cs b/src/EFCore/Metadata/IParameterBindingFactory.cs new file mode 100644 index 00000000000..b6fc71567e1 --- /dev/null +++ b/src/EFCore/Metadata/IParameterBindingFactory.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// + /// Factory for finding and creating instances. + /// + /// + /// The service lifetime is and multiple registrations + /// are allowed. This means a single instance of each service is used by many + /// instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public interface IParameterBindingFactory + { + /// + /// Checks whether or not this factory can bind a parameter with the given type and name. + /// + /// The parameter type. + /// The parameter name. + /// True if this parameter can be bound; false otherwise. + bool CanBind( + [NotNull] Type parameterType, + [NotNull] string parameterName); + + /// + /// Creates a for the given type and name on the given entity type. + /// + /// The entity type. + /// The parameter type. + /// The parameter name. + /// The binding. + ParameterBinding Bind( + [NotNull] IMutableEntityType entityType, + [NotNull] Type parameterType, + [NotNull] string parameterName); + } +} diff --git a/src/EFCore/Metadata/Internal/IPropertyParameterBindingFactory.cs b/src/EFCore/Metadata/IPropertyParameterBindingFactory.cs similarity index 50% rename from src/EFCore/Metadata/Internal/IPropertyParameterBindingFactory.cs rename to src/EFCore/Metadata/IPropertyParameterBindingFactory.cs index 067f4958b50..53f4d7dd248 100644 --- a/src/EFCore/Metadata/Internal/IPropertyParameterBindingFactory.cs +++ b/src/EFCore/Metadata/IPropertyParameterBindingFactory.cs @@ -5,14 +5,12 @@ using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.EntityFrameworkCore.Metadata.Internal +namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// Finds a specifically for some form of property + /// (that is, some ) of the model. /// /// /// The service lifetime is . This means a single instance @@ -23,11 +21,12 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal public interface IPropertyParameterBindingFactory { /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// Finds a specifically for an in the model. /// + /// The entity type on which the is defined. + /// The parameter name. + /// The parameter type. + /// The parameter binding, or null if none was found. ParameterBinding TryBindParameter( [NotNull] IMutableEntityType entityType, [NotNull] Type parameterType, diff --git a/src/EFCore/Metadata/IServiceProperty.cs b/src/EFCore/Metadata/IServiceProperty.cs index eb46d8c3635..fb3df8751bd 100644 --- a/src/EFCore/Metadata/IServiceProperty.cs +++ b/src/EFCore/Metadata/IServiceProperty.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.EntityFrameworkCore.Metadata.Internal; + namespace Microsoft.EntityFrameworkCore.Metadata { /// @@ -13,5 +15,10 @@ public interface IServiceProperty : IPropertyBase /// Gets the entity type that this property belongs to. /// IEntityType DeclaringEntityType { get; } - } + + /// + /// The for this property. + /// + ServiceParameterBinding ParameterBinding { get; } } } + diff --git a/src/EFCore/Metadata/Internal/ConstructorBinding.cs b/src/EFCore/Metadata/Internal/ConstructorBinding.cs deleted file mode 100644 index bd5cc6659a2..00000000000 --- a/src/EFCore/Metadata/Internal/ConstructorBinding.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public abstract class ConstructorBinding - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - protected ConstructorBinding( - [NotNull] IReadOnlyList parameterBindings) - { - ParameterBindings = parameterBindings; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public abstract Expression CreateConstructorExpression(ParameterBindingInfo bindingInfo); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual IReadOnlyList ParameterBindings { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public abstract Type RuntimeType { get; } - } -} diff --git a/src/EFCore/Metadata/Internal/ContextParameterBinding.cs b/src/EFCore/Metadata/Internal/ContextParameterBinding.cs deleted file mode 100644 index fa2d0cd3cb6..00000000000 --- a/src/EFCore/Metadata/Internal/ContextParameterBinding.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq.Expressions; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Storage; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public class ContextParameterBinding : ServiceParameterBinding - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public ContextParameterBinding( - [NotNull] Type contextType, - [CanBeNull] IPropertyBase consumedProperty = null) - : base(contextType, contextType, consumedProperty) - { - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression BindToParameter( - Expression materializationExpression, - Expression entityTypeExpression) - { - var propertyExpression - = Expression.Property( - materializationExpression, - MaterializationContext.ContextProperty); - - return ServiceType != typeof(DbContext) - ? (Expression)Expression.TypeAs(propertyExpression, ServiceType) - : propertyExpression; - } - } -} diff --git a/src/EFCore/Metadata/Internal/DefaultServiceParameterBinding.cs b/src/EFCore/Metadata/Internal/DefaultServiceParameterBinding.cs deleted file mode 100644 index 466c34671d9..00000000000 --- a/src/EFCore/Metadata/Internal/DefaultServiceParameterBinding.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq.Expressions; -using System.Reflection; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Internal; -using Microsoft.EntityFrameworkCore.Storage; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public class DefaultServiceParameterBinding : ServiceParameterBinding - { - private static readonly MethodInfo _getServiceMethod - = typeof(InternalAccessorExtensions).GetMethod(nameof(InternalAccessorExtensions.GetService)); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public DefaultServiceParameterBinding( - [NotNull] Type parameterType, - [NotNull] Type serviceType, - [CanBeNull] IPropertyBase consumedProperty = null) - : base(parameterType, serviceType, consumedProperty) - { - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression BindToParameter( - Expression materializationExpression, - Expression entityTypeExpression) - => Expression.Call( - _getServiceMethod.MakeGenericMethod(ServiceType), - Expression.Convert( - Expression.Property( - materializationExpression, - MaterializationContext.ContextProperty), - typeof(IInfrastructure))); - } -} diff --git a/src/EFCore/Metadata/Internal/DirectConstructorBinding.cs b/src/EFCore/Metadata/Internal/DirectConstructorBinding.cs deleted file mode 100644 index a13808d9066..00000000000 --- a/src/EFCore/Metadata/Internal/DirectConstructorBinding.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public class DirectConstructorBinding : ConstructorBinding - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public DirectConstructorBinding( - [NotNull] ConstructorInfo constructor, - [NotNull] IReadOnlyList parameterBindings) - : base(parameterBindings) - { - Constructor = constructor; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual ConstructorInfo Constructor { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression CreateConstructorExpression(ParameterBindingInfo bindingInfo) - => Expression.New( - Constructor, - ParameterBindings.Select(b => b.BindToParameter(bindingInfo))); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Type RuntimeType => Constructor.DeclaringType; - } -} diff --git a/src/EFCore/Metadata/Internal/EntityTypeParameterBinding.cs b/src/EFCore/Metadata/Internal/EntityTypeParameterBinding.cs deleted file mode 100644 index d4ace66c533..00000000000 --- a/src/EFCore/Metadata/Internal/EntityTypeParameterBinding.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq.Expressions; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public class EntityTypeParameterBinding : ServiceParameterBinding - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public EntityTypeParameterBinding([CanBeNull] IPropertyBase consumedProperty = null) - : base(typeof(IEntityType), typeof(IEntityType), consumedProperty) - { - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression BindToParameter( - Expression materializationExpression, - Expression entityTypeExpression) - => entityTypeExpression; - } -} diff --git a/src/EFCore/Metadata/Internal/FactoryMethodConstructorBinding.cs b/src/EFCore/Metadata/Internal/FactoryMethodConstructorBinding.cs deleted file mode 100644 index ed4f18bd59f..00000000000 --- a/src/EFCore/Metadata/Internal/FactoryMethodConstructorBinding.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public class FactoryMethodConstructorBinding : ConstructorBinding - { - private readonly object _factoryInstance; - private readonly MethodInfo _factoryMethod; - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public FactoryMethodConstructorBinding( - [NotNull] MethodInfo factoryMethod, - [NotNull] IReadOnlyList parameterBindings, - [NotNull] Type runtimeType) - : base(parameterBindings) - { - _factoryMethod = factoryMethod; - RuntimeType = runtimeType; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public FactoryMethodConstructorBinding( - [NotNull] object factoryInstance, - [NotNull] MethodInfo factoryMethod, - [NotNull] IReadOnlyList parameterBindings, - [NotNull] Type runtimeType) - : this(factoryMethod, parameterBindings, runtimeType) - { - _factoryInstance = factoryInstance; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression CreateConstructorExpression(ParameterBindingInfo bindingInfo) - { - var arguments = ParameterBindings.Select(b => b.BindToParameter(bindingInfo)); - - Expression expression - = _factoryInstance == null - ? Expression.Call( - _factoryMethod, - arguments) - : Expression.Call( - Expression.Constant(_factoryInstance), - _factoryMethod, - arguments); - - if (_factoryMethod.ReturnType != RuntimeType) - { - expression = Expression.Convert(expression, RuntimeType); - } - - return expression; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Type RuntimeType { get; } - } -} diff --git a/src/EFCore/Metadata/Internal/IParameterBindingFactories.cs b/src/EFCore/Metadata/Internal/IParameterBindingFactories.cs deleted file mode 100644 index 4c40daaec9d..00000000000 --- a/src/EFCore/Metadata/Internal/IParameterBindingFactories.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - /// - /// The service lifetime is . This means a single instance - /// is used by many instances. The implementation must be thread-safe. - /// This service cannot depend on services registered as . - /// - /// - public interface IParameterBindingFactories - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - IParameterBindingFactory FindFactory([NotNull] Type type, [NotNull] string name); - } -} diff --git a/src/EFCore/Metadata/Internal/IParameterBindingFactory.cs b/src/EFCore/Metadata/Internal/IParameterBindingFactory.cs deleted file mode 100644 index c4f590edf9f..00000000000 --- a/src/EFCore/Metadata/Internal/IParameterBindingFactory.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - /// - /// The service lifetime is and multiple registrations - /// are allowed. This means a single instance of each service is used by many - /// instances. The implementation must be thread-safe. - /// This service cannot depend on services registered as . - /// - /// - public interface IParameterBindingFactory - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - bool CanBind( - [NotNull] Type parameterType, - [NotNull] string parameterName); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - ParameterBinding Bind( - [NotNull] IMutableEntityType entityType, - [NotNull] Type parameterType, - [NotNull] string parameterName); - } -} diff --git a/src/EFCore/Metadata/Internal/ObjectArrayParameterBinding.cs b/src/EFCore/Metadata/Internal/ObjectArrayParameterBinding.cs deleted file mode 100644 index d46abc14134..00000000000 --- a/src/EFCore/Metadata/Internal/ObjectArrayParameterBinding.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public class ObjectArrayParameterBinding : ParameterBinding - { - private readonly IReadOnlyList _bindings; - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public ObjectArrayParameterBinding([NotNull] IReadOnlyList bindings) - : base(typeof(object[]), bindings.SelectMany(b => b.ConsumedProperties).ToArray()) - { - _bindings = bindings; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression BindToParameter(ParameterBindingInfo bindingInfo) - => Expression.NewArrayInit( - typeof(object), - _bindings.Select( - b => - { - var expression = b.BindToParameter(bindingInfo); - - if (expression.Type.GetTypeInfo().IsValueType) - { - expression = Expression.Convert(expression, typeof(object)); - } - - return expression; - })); - } -} diff --git a/src/EFCore/Metadata/Internal/ParameterBinding.cs b/src/EFCore/Metadata/Internal/ParameterBinding.cs deleted file mode 100644 index cef452164a6..00000000000 --- a/src/EFCore/Metadata/Internal/ParameterBinding.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public abstract class ParameterBinding - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - protected ParameterBinding( - [NotNull] Type parameterType, - [NotNull] params IPropertyBase[] consumedProperties) - { - ParameterType = parameterType; - ConsumedProperties = consumedProperties; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual Type ParameterType { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual IReadOnlyList ConsumedProperties { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public abstract Expression BindToParameter(ParameterBindingInfo bindingInfo); - } -} diff --git a/src/EFCore/Metadata/Internal/ParameterBindingInfo.cs b/src/EFCore/Metadata/Internal/ParameterBindingInfo.cs deleted file mode 100644 index de7dc1a69ea..00000000000 --- a/src/EFCore/Metadata/Internal/ParameterBindingInfo.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq.Expressions; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public readonly struct ParameterBindingInfo - { - private readonly int[] _indexMap; - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public ParameterBindingInfo( - [NotNull] IEntityType entityType, - [NotNull] Expression materializationContextExpression, - [CanBeNull] int[] indexMap) - { - _indexMap = indexMap; - EntityType = entityType; - MaterializationContextExpression = materializationContextExpression; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public IEntityType EntityType { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public Expression MaterializationContextExpression { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public int GetValueBufferIndex([NotNull] IPropertyBase property) - => _indexMap?[property.GetIndex()] ?? property.GetIndex(); - } -} diff --git a/src/EFCore/Metadata/Internal/PropertyParameterBinding.cs b/src/EFCore/Metadata/Internal/PropertyParameterBinding.cs deleted file mode 100644 index b70e3ced8a5..00000000000 --- a/src/EFCore/Metadata/Internal/PropertyParameterBinding.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq.Expressions; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Storage; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public class PropertyParameterBinding : ParameterBinding - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public PropertyParameterBinding([NotNull] IProperty consumedProperty) - : base(consumedProperty.ClrType, consumedProperty) - { - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression BindToParameter(ParameterBindingInfo bindingInfo) - { - var property = ConsumedProperties[0]; - - return Expression.Call( - EntityMaterializerSource.TryReadValueMethod.MakeGenericMethod(property.ClrType), - Expression.Call(bindingInfo.MaterializationContextExpression, MaterializationContext.GetValueBufferMethod), - Expression.Constant(bindingInfo.GetValueBufferIndex(property)), - Expression.Constant(property, typeof(IPropertyBase))); - } - } -} diff --git a/src/EFCore/Metadata/Internal/ServiceMethodParameterBinding.cs b/src/EFCore/Metadata/Internal/ServiceMethodParameterBinding.cs deleted file mode 100644 index 6ac41343b30..00000000000 --- a/src/EFCore/Metadata/Internal/ServiceMethodParameterBinding.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public class ServiceMethodParameterBinding : DefaultServiceParameterBinding - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public ServiceMethodParameterBinding( - [NotNull] Type parameterType, - [NotNull] Type serviceType, - [NotNull] MethodInfo method, - [CanBeNull] IPropertyBase consumedProperty = null) - : base(parameterType, serviceType, consumedProperty) - { - Method = method; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual MethodInfo Method { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression BindToParameter( - Expression materializationExpression, - Expression entityTypeExpression) - { - var parameters = Method.GetParameters().Select( - (p, i) => Expression.Parameter(p.ParameterType, "param" + i)).ToArray(); - - var serviceVariable = Expression.Variable(ServiceType, "service"); - var delegateVariable = Expression.Variable(ParameterType, "delegate"); - - return Expression.Block( - new[] { serviceVariable, delegateVariable }, - new List - { - Expression.Assign( - serviceVariable, - base.BindToParameter(materializationExpression, entityTypeExpression)), - Expression.Assign( - delegateVariable, - Expression.Condition( - Expression.ReferenceEqual(serviceVariable, Expression.Constant(null)), - Expression.Constant(null, ParameterType), - Expression.Lambda( - Expression.Call( - serviceVariable, - Method, - parameters), - parameters))), - delegateVariable - }); - } - } -} diff --git a/src/EFCore/Metadata/Internal/ServiceParameterBinding.cs b/src/EFCore/Metadata/Internal/ServiceParameterBinding.cs deleted file mode 100644 index 48e5d5a380a..00000000000 --- a/src/EFCore/Metadata/Internal/ServiceParameterBinding.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq.Expressions; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Internal; -using Microsoft.EntityFrameworkCore.Storage; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public abstract class ServiceParameterBinding : ParameterBinding - { - private Func _serviceDelegate; - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - protected ServiceParameterBinding( - [NotNull] Type parameterType, - [NotNull] Type serviceType, - [CanBeNull] IPropertyBase consumedProperty = null) - : base(parameterType, consumedProperty != null ? new[] { consumedProperty } : Array.Empty()) - { - ServiceType = serviceType; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual Type ServiceType { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public override Expression BindToParameter(ParameterBindingInfo bindingInfo) - => BindToParameter( - bindingInfo.MaterializationContextExpression, - Expression.Constant(bindingInfo.EntityType)); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public abstract Expression BindToParameter( - [NotNull] Expression materializationExpression, - [NotNull] Expression entityTypeExpression); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual Func ServiceDelegate - => NonCapturingLazyInitializer.EnsureInitialized( - ref _serviceDelegate, this, b => - { - var materializationContextParam = Expression.Parameter(typeof(MaterializationContext)); - var entityTypeParam = Expression.Parameter(typeof(IEntityType)); - var entityParam = Expression.Parameter(typeof(object)); - - return Expression.Lambda>( - b.BindToParameter(materializationContextParam, entityTypeParam), - materializationContextParam, - entityTypeParam, - entityParam).Compile(); - }); - } -} diff --git a/src/EFCore/Metadata/Internal/ServiceParameterBindingFactory.cs b/src/EFCore/Metadata/Internal/ServiceParameterBindingFactory.cs deleted file mode 100644 index 045418e3e67..00000000000 --- a/src/EFCore/Metadata/Internal/ServiceParameterBindingFactory.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - /// - /// The service lifetime is and multiple registrations - /// are allowed. This means a single instance of each service is used by many - /// instances. The implementation must be thread-safe. - /// This service cannot depend on services registered as . - /// - /// - public class ServiceParameterBindingFactory : IParameterBindingFactory - { - private readonly Type _serviceType; - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public ServiceParameterBindingFactory([NotNull] Type serviceType) - { - _serviceType = serviceType; - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual bool CanBind( - Type parameterType, - string parameterName) - => parameterType == _serviceType; - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual ParameterBinding Bind( - IMutableEntityType entityType, - Type parameterType, - string parameterName) - => new DefaultServiceParameterBinding( - _serviceType, - _serviceType, - entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == _serviceType)); - } -} diff --git a/src/EFCore/Metadata/Internal/ServiceProperty.cs b/src/EFCore/Metadata/Internal/ServiceProperty.cs index 234203387c3..7c1a2d4baf7 100644 --- a/src/EFCore/Metadata/Internal/ServiceProperty.cs +++ b/src/EFCore/Metadata/Internal/ServiceProperty.cs @@ -121,7 +121,7 @@ public virtual void UpdateConfigurationSource(ConfigurationSource configurationS public virtual ServiceParameterBinding ParameterBinding { get => _parameterBinding; - [param: NotNull] set => SetParameterBinding(value, ConfigurationSource.Explicit); + set => SetParameterBinding(value, ConfigurationSource.Explicit); } /// @@ -142,6 +142,14 @@ public virtual void SetParameterBinding(ServiceParameterBinding parameterBinding UpdateParameterBindingConfigurationSource(configurationSource); } + /// + /// Sets the for this property. + /// + /// The parameter binding. + /// Indicates whether the configuration was specified using a data annotation. + void IConventionServiceProperty.SetParameterBinding(ServiceParameterBinding parameterBinding, bool fromDataAnnotation) + => SetParameterBinding(parameterBinding, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/EFCore/Metadata/Internal/LazyLoaderParameterBindingFactory.cs b/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs similarity index 59% rename from src/EFCore/Metadata/Internal/LazyLoaderParameterBindingFactory.cs rename to src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs index 7f486097216..69a08ee680c 100644 --- a/src/EFCore/Metadata/Internal/LazyLoaderParameterBindingFactory.cs +++ b/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs @@ -6,18 +6,18 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Extensions; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.EntityFrameworkCore.Metadata.Internal +namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// A for binding to the service. /// /// /// The service lifetime is and multiple registrations @@ -32,40 +32,49 @@ public class LazyLoaderParameterBindingFactory : ServiceParameterBindingFactory private static readonly MethodInfo _loadAsyncMethod = typeof(ILazyLoader).GetMethod(nameof(ILazyLoader.LoadAsync)); /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// Creates a new instance. /// - public LazyLoaderParameterBindingFactory() + /// The service dependencies to use. + public LazyLoaderParameterBindingFactory([NotNull] LazyLoaderParameterBindingFactoryDependencies dependencies) : base(typeof(ILazyLoader)) { + Check.NotNull(dependencies, nameof(dependencies)); } /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// Checks whether or not this factory can bind a parameter with the given type and name. /// + /// The parameter type. + /// The parameter name. + /// True if this parameter can be bound; false otherwise. public override bool CanBind( Type parameterType, string parameterName) - => IsLazyLoader(parameterType) - || IsLazyLoaderMethod(parameterType, parameterName) - || IsLazyLoaderAsyncMethod(parameterType, parameterName); + { + Check.NotNull(parameterType, nameof(parameterType)); + Check.NotEmpty(parameterName, nameof(parameterName)); + + return IsLazyLoader(parameterType) + || IsLazyLoaderMethod(parameterType, parameterName) + || IsLazyLoaderAsyncMethod(parameterType, parameterName); + } /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// Creates a for the given type and name on the given entity type. /// + /// The entity type. + /// The parameter type. + /// The parameter name. + /// The binding. public override ParameterBinding Bind( IMutableEntityType entityType, Type parameterType, string parameterName) { + Check.NotNull(entityType, nameof(entityType)); + Check.NotNull(parameterType, nameof(parameterType)); + Check.NotEmpty(parameterName, nameof(parameterName)); + var baseType = entityType; do { @@ -75,17 +84,17 @@ public override ParameterBinding Bind( while (baseType != null); return parameterType == typeof(ILazyLoader) - ? new DefaultServiceParameterBinding( + ? new DependencyInjectionParameterBinding( typeof(ILazyLoader), typeof(ILazyLoader), entityType.GetServiceProperties().FirstOrDefault(p => IsLazyLoader(p.ClrType))) : parameterType == typeof(Action) - ? new ServiceMethodParameterBinding( + ? new DependencyInjectionMethodParameterBinding( typeof(Action), typeof(ILazyLoader), _loadMethod, entityType.GetServiceProperties().FirstOrDefault(p => IsLazyLoaderMethod(p.ClrType, p.Name))) - : new ServiceMethodParameterBinding( + : new DependencyInjectionMethodParameterBinding( typeof(Func), typeof(ILazyLoader), _loadAsyncMethod, diff --git a/src/EFCore/Metadata/LazyLoaderParameterBindingFactoryDependencies.cs b/src/EFCore/Metadata/LazyLoaderParameterBindingFactoryDependencies.cs new file mode 100644 index 00000000000..60941cb861f --- /dev/null +++ b/src/EFCore/Metadata/LazyLoaderParameterBindingFactoryDependencies.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// + /// Service dependencies parameter class for + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// Do not construct instances of this class directly from either provider or application code as the + /// constructor signature may change as new dependencies are added. Instead, use this type in + /// your constructor so that an instance will be created and injected automatically by the + /// dependency injection container. To create an instance with some dependent services replaced, + /// first resolve the object from the dependency injection container, then replace selected + /// services using the 'With...' methods. Do not call the constructor at any point in this process. + /// + /// + /// The service lifetime is and multiple registrations + /// are allowed. This means a single instance of each service is used by many + /// instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public sealed class LazyLoaderParameterBindingFactoryDependencies + { + /// + /// + /// Creates the service dependencies parameter object for a . + /// + /// + /// Do not call this constructor directly from either provider or application code as it may change + /// as new dependencies are added. Instead, use this type in your constructor so that an instance + /// will be created and injected automatically by the dependency injection container. To create + /// an instance with some dependent services replaced, first resolve the object from the dependency + /// injection container, then replace selected services using the 'With...' methods. Do not call + /// the constructor at any point in this process. + /// + /// + public LazyLoaderParameterBindingFactoryDependencies() + { + } + } +} diff --git a/src/EFCore/Metadata/ObjectArrayParameterBinding.cs b/src/EFCore/Metadata/ObjectArrayParameterBinding.cs new file mode 100644 index 00000000000..9ea87c41fd5 --- /dev/null +++ b/src/EFCore/Metadata/ObjectArrayParameterBinding.cs @@ -0,0 +1,57 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Describes the binding from many EF model properties, dependency injection services, or metadata types to + /// a new array of objects suitable for passing to a general purpose factory method such as is often used for + /// creating proxies. + /// + public class ObjectArrayParameterBinding : ParameterBinding + { + private readonly IReadOnlyList _bindings; + + /// + /// Creates a new taking all the given + /// instances and combining them into one binding that will initialize an array of . + /// + /// The binding to combine. + public ObjectArrayParameterBinding([NotNull] IReadOnlyList bindings) + : base( + typeof(object[]), + Check.NotNull(bindings, nameof(bindings)).SelectMany(b => b.ConsumedProperties).ToArray()) + { + _bindings = bindings; + } + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The binding information. + /// The expression tree. + public override Expression BindToParameter(ParameterBindingInfo bindingInfo) + => Expression.NewArrayInit( + typeof(object), + _bindings.Select( + b => + { + var expression = b.BindToParameter(bindingInfo); + + if (expression.Type.GetTypeInfo().IsValueType) + { + expression = Expression.Convert(expression, typeof(object)); + } + + return expression; + })); + } +} diff --git a/src/EFCore/Metadata/ParameterBinding.cs b/src/EFCore/Metadata/ParameterBinding.cs new file mode 100644 index 00000000000..2b704fde2ac --- /dev/null +++ b/src/EFCore/Metadata/ParameterBinding.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Describes the binding from one or many EF model properties, dependency injection services, or metadata types to + /// a parameter in a constructor, factory method, or similar. + /// + public abstract class ParameterBinding + { + /// + /// Creates a new instance. + /// + /// The parameter CLR type. + /// The properties that are handled by this binding and so do not need to be set in some other way. + protected ParameterBinding( + [NotNull] Type parameterType, + [NotNull] params IPropertyBase[] consumedProperties) + { + Check.NotNull(parameterType, nameof(parameterType)); + Check.NotNull(consumedProperties, nameof(parameterType)); + + ParameterType = parameterType; + ConsumedProperties = consumedProperties; + } + + /// + /// The parameter CLR type. + /// + public virtual Type ParameterType { get; } + + /// + /// The properties that are handled by this binding and so do not need to be set in some other way. + /// + public virtual IReadOnlyList ConsumedProperties { get; } + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The binding information. + /// The expression tree. + public abstract Expression BindToParameter(ParameterBindingInfo bindingInfo); + } +} diff --git a/src/EFCore/Metadata/ParameterBindingInfo.cs b/src/EFCore/Metadata/ParameterBindingInfo.cs new file mode 100644 index 00000000000..b8ad09e0905 --- /dev/null +++ b/src/EFCore/Metadata/ParameterBindingInfo.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Carries information about a parameter binding. + /// + public readonly struct ParameterBindingInfo + { + private readonly int[] _indexMap; + + /// + /// Creates a new to define a parameter binding. + /// + /// The entity type for this binding. + /// The expression tree from which the parameter value will come. + /// An optional index map for finding the parameter value. + public ParameterBindingInfo( + [NotNull] IEntityType entityType, + [NotNull] Expression materializationContextExpression, + [CanBeNull] int[] indexMap) + { + Check.NotNull(entityType, nameof(entityType)); + Check.NotNull(materializationContextExpression, nameof(materializationContextExpression)); + + _indexMap = indexMap; + EntityType = entityType; + MaterializationContextExpression = materializationContextExpression; + } + + /// + /// The entity type for this binding. + /// + public IEntityType EntityType { get; } + + /// + /// The expression tree from which the parameter value will come. + /// + public Expression MaterializationContextExpression { get; } + + /// + /// Gets the index into the where the property value can be found. + /// + /// The property. + /// The index where its value can be found. + public int GetValueBufferIndex([NotNull] IPropertyBase property) + => _indexMap?[property.GetIndex()] ?? property.GetIndex(); + } +} diff --git a/src/EFCore/Metadata/PropertyParameterBinding.cs b/src/EFCore/Metadata/PropertyParameterBinding.cs new file mode 100644 index 00000000000..778958e1c96 --- /dev/null +++ b/src/EFCore/Metadata/PropertyParameterBinding.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Storage; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Describes the binding from an to a parameter in a constructor, factory method, + /// or similar. + /// + public class PropertyParameterBinding : ParameterBinding + { + /// + /// Creates a new instance for the given . + /// + /// The property to bind. + public PropertyParameterBinding([NotNull] IProperty property) + : base(property.ClrType, property) + { + } + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The binding information. + /// The expression tree. + public override Expression BindToParameter(ParameterBindingInfo bindingInfo) + { + var property = ConsumedProperties[0]; + + return Expression.Call( + EntityMaterializerSource.TryReadValueMethod.MakeGenericMethod(property.ClrType), + Expression.Call(bindingInfo.MaterializationContextExpression, MaterializationContext.GetValueBufferMethod), + Expression.Constant(bindingInfo.GetValueBufferIndex(property)), + Expression.Constant(property, typeof(IPropertyBase))); + } + } +} diff --git a/src/EFCore/Metadata/ServiceParameterBinding.cs b/src/EFCore/Metadata/ServiceParameterBinding.cs new file mode 100644 index 00000000000..da34a41e0a0 --- /dev/null +++ b/src/EFCore/Metadata/ServiceParameterBinding.cs @@ -0,0 +1,91 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Describes the binding from an EF dependency injection service, or metadata type, which may or + /// may not also have and associated , to a parameter in + /// a constructor, factory method, or similar. + /// + public abstract class ServiceParameterBinding : ParameterBinding + { + private Func _serviceDelegate; + + /// + /// Creates a new instance for the given service type + /// or metadata type. + /// + /// The parameter CLR type. + /// The service or metadata CLR type. + /// The associated , or null. + protected ServiceParameterBinding( + [NotNull] Type parameterType, + [NotNull] Type serviceType, + [CanBeNull] IPropertyBase serviceProperty = null) + : base( + parameterType, serviceProperty != null + ? new[] + { + serviceProperty + } + : Array.Empty()) + { + Check.NotNull(serviceType, nameof(serviceType)); + + ServiceType = serviceType; + } + + /// + /// The EF internal service CLR type. + /// + public virtual Type ServiceType { get; } + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The binding information. + /// The expression tree. + public override Expression BindToParameter(ParameterBindingInfo bindingInfo) + => BindToParameter( + bindingInfo.MaterializationContextExpression, + Expression.Constant(bindingInfo.EntityType)); + + /// + /// Creates an expression tree representing the binding of the value of a property from a + /// materialization expression to a parameter of the constructor, factory method, etc. + /// + /// The expression representing the materialization context. + /// The expression representing the constant. + /// The expression tree. + public abstract Expression BindToParameter( + [NotNull] Expression materializationExpression, + [NotNull] Expression entityTypeExpression); + + /// + /// A delegate to set a CLR service property on an entity instance. + /// + public virtual Func ServiceDelegate + => NonCapturingLazyInitializer.EnsureInitialized( + ref _serviceDelegate, this, b => + { + var materializationContextParam = Expression.Parameter(typeof(MaterializationContext)); + var entityTypeParam = Expression.Parameter(typeof(IEntityType)); + var entityParam = Expression.Parameter(typeof(object)); + + return Expression.Lambda>( + b.BindToParameter(materializationContextParam, entityTypeParam), + materializationContextParam, + entityTypeParam, + entityParam).Compile(); + }); + } +} diff --git a/src/EFCore/Metadata/ServiceParameterBindingFactory.cs b/src/EFCore/Metadata/ServiceParameterBindingFactory.cs new file mode 100644 index 00000000000..bfe5f3aa769 --- /dev/null +++ b/src/EFCore/Metadata/ServiceParameterBindingFactory.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// + /// A for binding to dependency-injected services. + /// + /// + /// The service lifetime is and multiple registrations + /// are allowed. This means a single instance of each service is used by many + /// instances. The implementation must be thread-safe. + /// This service cannot depend on services registered as . + /// + /// + public class ServiceParameterBindingFactory : IParameterBindingFactory + { + private readonly Type _serviceType; + + /// + /// Creates a new instance for the given service type. + /// + /// The service type. + public ServiceParameterBindingFactory([NotNull] Type serviceType) + { + Check.NotNull(serviceType, nameof(serviceType)); + + _serviceType = serviceType; + } + + /// + /// Checks whether or not this factory can bind a parameter with the given type and name. + /// + /// The parameter type. + /// The parameter name. + /// True if this parameter can be bound; false otherwise. + public virtual bool CanBind( + Type parameterType, + string parameterName) + => parameterType == _serviceType; + + /// + /// Creates a for the given type and name on the given entity type. + /// + /// The entity type. + /// The parameter type. + /// The parameter name. + /// The binding. + public virtual ParameterBinding Bind( + IMutableEntityType entityType, + Type parameterType, + string parameterName) + { + Check.NotNull(entityType, nameof(entityType)); + Check.NotNull(parameterType, nameof(parameterType)); + Check.NotEmpty(parameterName, nameof(parameterName)); + + return new DependencyInjectionParameterBinding( + _serviceType, + _serviceType, + entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == _serviceType)); + } + } +} diff --git a/test/EFCore.Specification.Tests/TestUtilities/TestServiceFactory.cs b/test/EFCore.Specification.Tests/TestUtilities/TestServiceFactory.cs index bfca735bc51..b0fc1f490d8 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/TestServiceFactory.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/TestServiceFactory.cs @@ -10,6 +10,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.Extensions.DependencyInjection; diff --git a/test/EFCore.Tests/Metadata/Conventions/Internal/ConstructorBindingConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/Internal/ConstructorBindingConventionTest.cs index 160a9e1b06a..109ecd25eb8 100644 --- a/test/EFCore.Tests/Metadata/Conventions/Internal/ConstructorBindingConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/Internal/ConstructorBindingConventionTest.cs @@ -549,9 +549,9 @@ public void Binds_to_ILazyLoader() Assert.Equal("loader", parameters[0].Name); - Assert.IsType(bindings[0]); + Assert.IsType(bindings[0]); Assert.Empty(bindings[0].ConsumedProperties); - Assert.Same(typeof(ILazyLoader), ((DefaultServiceParameterBinding)bindings[0]).ServiceType); + Assert.Same(typeof(ILazyLoader), ((DependencyInjectionParameterBinding)bindings[0]).ServiceType); } private class BlogWithLazyLoader : Blog @@ -576,9 +576,9 @@ public void Binds_to_delegate_parameter_called_lazyLoader() Assert.Equal("lazyLoader", parameters[0].Name); - Assert.IsType(bindings[0]); + Assert.IsType(bindings[0]); Assert.Empty(bindings[0].ConsumedProperties); - Assert.Same(typeof(ILazyLoader), ((ServiceMethodParameterBinding)bindings[0]).ServiceType); + Assert.Same(typeof(ILazyLoader), ((DependencyInjectionMethodParameterBinding)bindings[0]).ServiceType); } private class BlogWithLazyLoaderMethod : Blog