/// <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; }
private Expression CallInner(QueryExpressionContext context) { if (this.InnerHandler != null) { return this.InnerHandler.Expand(context); } return null; }
/// <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); }
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>(); }
/// <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; }
/// <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); } }
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; }
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; }
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; }
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; }
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; }
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; }
public Expression Source(QueryExpressionContext context, bool embedded) { throw new SecurityException(); }