private Expression CreateCollectionInitializer(LambdaScope lambdaScope, PropertyInfo collectionProperty, Type elementType, QueryLayer layer,
                                                       LambdaScopeFactory lambdaScopeFactory)
        {
            MemberExpression propertyExpression = Expression.Property(lambdaScope.Accessor, collectionProperty);

            var builder = new QueryableBuilder(propertyExpression, elementType, typeof(Enumerable), _nameFactory, _resourceFactory, _resourceContextProvider,
                                               _entityModel, lambdaScopeFactory);

            Expression layerExpression = builder.ApplyQuery(layer);

            // Earlier versions of EF Core 3.x failed to understand `query.ToHashSet()`, so we emit `new HashSet(query)` instead.
            // Interestingly, EF Core 5 RC1 fails to understand `new HashSet(query)`, so we emit `query.ToHashSet()` instead.
            // https://github.com/dotnet/efcore/issues/22902

            if (EntityFrameworkCoreSupport.Version.Major < 5)
            {
                Type enumerableOfElementType = typeof(IEnumerable <>).MakeGenericType(elementType);
                Type typedCollection         = CollectionConverter.ToConcreteCollectionType(collectionProperty.PropertyType);

                ConstructorInfo typedCollectionConstructor = typedCollection.GetConstructor(enumerableOfElementType.AsArray());

                if (typedCollectionConstructor == null)
                {
                    throw new InvalidOperationException($"Constructor on '{typedCollection.Name}' that accepts '{enumerableOfElementType.Name}' not found.");
                }

                return(Expression.New(typedCollectionConstructor, layerExpression));
            }

            string operationName = CollectionConverter.TypeCanContainHashSet(collectionProperty.PropertyType) ? "ToHashSet" : "ToList";

            return(CopyCollectionExtensionMethodCall(layerExpression, operationName, elementType));
        }
