Ejemplo n.º 1
0
        /// <summary>
        /// Sources an expression.
        /// </summary>
        /// <param name="context">
        /// The query expression context.
        /// </param>
        /// <param name="embedded">
        /// Indicates if the sourcing is occurring on an embedded node.
        /// </param>
        /// <returns>
        /// A data source expression that represents the visited node.
        /// </returns>
        public Expression Source(QueryExpressionContext context, bool embedded)
        {
            Ensure.NotNull(context, "context");

            if (context.ModelReference.EntitySet == null)
            {
                // EF provider can only source *EntitySet*.
                return null;
            }

            var dbContext = context.QueryContext.GetApiService<DbContext>();
            var dbSetProperty = dbContext.GetType().GetProperties()
                .FirstOrDefault(prop => prop.Name == context.ModelReference.EntitySet.Name);
            if (dbSetProperty == null)
            {
                // EF provider can only source EntitySet from *DbSet property*.
                return null;
            }

            if (!embedded)
            {
                // TODO GitHubIssue#37 : Add API entity manager for tracking entities
                // the underlying DbContext shouldn't track the entities
                var dbSet = dbSetProperty.GetValue(dbContext);

                ////dbSet = dbSet.GetType().GetMethod("AsNoTracking").Invoke(dbSet, null);
                return Expression.Constant(dbSet);
            }
            else
            {
                return Expression.MakeMemberAccess(
                    Expression.Constant(dbContext),
                    dbSetProperty);
            }
        }
        /// <inheritdoc/>
        public Expression Process(QueryExpressionContext context)
        {
            Ensure.NotNull(context, "context");

            if (Inner != null)
            {
                var innerFilteredExpression = Inner.Process(context);
                if (innerFilteredExpression != null && innerFilteredExpression != context.VisitedNode)
                {
                    return innerFilteredExpression;
                }
            }

            // TODO GitHubIssue#330: EF QueryExecutor will throw exception if check whether collections is null added.
            // Exception message likes "Cannot compare elements of type 'ICollection`1[[EntityType]]'.
            // Only primitive types, enumeration types and entity types are supported."
            // EF does not support complex != null neither, and it requires complex not null.
            // EF model builder set complex type not null by default, but Web Api OData does not.
            if (context.VisitedNode.NodeType == ExpressionType.NotEqual)
            {
                var binaryExp = (BinaryExpression)context.VisitedNode;
                var left = binaryExp.Left as MemberExpression;
                var right = binaryExp.Right as ConstantExpression;

                bool rightCheck = right != null && right.Value == null;

                // Check right first which is simple
                if (!rightCheck)
                {
                    return context.VisitedNode;
                }

                bool leftCheck = false;
                if (left != null)
                {
                    // If it is a collection, then replace coll != null with true
                    leftCheck = left.Type.IsGenericType
                        && left.Type.GetGenericTypeDefinition() == typeof(ICollection<>);

                    // If it is a complex, replace complex!=null with true
                    if (!leftCheck)
                    {
                        var modelRef = context.GetModelReferenceForNode(left);
                        if (modelRef != null && modelRef.Type != null)
                        {
                            leftCheck = modelRef.Type.TypeKind.Equals(EdmTypeKind.Complex);
                        }
                    }
                }

                if (leftCheck)
                {
                    return Expression.Constant(true);
                }
            }

            return context.VisitedNode;
        }
        /// <inheritdoc/>
        public Expression Filter(QueryExpressionContext context)
        {
            Ensure.NotNull(context, "context");
            if (context.ModelReference == null)
            {
                return null;
            }

            var apiDataReference = context.ModelReference as ApiDataReference;
            if (apiDataReference == null)
            {
                return null;
            }

            var entitySet = apiDataReference.Element as IEdmEntitySet;
            if (entitySet == null)
            {
                return null;
            }

            var returnType = context.VisitedNode.Type
                .FindGenericType(typeof(IQueryable<>));
            var elementType = returnType.GetGenericArguments()[0];
            var methodName = ConventionBasedChangeSetConstants.FilterMethodEntitySetFilter + entitySet.Name;
            var method = this.targetType.GetQualifiedMethod(methodName);
            if (method != null && method.IsFamily &&
                method.ReturnType == returnType)
            {
                object target = null;
                if (!method.IsStatic)
                {
                    target = context.QueryContext.ApiContext.GetProperty(
                        typeof(Api).AssemblyQualifiedName);
                    if (target == null ||
                        !this.targetType.IsAssignableFrom(target.GetType()))
                    {
                        return null;
                    }
                }

                var parameters = method.GetParameters();
                if (parameters.Length == 1 &&
                    parameters[0].ParameterType == returnType)
                {
                    var queryType = typeof(EnumerableQuery<>)
                        .MakeGenericType(elementType);
                    var query = Activator.CreateInstance(queryType, context.VisitedNode);
                    var result = method.Invoke(target, new object[] { query }) as IQueryable;
                    if (result != null && result != query)
                    {
                        return result.Expression;
                    }
                }
            }

            return null;
        }
