private static IEdmOperation[] FindCandidates(ODataControllerActionContext context, IEdmEntityType entityType, string actionName, out IEdmEntityType castTypeFromActionName, out bool isOnCollection) { // OperationNameOnCollectionOfEntityType StringComparison caseComparision = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; string operationName = SplitActionName(actionName, out string cast, out isOnCollection, caseComparision); castTypeFromActionName = null; if (cast != null) { if (cast.Length == 0) { // Early return for the following cases: // - {OperationName}On // - {OperationName}OnCollectionOf return(Array.Empty <IEdmOperation>()); } castTypeFromActionName = entityType.FindTypeInInheritance(context.Model, cast, context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true) as IEdmEntityType; if (castTypeFromActionName == null) { return(Array.Empty <IEdmOperation>()); } } return(FindCandidates(context, operationName)); }
/// <inheritdoc /> public bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } Debug.Assert(context.Singleton != null); Debug.Assert(context.Action != null); ActionModel action = context.Action; string singletonName = context.Singleton.Name; string actionMethodName = action.ActionName; if (IsSupportedActionName(actionMethodName, singletonName, out string httpMethod)) { // ~/Me ODataPathTemplate template = new ODataPathTemplate(new SingletonSegmentTemplate(context.Singleton)); action.AddSelector(httpMethod, context.Prefix, context.Model, template, context.Options?.RouteOptions); // processed return(true); } // type cast // GetFrom{EntityTypeName} or Get{SingletonName}From{EntityTypeName} int index = actionMethodName.IndexOf("From", StringComparison.Ordinal); if (index == -1) { return(false); } string actionPrefix = actionMethodName.Substring(0, index); if (IsSupportedActionName(actionPrefix, singletonName, out httpMethod)) { string castTypeName = actionMethodName.Substring(index + 4); IEdmEntityType entityType = context.Singleton.EntityType(); // Shall we cast to base type and the type itself? I think yes. IEdmStructuredType castType = entityType.FindTypeInInheritance(context.Model, castTypeName); if (castType != null) { // ~/Me/Namespace.TypeCast ODataPathTemplate template = new ODataPathTemplate( new SingletonSegmentTemplate(context.Singleton), new CastSegmentTemplate(castType, entityType, context.Singleton)); action.AddSelector(httpMethod, context.Prefix, context.Model, template, context.Options?.RouteOptions); return(true); } } return(false); }
/// <inheritdoc /> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } ActionModel action = context.Action; IEdmEntitySet entitySet = context.EntitySet; IEdmEntityType entityType = entitySet.EntityType(); // if the action has key parameter, skip it. if (action.HasODataKeyParameter(entityType, context.Options?.RouteOptions?.EnablePropertyNameCaseInsensitive ?? false)) { return(false); } string actionName = action.ActionName; // 1. Without type case if (ProcessEntitySetAction(actionName, entitySet, null, context, action)) { return(true); } // 2. process the derived type (cast) by searching all derived types // GetFrom{EntityTypeName} or Get{EntitySet}From{EntityTypeName} int index = actionName.IndexOf("From", StringComparison.Ordinal); if (index == -1) { return(false); } string castTypeName = actionName.Substring(index + 4); // + 4 means to skip the "From" if (castTypeName.Length == 0) { // Early return for the following cases: // - Get|Post|PatchFrom // - Get|Patch{EntitySet}From // - Post{EntityType}From return(false); } IEdmStructuredType castType = entityType.FindTypeInInheritance(context.Model, castTypeName); if (castType == null) { return(false); } string actionPrefix = actionName.Substring(0, index); return(ProcessEntitySetAction(actionPrefix, entitySet, castType, context, action)); }
/// <inheritdoc /> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } ActionModel action = context.Action; IEdmEntitySet entitySet = context.EntitySet; IEdmEntityType entityType = entitySet.EntityType(); // if the action has key parameter, skip it. if (action.HasODataKeyParameter(entityType)) { return(false); } string actionName = action.ActionMethod.Name; // 1. Without type case if (ProcessEntitySetAction(actionName, entitySet, null, context, action)) { return(true); } // 2. process the derive type (cast) by searching all derived types // GetFrom{EntityTypeName} or Get{EntitySet}From{EntityTypeName} int index = actionName.IndexOf("From", StringComparison.Ordinal); if (index == -1) { return(false); } string castTypeName = actionName.Substring(index + 4); // + 4 means to skip the "From" IEdmStructuredType castType = entityType.FindTypeInInheritance(context.Model, castTypeName); if (castType == null) { return(false); } string actionPrefix = actionName.Substring(0, index); return(ProcessEntitySetAction(actionPrefix, entitySet, castType, context, action)); }
/// <inheritdoc /> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } ActionModel action = context.Action; IEdmEntitySet entitySet = context.EntitySet; IEdmEntityType entityType = entitySet.EntityType(); // if the action has no key parameter, skip it. if (!action.HasODataKeyParameter(entityType)) { return(false); } string actionName = action.ActionMethod.Name; // We care about the action in this pattern: {HttpMethod}{EntityTypeName} (string httpMethod, string castTypeName) = Split(actionName); if (httpMethod == null) { return(false); } IEdmStructuredType castType = null; if (castTypeName != null) { castType = entityType.FindTypeInInheritance(context.Model, castTypeName); if (castType == null) { return(false); } } AddSelector(entitySet, entityType, castType, context.Prefix, context.Model, action, httpMethod); return(true); }
/// <summary> /// /// </summary> /// <param name="context"></param> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } ActionModel action = context.Action; if (context.EntitySet == null && context.Singleton == null) { return(false); } IEdmNavigationSource navigationSource = context.EntitySet == null ? (IEdmNavigationSource)context.Singleton : (IEdmNavigationSource)context.EntitySet; string actionName = action.ActionMethod.Name; string method = Split(actionName, out string property, out string cast, out string declared); if (method == null || string.IsNullOrEmpty(property)) { return(false); } IEdmEntityType entityType = navigationSource.EntityType(); IEdmModel model = context.Model; string prefix = context.Prefix; IEdmEntityType declaredEntityType = null; if (declared != null) { declaredEntityType = entityType.FindTypeInInheritance(model, declared) as IEdmEntityType; if (declaredEntityType == null) { return(false); } if (declaredEntityType == entityType) { declaredEntityType = null; } } bool hasKeyParameter = HasKeyParameter(entityType, action); IEdmSingleton singleton = navigationSource as IEdmSingleton; if (singleton != null && hasKeyParameter) { // Singleton, doesn't allow to query property with key return(false); } if (singleton == null && !hasKeyParameter) { // in entityset, doesn't allow for non-key to query property return(false); } IEdmProperty edmProperty = entityType.FindProperty(property); if (edmProperty != null && edmProperty.PropertyKind == EdmPropertyKind.Structural) { // only process structural property IEdmStructuredType castComplexType = null; if (cast != null) { IEdmTypeReference propertyType = edmProperty.Type; if (propertyType.IsCollection()) { propertyType = propertyType.AsCollection().ElementType(); } if (!propertyType.IsComplex()) { return(false); } castComplexType = propertyType.ToStructuredType().FindTypeInInheritance(model, cast); if (castComplexType == null) { return(false); } } IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>(); if (context.EntitySet != null) { segments.Add(new EntitySetSegmentTemplate(context.EntitySet)); } else { segments.Add(new SingletonSegmentTemplate(context.Singleton)); } if (hasKeyParameter) { segments.Add(new KeySegmentTemplate(entityType)); } if (declaredEntityType != null && declaredEntityType != entityType) { segments.Add(new CastSegmentTemplate(declaredEntityType)); } segments.Add(new PropertySegmentTemplate((IEdmStructuralProperty)edmProperty)); ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(prefix, model, template); return(true); } else { // map to a static action like: <method>Property(int key, string property)From<...> if (property == "Property" && cast == null) { if (action.Parameters.Any(p => p.ParameterInfo.Name == "property" && p.ParameterType == typeof(string))) { // we find a static method mapping for all property // we find a action route IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>(); if (context.EntitySet != null) { segments.Add(new EntitySetSegmentTemplate(context.EntitySet)); } else { segments.Add(new SingletonSegmentTemplate(context.Singleton)); } if (hasKeyParameter) { segments.Add(new KeySegmentTemplate(entityType)); } if (declaredEntityType != null) { segments.Add(new CastSegmentTemplate(declaredEntityType)); } segments.Add(new PropertySegmentTemplate((string)null /*entityType*/)); ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(prefix, model, template); return(true); } } } return(false); }
/// <summary> /// Process the operation candidates using the information. /// </summary> /// <param name="context">The controller and action context.</param> /// <param name="entityType">The Edm entity type.</param> /// <param name="navigationSource">The Edm navigation source.</param> protected void ProcessOperations(ODataControllerActionContext context, IEdmEntityType entityType, IEdmNavigationSource navigationSource) { Contract.Assert(context != null); Contract.Assert(entityType != null); Contract.Assert(navigationSource != null); string actionName = context.Action.ActionMethod.Name; bool hasKeyParameter = context.Action.HasODataKeyParameter(entityType); if (context.Singleton != null && hasKeyParameter) { // Singleton doesn't allow to call action with key. return; } // OperationNameOnCollectionOfEntityType string operationName = SplitActionName(actionName, out string cast, out bool isOnCollection); IEdmEntityType castTypeFromActionName = null; if (cast != null) { castTypeFromActionName = entityType.FindTypeInInheritance(context.Model, cast) as IEdmEntityType; if (castTypeFromActionName == null) { return; } } // TODO: refactor here // If we have mulitple same function defined, we should match the best one? IEnumerable <IEdmOperation> candidates = context.Model.SchemaElements.OfType <IEdmOperation>().Where(f => f.IsBound && f.Name == operationName); foreach (IEdmOperation edmOperation in candidates) { IEdmOperationParameter bindingParameter = edmOperation.Parameters.FirstOrDefault(); if (bindingParameter == null) { // bound operation at least has one parameter which type is the binding type. continue; } IEdmTypeReference bindingType = bindingParameter.Type; bool bindToCollection = bindingType.TypeKind() == EdmTypeKind.Collection; if (bindToCollection) { // if binding to collection the action has key parameter or a singleton, skip if (context.Singleton != null || hasKeyParameter) { continue; } } else { // if binding to non-collection and the action hasn't key parameter, skip if (isOnCollection || (context.EntitySet != null && !hasKeyParameter)) { continue; } } // We only allow the binding type is entity type or collection of entity type. if (!bindingType.Definition.IsEntityOrEntityCollectionType(out IEdmEntityType bindingEntityType)) { continue; } IEdmEntityType castType = null; if (castTypeFromActionName == null) { if (entityType.IsOrInheritsFrom(bindingEntityType)) { // True if and only if the thisType is equivalent to or inherits from otherType. castType = null; } else if (bindingEntityType.InheritsFrom(entityType)) { // True if and only if the type inherits from the potential base type. castType = bindingEntityType; } else { continue; } } else { if (isOnCollection && !bindToCollection) { continue; } if (bindingEntityType != castTypeFromActionName) { continue; } if (castTypeFromActionName != entityType) { castType = castTypeFromActionName; } } // TODO: need discussion ahout: // 1) Do we need to match the whole parameter count? // 2) Do we need to select the best match? So far, i don't think and let it go. if (!IsOperationParameterMeet(edmOperation, context.Action)) { continue; } AddSelector(context, edmOperation, hasKeyParameter, entityType, navigationSource, castType); } }
/// <inheritdoc /> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } if (context.EntitySet == null && context.Singleton == null) { return(false); } ActionModel action = context.Action; // Filter by the action name. // The action for navigation property request should follow up {httpMethod}{PropertyName}[From{Declaring}] string actionName = action.ActionMethod.Name; string method = SplitActionName(actionName, out string property, out string declared); if (method == null || string.IsNullOrEmpty(property)) { return(false); } IEdmNavigationSource navigationSource = context.EntitySet == null ? (IEdmNavigationSource)context.Singleton : (IEdmNavigationSource)context.EntitySet; // filter by action parameter IEdmEntityType entityType = navigationSource.EntityType(); bool hasKeyParameter = action.HasODataKeyParameter(entityType); if (!(context.Singleton != null ^ hasKeyParameter)) { // Singleton, doesn't allow to query property with key // entityset, doesn't allow for non-key to query property return(false); } // Find the declaring type of the property if we have the declaring type name in the action name. // eitherwise, it means the property is defined on the entity type of the navigation source. IEdmEntityType declaringEntityType = entityType; if (declared != null) { declaringEntityType = entityType.FindTypeInInheritance(context.Model, declared) as IEdmEntityType; if (declaringEntityType == null) { return(false); } } // Find the property, and we only care about the navigation property. IEdmProperty edmProperty = declaringEntityType.FindProperty(property); if (edmProperty == null || edmProperty.PropertyKind == EdmPropertyKind.Structural) { return(false); } // Starts the routing template //IList<ODataSegmentTemplate> segments = new List<ODataSegmentTemplate>(); //if (context.EntitySet != null) //{ // segments.Add(new EntitySetSegmentTemplate(context.EntitySet)); //} //else //{ // segments.Add(new SingletonSegmentTemplate(context.Singleton)); //} //if (hasKeyParameter) //{ // segments.Add(new KeySegmentTemplate(entityType)); //} //if (declared != null) //{ // // It should be always single type // segments.Add(new CastSegmentTemplate(declaringEntityType, entityType, navigationSource)); //} IEdmNavigationProperty navigationProperty = (IEdmNavigationProperty)edmProperty; //IEdmNavigationSource targetNavigationSource = navigationSource.FindNavigationTarget(navigationProperty, segments, out _); //segments.Add(new NavigationSegmentTemplate(navigationProperty, targetNavigationSource)); //ODataPathTemplate template = new ODataPathTemplate(segments); //action.AddSelector(method, context.Prefix, context.Model, template); AddSelector(method, context.Prefix, context.Model, action, navigationSource, declared, declaringEntityType, navigationProperty, hasKeyParameter, false); if (CanApplyDollarCount(navigationProperty, method)) { AddSelector(method, context.Prefix, context.Model, action, navigationSource, declared, declaringEntityType, navigationProperty, hasKeyParameter, true); } return(true); }
/// <inheritdoc /> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } if (context.EntitySet == null && context.Singleton == null) { return(false); } IEdmNavigationSource navigationSource = context.EntitySet == null ? (IEdmNavigationSource)context.Singleton : (IEdmNavigationSource)context.EntitySet; ActionModel action = context.Action; string actionName = action.ActionMethod.Name; string method = SplitActionName(actionName, out string property, out string cast, out string declared); if (method == null || string.IsNullOrEmpty(property)) { return(false); } // filter by action parameter IEdmEntityType entityType = navigationSource.EntityType(); bool hasKeyParameter = action.HasODataKeyParameter(entityType); if (!(context.Singleton != null ^ hasKeyParameter)) { // Singleton, doesn't allow to query property with key // entityset, doesn't allow for non-key to query property return(false); } // Find the declaring type of the property if we have the declaring type name in the action name. // eitherwise, it means the property is defined on the entity type of the navigation source. IEdmEntityType declaringEntityType = entityType; if (declared != null) { declaringEntityType = entityType.FindTypeInInheritance(context.Model, declared) as IEdmEntityType; if (declaringEntityType == null) { return(false); } } IEdmProperty edmProperty = declaringEntityType.FindProperty(property); if (edmProperty == null || edmProperty.PropertyKind != EdmPropertyKind.Structural) { return(false); } if (!CanApply(edmProperty, method)) { return(false); } IEdmComplexType castType = null; if (cast != null) { IEdmType propertyElementType = edmProperty.Type.Definition.AsElementType(); if (propertyElementType.TypeKind == EdmTypeKind.Complex) { IEdmComplexType complexType = (IEdmComplexType)propertyElementType; castType = complexType.FindTypeInInheritance(context.Model, cast) as IEdmComplexType; if (castType == null) { return(false); } } else { // only support complex type cast, (TODO: maybe consider to support Edm.PrimitiveType cast) return(false); } } // only process structural property IEdmStructuredType castComplexType = null; if (cast != null) { IEdmTypeReference propertyType = edmProperty.Type; if (propertyType.IsCollection()) { propertyType = propertyType.AsCollection().ElementType(); } if (!propertyType.IsComplex()) { return(false); } castComplexType = propertyType.ToStructuredType().FindTypeInInheritance(context.Model, cast); if (castComplexType == null) { return(false); } } AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, false, false); if (CanApplyDollarCount(edmProperty, method)) { AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, false, true); } if (CanApplyDollarValue(edmProperty, method)) { AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, true, false); } return(true); }
/// <inheritdoc /> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } IEdmNavigationSource navigationSource = context.NavigationSource; if (navigationSource == null) { return(false); } ActionModel action = context.Action; string actionName = action.ActionName; string method = SplitActionName(actionName, out string property, out string cast, out string declared); if (method == null || string.IsNullOrEmpty(property)) { return(false); } // filter by action parameter IEdmEntityType entityType = navigationSource.EntityType(); bool hasKeyParameter = action.HasODataKeyParameter(entityType, context.Options?.RouteOptions?.EnablePropertyNameCaseInsensitive ?? false); if (!(context.Singleton != null ^ hasKeyParameter)) { // Singleton, doesn't allow to query property with key // entityset, doesn't allow for non-key to query property return(false); } // Find the declaring type of the property if we have the declaring type name in the action name. // otherwise, it means the property is defined on the entity type of the navigation source. IEdmEntityType declaringEntityType = entityType; if (declared != null) { if (declared.Length == 0) { // Early return for the following cases: // - Get|PostTo|PutTo|PatchTo|DeleteTo{PropertyName}From // - Get|PostTo|PutTo|PatchTo|DeleteTo{PropertyName}Of{Cast}From return(false); } declaringEntityType = entityType.FindTypeInInheritance(context.Model, declared) as IEdmEntityType; if (declaringEntityType == null) { return(false); } } bool enablePropertyNameCaseInsensitive = context?.Options?.RouteOptions.EnablePropertyNameCaseInsensitive ?? false; IEdmProperty edmProperty = declaringEntityType.FindProperty(property, enablePropertyNameCaseInsensitive); if (edmProperty == null || edmProperty.PropertyKind != EdmPropertyKind.Structural) { return(false); } if (!CanApply(edmProperty, method)) { return(false); } IEdmComplexType castType; // Only process structural property IEdmStructuredType castComplexType = null; if (cast != null) { if (cast.Length == 0) { // Avoid unnecessary call to FindTypeInheritance // Cases handled: Get|PostTo|PutTo|PatchTo|DeleteTo{PropertyName}Of return(false); } IEdmType propertyElementType = edmProperty.Type.Definition.AsElementType(); if (propertyElementType.TypeKind == EdmTypeKind.Complex) { IEdmComplexType complexType = (IEdmComplexType)propertyElementType; castType = complexType.FindTypeInInheritance(context.Model, cast) as IEdmComplexType; if (castType == null) { return(false); } } else { // only support complex type cast, (TODO: maybe consider to support Edm.PrimitiveType cast) return(false); } IEdmTypeReference propertyType = edmProperty.Type; if (propertyType.IsCollection()) { propertyType = propertyType.AsCollection().ElementType(); } if (!propertyType.IsComplex()) { return(false); } castComplexType = propertyType.ToStructuredType().FindTypeInInheritance(context.Model, cast); if (castComplexType == null) { return(false); } } AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, false, false); if (CanApplyDollarCount(edmProperty, method)) { AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, false, true); } if (CanApplyDollarValue(edmProperty, method)) { AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, true, false); } return(true); }
/// <inheritdoc /> public bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } Debug.Assert(context.Action != null); ActionModel action = context.Action; string actionMethodName = action.ActionName; // Need to refactor the following // for example: CreateRef( with the navigation property parameter) should for all navigation properties // CreateRefToOrdersFromCustomer, CreateRefToOrders, CreateRef. string method = SplitRefActionName(actionMethodName, out string httpMethod, out string property, out string declaring); if (method == null) { return(false); } IEdmNavigationSource navigationSource = context.NavigationSource; IEdmEntityType entityType = context.EntityType; // For entity set, we should have the key parameter // For Singleton, we should not have the key parameter bool hasODataKeyParameter = action.HasODataKeyParameter(entityType); if ((context.EntitySet != null && !hasODataKeyParameter) || (context.Singleton != null && hasODataKeyParameter)) { return(false); } // Find the navigation property declaring type IEdmStructuredType declaringType = entityType; if (declaring != null) { declaringType = entityType.FindTypeInInheritance(context.Model, declaring); if (declaringType == null) { return(false); } } // Process the generic scenario if (property == null) { return(ProcessNonNavigationProperty(httpMethod, context, action, navigationSource, entityType, declaringType)); } // Find the navigation property if have IEdmNavigationProperty navigationProperty = null; if (property != null) { navigationProperty = declaringType.DeclaredNavigationProperties().FirstOrDefault(p => p.Name == property); } if (navigationProperty == null) { return(false); } IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>(); if (context.EntitySet != null) { segments.Add(new EntitySetSegmentTemplate(context.EntitySet)); segments.Add(KeySegmentTemplate.CreateKeySegment(entityType, context.EntitySet)); } else { segments.Add(new SingletonSegmentTemplate(context.Singleton)); } if (entityType != declaringType) { segments.Add(new CastSegmentTemplate(declaringType, entityType, navigationSource)); } IEdmNavigationSource targetNavigationSource = navigationSource.FindNavigationTarget(navigationProperty, segments, out _); NavigationLinkSegmentTemplate linkTemplate = new NavigationLinkSegmentTemplate(navigationProperty, targetNavigationSource); IEdmEntityType navigationPropertyType = navigationProperty.Type.GetElementTypeOrSelf().AsEntity().EntityDefinition(); bool hasNavigationPropertyKeyParameter = action.HasODataKeyParameter(navigationPropertyType, "relatedKey"); if (hasNavigationPropertyKeyParameter) { linkTemplate.Key = KeySegmentTemplate.CreateKeySegment(navigationPropertyType, targetNavigationSource, "relatedKey"); } else { hasNavigationPropertyKeyParameter = action.HasODataKeyParameter(navigationPropertyType, "relatedId"); if (hasNavigationPropertyKeyParameter) { linkTemplate.Key = KeySegmentTemplate.CreateKeySegment(navigationPropertyType, targetNavigationSource, "relatedId"); } } segments.Add(linkTemplate); ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(httpMethod, context.Prefix, context.Model, template, context.Options?.RouteOptions); // processed return(true); }