Example #2
0
        public override Expression VisitCollectionNotEmpty(CollectionNotEmptyExpression expression, Type argument)
        {
            Expression property = Visit(expression.TargetCollection, argument);

            Type elementType = CollectionConverter.TryGetCollectionElementType(property.Type);

            if (elementType == null)
            {
                throw new InvalidOperationException("Expression must be a collection.");
            }

            Expression predicate = null;

            if (expression.Filter != null)
            {
                var hasManyThrough     = expression.TargetCollection.Fields.Last() as HasManyThroughAttribute;
                var lambdaScopeFactory = new LambdaScopeFactory(_nameFactory, hasManyThrough);
                using LambdaScope lambdaScope = lambdaScopeFactory.CreateScope(elementType);

                var builder = new WhereClauseBuilder(property, lambdaScope, typeof(Enumerable), _nameFactory);
                predicate = builder.GetPredicateLambda(expression.Filter);
            }

            return(AnyExtensionMethodCall(elementType, property, predicate));
        }
 public QueryableBuilder(Expression source, Type elementType, Type extensionType, LambdaParameterNameFactory nameFactory,
                         IResourceFactory resourceFactory, IResourceContextProvider resourceContextProvider, IModel entityModel,
                         LambdaScopeFactory lambdaScopeFactory = null)
 {
     _source                  = source ?? throw new ArgumentNullException(nameof(source));
     _elementType             = elementType ?? throw new ArgumentNullException(nameof(elementType));
     _extensionType           = extensionType ?? throw new ArgumentNullException(nameof(extensionType));
     _nameFactory             = nameFactory ?? throw new ArgumentNullException(nameof(nameFactory));
     _resourceFactory         = resourceFactory ?? throw new ArgumentNullException(nameof(resourceFactory));
     _resourceContextProvider = resourceContextProvider ?? throw new ArgumentNullException(nameof(resourceContextProvider));
     _entityModel             = entityModel ?? throw new ArgumentNullException(nameof(entityModel));
     _lambdaScopeFactory      = lambdaScopeFactory ?? new LambdaScopeFactory(_nameFactory);
 }
        private MemberAssignment CreatePropertyAssignment(PropertySelector selector, LambdaScope lambdaScope)
        {
            MemberExpression propertyAccess = Expression.Property(lambdaScope.Accessor, selector.Property);

            Expression assignmentRightHandSide = propertyAccess;

            if (selector.NextLayer != null)
            {
                HasManyThroughAttribute hasManyThrough = selector.OriginatingField as HasManyThroughAttribute;
                var lambdaScopeFactory = new LambdaScopeFactory(_nameFactory, hasManyThrough);

                assignmentRightHandSide = CreateAssignmentRightHandSideForLayer(selector.NextLayer, lambdaScope, propertyAccess,
                                                                                selector.Property, lambdaScopeFactory);
            }

            return(Expression.Bind(selector.Property, assignmentRightHandSide));
        }
        public QueryableBuilder(Expression source, Type elementType, Type extensionType, LambdaParameterNameFactory nameFactory,
                                IResourceFactory resourceFactory, IResourceContextProvider resourceContextProvider, IModel entityModel,
                                LambdaScopeFactory lambdaScopeFactory = null)
        {
            ArgumentGuard.NotNull(source, nameof(source));
            ArgumentGuard.NotNull(elementType, nameof(elementType));
            ArgumentGuard.NotNull(extensionType, nameof(extensionType));
            ArgumentGuard.NotNull(nameFactory, nameof(nameFactory));
            ArgumentGuard.NotNull(resourceFactory, nameof(resourceFactory));
            ArgumentGuard.NotNull(resourceContextProvider, nameof(resourceContextProvider));
            ArgumentGuard.NotNull(entityModel, nameof(entityModel));

            _source                  = source;
            _elementType             = elementType;
            _extensionType           = extensionType;
            _nameFactory             = nameFactory;
            _resourceFactory         = resourceFactory;
            _resourceContextProvider = resourceContextProvider;
            _entityModel             = entityModel;
            _lambdaScopeFactory      = lambdaScopeFactory ?? new LambdaScopeFactory(_nameFactory);
        }
        private Expression CreateCollectionInitializer(LambdaScope lambdaScope, PropertyInfo collectionProperty,
                                                       Type elementType, QueryLayer layer, LambdaScopeFactory lambdaScopeFactory)
        {
            MemberExpression propertyExpression = Expression.Property(lambdaScope.Accessor, collectionProperty);

            var builder = new QueryableBuilder(propertyExpression, elementType, typeof(Enumerable), _nameFactory,
                                               _resourceFactory, _resourceContextProvider, _entityModel, lambdaScopeFactory);

            Expression layerExpression = builder.ApplyQuery(layer);

            Type enumerableOfElementType = typeof(IEnumerable <>).MakeGenericType(elementType);
            Type typedCollection         = TypeHelper.ToConcreteCollectionType(collectionProperty.PropertyType);

            ConstructorInfo typedCollectionConstructor = typedCollection.GetConstructor(new[]
            {
                enumerableOfElementType
            });

            if (typedCollectionConstructor == null)
            {
                throw new Exception(
                          $"Constructor on '{typedCollection.Name}' that accepts '{enumerableOfElementType.Name}' not found.");
            }

            return(Expression.New(typedCollectionConstructor, layerExpression));
        }
        private Expression CreateAssignmentRightHandSideForLayer(QueryLayer layer, LambdaScope outerLambdaScope, MemberExpression propertyAccess,
                                                                 PropertyInfo selectorPropertyInfo, LambdaScopeFactory lambdaScopeFactory)
        {
            Type collectionElementType = TypeHelper.TryGetCollectionElementType(selectorPropertyInfo.PropertyType);
            Type bodyElementType       = collectionElementType ?? selectorPropertyInfo.PropertyType;

            if (collectionElementType != null)
            {
                return(CreateCollectionInitializer(outerLambdaScope, selectorPropertyInfo, bodyElementType, layer, lambdaScopeFactory));
            }

            if (layer.Projection == null || !layer.Projection.Any())
            {
                return(propertyAccess);
            }

            using var scope = lambdaScopeFactory.CreateScope(bodyElementType, propertyAccess);
            return(CreateLambdaBodyInitializer(layer.Projection, layer.ResourceContext, scope, true));
        }