Ejemplo n.º 4
0
        private Expression CallInner(QueryExpressionContext context)
        {
            if (this.InnerHandler != null)
            {
                return this.InnerHandler.Expand(context);
            }

            return null;
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Expands an expression.
        /// </summary>
        /// <param name="context">
        /// The query expression context.
        /// </param>
        /// <returns>
        /// An expanded expression of the same type as the visited node, or
        /// if expansion did not apply, the visited node or <c>null</c>.
        /// </returns>
        public Expression Expand(QueryExpressionContext context)
        {
            Ensure.NotNull(context, "context");

            if (context.ModelReference == null)
            {
                return CallInner(context);
            }

            var dataSourceStubReference = context.ModelReference as DataSourceStubReference;
            if (dataSourceStubReference == null)
            {
                return CallInner(context);
            }

            var entitySet = dataSourceStubReference.Element as IEdmEntitySet;
            if (entitySet == null)
            {
                return CallInner(context);
            }

            var target = context.QueryContext.GetApiService<ApiBase>();
            var entitySetProperty = target.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.Instance |
                BindingFlags.Static | BindingFlags.DeclaredOnly)
                .SingleOrDefault(p => p.Name == entitySet.Name);
            if (entitySetProperty != null)
            {
                var policies = entitySetProperty.GetCustomAttributes()
                        .OfType<IApiPolicy>();

                foreach (var policy in policies)
                {
                    policy.Activate(context.QueryContext);
                }

                context.AfterNestedVisitCallback = () =>
                {
                    foreach (var policy in policies.Reverse())
                    {
                        policy.Deactivate(context.QueryContext);
                    }
                };
            }

            // This class is used to activate and deactivate the policies
            // thus it is NOT intended to actually expand any query here.
            return CallInner(context);
        }
Ejemplo n.º 6
0
            public QueryExpressionVisitor(QueryContext context,
                                          IQueryExpressionSourcer sourcer,
                                          IQueryExpressionAuthorizer authorizer = null,
                                          IQueryExpressionExpander expander     = null,
                                          IQueryExpressionProcessor processor   = null)
            {
                Ensure.NotNull(sourcer, nameof(sourcer));

                this.context         = new QueryExpressionContext(context);
                this.authorizer      = authorizer;
                this.expander        = expander;
                this.processor       = processor;
                this.sourcer         = sourcer;
                processedExpressions = new Dictionary <Expression, Expression>();
            }
Ejemplo n.º 7
0
        /// <summary>
        /// Inspects an expression.
        /// </summary>
        /// <param name="context">
        /// The query expression context.
        /// </param>
        /// <returns>
        /// <c>true</c> if the inspection passed; otherwise, <c>false</c>.
        /// </returns>
        public bool Inspect(QueryExpressionContext context)
        {
            Ensure.NotNull(context, "context");

            // TODO GitHubIssue#35 : Support Inspect more elements in authorization
            if (context.ModelReference == null)
            {
                return true;
            }

            var dataSourceStubReference = context.ModelReference as DataSourceStubReference;
            if (dataSourceStubReference == null)
            {
                return true;
            }

            var entitySet = dataSourceStubReference.Element as IEdmEntitySet;
            if (entitySet == null)
            {
                return true;
            }

            var assertedRoles = context.QueryContext
                .GetProperty<List<string>>(AssertedRoles);
            var permissions = context.QueryContext.ApiContext.GetApiService<IEnumerable<ApiPermission>>();
            if (permissions == null)
            {
                throw new SecurityException(
                    string.Format(CultureInfo.InvariantCulture, Resources.ReadDeniedOnEntitySet, entitySet.Name));
            }

            permissions = permissions.Where(p => (
                p.PermissionType == ApiPermissionType.All ||
                p.PermissionType == ApiPermissionType.Read) && (
                (p.NamespaceName == null && p.SecurableName == null) ||
                (p.NamespaceName == null && p.SecurableName == entitySet.Name)) &&
                p.ChildName == null && (p.Role == null || this.IsInRole(p.Role) ||
                (assertedRoles != null && assertedRoles.Contains(p.Role))));
            if (!permissions.Any() || permissions.Any(p => p.IsDeny))
            {
                throw new SecurityException(
                    string.Format(CultureInfo.InvariantCulture, Resources.ReadDeniedOnEntitySet, entitySet.Name));
            }

            return true;
        }
        private IQueryable GetEntitySetQuery(QueryExpressionContext context)
        {
            Ensure.NotNull(context, "context");
            if (context.ModelReference == null)
            {
                return null;
            }

            var dataSourceStubReference = context.ModelReference as DataSourceStubReference;
            if (dataSourceStubReference == null)
            {
                return null;
            }

            var entitySet = dataSourceStubReference.Element as IEdmEntitySet;
            if (entitySet == null)
            {
                return null;
            }

            var entitySetProperty = this.entitySetProperties
                .SingleOrDefault(p => p.Name == entitySet.Name);
            if (entitySetProperty != null)
            {
                object target = null;
                if (!entitySetProperty.GetMethod.IsStatic)
                {
                    target = context.QueryContext.GetApiService<ApiBase>();
                    if (target == null ||
                        !this.targetType.IsAssignableFrom(target.GetType()))
                    {
                        return null;
                    }
                }

                return entitySetProperty.GetValue(target) as IQueryable;
            }

            return null;
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Sources an expression.
        /// </summary>
        /// <param name="context">
        /// The query expression context.
        /// </param>
        /// <param name="embedded">
        /// Indicates if the sourcing is occurring on an embedded node.
        /// </param>
        /// <returns>
        /// A data source expression that represents the visited node.
        /// </returns>
        public Expression Source(QueryExpressionContext context, bool embedded)
        {
            Ensure.NotNull(context, "context");
            var dbContext = context.QueryContext
                .ApiContext.GetProperty<DbContext>(DbApiConstants.DbContextKey);
            var dbSetProperty = dbContext.GetType().GetProperties()
                .First(prop => prop.Name == context.ModelReference.EntitySet.Name);
            if (!embedded)
            {
                // TODO GitHubIssue#37 : Add API entity manager for tracking entities
                // the underlying DbContext shouldn't track the entities
                var dbSet = dbSetProperty.GetValue(dbContext);

                ////dbSet = dbSet.GetType().GetMethod("AsNoTracking").Invoke(dbSet, null);
                return Expression.Constant(dbSet);
            }
            else
            {
                return Expression.MakeMemberAccess(
                    Expression.Constant(dbContext),
                    dbSetProperty);
            }
        }
Ejemplo n.º 10
0
 public QueryExpressionVisitor(QueryContext context)
 {
     this.context = new QueryExpressionContext(context);
     this.processedExpressions = new Dictionary <Expression, Expression>();
 }
        /// <inheritdoc/>
        public Expression Expand(QueryExpressionContext context)
        {
            Ensure.NotNull(context);
            if (context.ModelReference == null)
            {
                return null;
            }

            var domainDataReference = context.ModelReference as DomainDataReference;
            if (domainDataReference == null)
            {
                return null;
            }

            var entitySet = domainDataReference.Element as IEdmEntitySet;
            if (entitySet == null)
            {
                return null;
            }

            var entitySetProperty = this.AddedEntitySets
                .SingleOrDefault(p => p.Name == entitySet.Name);
            if (entitySetProperty != null)
            {
                object target = null;
                if (!entitySetProperty.GetMethod.IsStatic)
                {
                    target = context.QueryContext.DomainContext.GetProperty(
                        this.targetType.AssemblyQualifiedName);
                    if (target == null ||
                        !this.targetType.IsAssignableFrom(target.GetType()))
                    {
                        return null;
                    }
                }

                var result = entitySetProperty.GetValue(target) as IQueryable;
                if (result != null)
                {
                    var policies = entitySetProperty.GetCustomAttributes()
                        .OfType<IDomainPolicy>();
                    foreach (var policy in policies)
                    {
                        policy.Activate(context.QueryContext);
                    }

                    context.AfterNestedVisitCallback = () =>
                    {
                        foreach (var policy in policies.Reverse())
                        {
                            policy.Deactivate(context.QueryContext);
                        }
                    };

                    return result.Expression;
                }
            }

            return null;
        }
Ejemplo n.º 12
0
 public QueryExpressionVisitor(QueryContext context)
 {
     this.context = new QueryExpressionContext(context);
     this.processedExpressions = new Dictionary<Expression, Expression>();
 }
            private Expression CallInner(QueryExpressionContext context, bool embedded)
            {
                if (this.InnerHandler != null)
                {
                    return this.InnerHandler.Source(context, embedded);
                }

                return null;
            }
            /// <inheritdoc/>
            public Expression Source(QueryExpressionContext context, bool embedded)
            {
                var result = CallInner(context, embedded);
                if (result != null)
                {
                    // Call the provider's sourcer to find the source of the query.
                    return result;
                }

                // This sourcer ONLY deals with queries that cannot be addressed by the provider
                // such as a singleton query that cannot be sourced by the EF provider, etc.
                var query = ModelCache.GetEntitySetQuery(context) ?? ModelCache.GetSingletonQuery(context);
                if (query != null)
                {
                    return Expression.Constant(query);
                }

                return null;
            }
            /// <inheritdoc/>
            public Expression Expand(QueryExpressionContext context)
            {
                Ensure.NotNull(context, "context");

                var result = CallInner(context);
                if (result != null)
                {
                    return result;
                }

                // Ensure this query constructs from DataSourceStubs.
                if (context.ModelReference is DataSourceStubReference)
                {
                    // Only expand entity set query which returns IQueryable<T>.
                    var query = ModelCache.GetEntitySetQuery(context);
                    if (query != null)
                    {
                        return query.Expression;
                    }
                }

                // No expansion happened just return the node itself.
                return context.VisitedNode;
            }
        private IQueryable GetSingletonQuery(QueryExpressionContext context)
        {
            Ensure.NotNull(context, "context");
            if (context.ModelReference == null)
            {
                return null;
            }

            var dataSourceStubReference = context.ModelReference as DataSourceStubReference;
            if (dataSourceStubReference == null)
            {
                return null;
            }

            var singleton = dataSourceStubReference.Element as IEdmSingleton;
            if (singleton == null)
            {
                return null;
            }

            var singletonProperty = this.singletonProperties
                .SingleOrDefault(p => p.Name == singleton.Name);
            if (singletonProperty != null)
            {
                object target = null;
                if (!singletonProperty.GetMethod.IsStatic)
                {
                    target = context.QueryContext.GetApiService<ApiBase>();
                    if (target == null ||
                        !this.targetType.IsAssignableFrom(target.GetType()))
                    {
                        return null;
                    }
                }

                var value = Array.CreateInstance(singletonProperty.PropertyType, 1);
                value.SetValue(singletonProperty.GetValue(target), 0);
                return value.AsQueryable();
            }

            return null;
        }
Ejemplo n.º 17
0
        public Expression Source(QueryExpressionContext context, bool embedded)
        {
            var a = new[] { new Product
            {
                Id = 1,
                Addr = new Address { Zip = 0001 },
                Addr2= new Address { Zip = 0002 }
            } };

            if (!embedded)
            {
                if (context.VisitedNode.ToString() == "Source(\"Products\", null)")
                {
                    return Expression.Constant(a.AsQueryable());
                }
            }

            return context.VisitedNode;
        }
Ejemplo n.º 18
0
 public Expression ReplaceQueryableSource(QueryExpressionContext context, bool embedded)
 {
     return Expression.Constant(new[] {"Test"}.AsQueryable());
 }
        /// <inheritdoc/>
        public Expression Process(QueryExpressionContext context)
        {
            Ensure.NotNull(context, "context");

            if (Inner != null)
            {
                var innerFilteredExpression = Inner.Process(context);
                if (innerFilteredExpression != null && innerFilteredExpression != context.VisitedNode)
                {
                    return innerFilteredExpression;
                }
            }

            var dataSourceStubReference = context.ModelReference as DataSourceStubModelReference;
            if (dataSourceStubReference != null)
            {
                var entitySet = dataSourceStubReference.Element as IEdmEntitySet;
                if (entitySet == null)
                {
                    return null;
                }

                var collectionType = entitySet.Type as IEdmCollectionType;
                if (collectionType == null)
                {
                    return null;
                }

                var entityType = collectionType.ElementType.Definition as IEdmEntityType;
                if (entityType == null)
                {
                    return null;
                }

                return AppendOnFilterExpression(context, entityType.Name);
            }

            var propertyModelReference = context.ModelReference as PropertyModelReference;
            if (propertyModelReference != null && propertyModelReference.Property != null)
            {
                // Could be a single navigation property or a collection navigation property
                var propType = propertyModelReference.Property.Type;
                var collectionTypeReference = propType as IEdmCollectionTypeReference;
                if (collectionTypeReference != null)
                {
                    var collectionType = collectionTypeReference.Definition as IEdmCollectionType;
                    propType = collectionType.ElementType;
                }

                var entityType = propType.Definition as IEdmEntityType;
                if (entityType == null)
                {
                    return null;
                }

                // In case of type inheritance, get the base type
                while (entityType.BaseType != null)
                {
                    entityType = (IEdmEntityType)entityType.BaseType;
                }

                return AppendOnFilterExpression(context, entityType.Name);
            }

            return null;
        }
        private Expression AppendOnFilterExpression(QueryExpressionContext context, string entityTypeName)
        {
            var methodName = ConventionBasedChangeSetConstants.FilterMethodEntitySetFilter + entityTypeName;
            var method = this.targetType.GetQualifiedMethod(methodName);
            if (method == null || !method.IsFamily)
            {
                return null;
            }

            var parameter = method.GetParameters().SingleOrDefault();
            if (parameter == null ||
                parameter.ParameterType != method.ReturnType)
            {
                return null;
            }

            object apiBase = null;
            if (!method.IsStatic)
            {
                apiBase = context.QueryContext.GetApiService<ApiBase>();
                if (apiBase == null ||
                    !this.targetType.IsInstanceOfType(apiBase))
                {
                    return null;
                }
            }

            // The LINQ expression built below has three cases
            // For navigation property, just add a where condition from OnFilter method
            // For collection property, will be like "Param_0.Prop.AsQueryable().Where(...)"
            // For collection property of derived type, will be like "Param_0.Prop.AsQueryable().Where(...).OfType()"
            var returnType = context.VisitedNode.Type.FindGenericType(typeof(IQueryable<>));
            var enumerableQueryParameter = (object)context.VisitedNode;
            Type elementType = null;

            if (returnType == null)
            {
                // This means append for properties model reference
                var collectionType = context.VisitedNode.Type.FindGenericType(typeof(ICollection<>));
                if (collectionType == null)
                {
                    return null;
                }

                elementType = collectionType.GetGenericArguments()[0];
                returnType = typeof(IQueryable<>).MakeGenericType(elementType);

                enumerableQueryParameter = Expression.Call(
                    ExpressionHelperMethods.QueryableAsQueryableGeneric.MakeGenericMethod(elementType),
                    context.VisitedNode);
            }
            else
            {
                elementType = returnType.GetGenericArguments()[0];
            }

            var queryType = typeof(EnumerableQuery<>).MakeGenericType(elementType);
            var query = Activator.CreateInstance(queryType, enumerableQueryParameter);
            var result = method.Invoke(apiBase, new object[] { query }) as IQueryable;
            if (result == null)
            {
                return null;
            }

            if (method.ReturnType == returnType)
            {
                if (result != query)
                {
                    return result.Expression;
                }
            }
            else
            {
                // This means calling onFilter against derived type and based type is returned
                // Need to convert back to derived type with OfType
                result = ExpressionHelpers.OfType(result, elementType);
                return result.Expression;
            }

            return null;
        }
Ejemplo n.º 21
0
        public Expression ReplaceQueryableSource(QueryExpressionContext context, bool embedded)
        {
            var a = new[] { new Product
            {
                Id = 1,
                Addr = new Address { Zip = 0001 },
                Addr2= new Address { Zip = 0002 }
            } };

            var b = new[] { new Customer
            {
                Id = 1,
            } };

            var c = new[] { new Store
            {
                Id = 1,
            } };

            if (!embedded)
            {
                if (context.VisitedNode.ToString() == "GetQueryableSource(\"Products\", null)")
                {
                    return Expression.Constant(a.AsQueryable());
                }

                if (context.VisitedNode.ToString() == "GetQueryableSource(\"Customers\", null)")
                {
                    return Expression.Constant(b.AsQueryable());
                }

                if (context.VisitedNode.ToString() == "GetQueryableSource(\"Stores\", null)")
                {
                    return Expression.Constant(c.AsQueryable());
                }
            }

            return context.VisitedNode;
        }
Ejemplo n.º 22
0
        public Expression Source(QueryExpressionContext context, bool embedded)
        {
            var orders = new[]
            {
                new Order {Id = 234}
            };

            if (!embedded)
            {
                if (context.VisitedNode.ToString().StartsWith("Source(\"Orders\""))
                {
                    return Expression.Constant(orders.AsQueryable());
                }
            }

            return context.VisitedNode;
        }
Ejemplo n.º 23
0
 public Expression Source(QueryExpressionContext context, bool embedded)
 {
     throw new SecurityException();
 }