/// <inheritdoc /> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } Debug.Assert(context.Controller != null); Debug.Assert(context.Action != null); ActionModel action = context.Action; string actionName = action.ActionMethod.Name; // for ~$metadata if (actionName == "GetMetadata") { ODataPathTemplate template = new ODataPathTemplate(MetadataSegmentTemplate.Instance); action.AddSelector("Get", context.Prefix, context.Model, template, context.RouteOptions); return(true); } // for ~/ if (actionName == "GetServiceDocument") { ODataPathTemplate template = new ODataPathTemplate(); action.AddSelector("Get", context.Prefix, context.Model, template, context.RouteOptions); return(true); } return(false); }
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 override bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } IEdmNavigationSource navigationSource = context.EntitySet == null ? (IEdmNavigationSource)context.Singleton : (IEdmNavigationSource)context.EntitySet; IEdmEntityType entityType = navigationSource.EntityType(); // function should have the [HttpPost] if (!context.Action.Attributes.Any(a => a is HttpPostAttribute)) { return(false); } // action overload on binding type, only one action overload on the same binding type. // however, it supports the bound action on derived type. ProcessOperations(context, entityType, navigationSource); // in OData operationImport routing convention, all action are processed by default // even it's not a really edm operation import call. return(false); }
/// <summary> /// /// </summary> /// <param name="context"></param> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } Debug.Assert(context.Controller != null); Debug.Assert(context.Action != null); ActionModel action = context.Action; if (action.Controller.ControllerType != typeof(MetadataController).GetTypeInfo()) { return(false); } if (action.ActionMethod.Name == "GetMetadata") { ODataPathTemplate template = new ODataPathTemplate(MetadataSegmentTemplate.Instance); action.AddSelector(context.Prefix, context.Model, template); return(true); } if (action.ActionMethod.Name == "GetServiceDocument") { ODataPathTemplate template = new ODataPathTemplate(); action.AddSelector(context.Prefix, context.Model, template); return(true); } return(false); }
private void ProcessAttributeModel(AttributeRouteModel attributeRouteModel, IEnumerable <string> prefixes, ODataControllerActionContext context, SelectorModel actionSelector, ActionModel actionModel, ControllerModel controllerModel, IDictionary <SelectorModel, IList <SelectorModel> > updatedSelectors) { if (attributeRouteModel == null) { // not an attribute routing, skip it. return; } string prefix = FindRelatedODataPrefix(attributeRouteModel.Template, prefixes, out string newRouteTemplate); if (prefix == null) { return; } IEdmModel model = context.Options.RouteComponents[prefix].EdmModel; IServiceProvider sp = context.Options.RouteComponents[prefix].ServiceProvider; SelectorModel newSelectorModel = CreateActionSelectorModel(prefix, model, sp, newRouteTemplate, actionSelector, attributeRouteModel.Template, actionModel.ActionName, controllerModel.ControllerName); if (newSelectorModel != null) { IList <SelectorModel> selectors; if (!updatedSelectors.TryGetValue(actionSelector, out selectors)) { selectors = new List <SelectorModel>(); updatedSelectors[actionSelector] = selectors; } selectors.Add(newSelectorModel); } }
/// <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); }
internal static bool ProcessNonNavigationProperty(string httpMethod, ODataControllerActionContext context, ActionModel action, IEdmNavigationSource navigationSource, IEdmEntityType entityType, IEdmStructuredType castType) { // Action parameter should have a (string navigationProperty) parameter if (!action.HasParameter <string>("navigationProperty")) { return(false); } // Let's only handle single-key convention, for composite key, use attribute routing or non-generic navigation. bool hasRelatedKey = action.Parameters.Any(p => p.Name == "relatedKey"); // case sensitive? bool hasRelatedId = action.Parameters.Any(p => p.Name == "relatedId"); 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 != castType) { segments.Add(new CastSegmentTemplate(castType, entityType, navigationSource)); } if (hasRelatedKey) { segments.Add(new NavigationLinkTemplateSegmentTemplate(entityType, navigationSource) { RelatedKey = "relatedKey" }); } else if (hasRelatedId) { segments.Add(new NavigationLinkTemplateSegmentTemplate(entityType, navigationSource) { RelatedKey = "relatedId" }); } else { segments.Add(new NavigationLinkTemplateSegmentTemplate(entityType, navigationSource)); } ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(httpMethod, context.Prefix, context.Model, template, context.Options?.RouteOptions); return(true); }
/// <inheritdoc /> public virtual bool AppliesToController(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } return(context.Singleton != null); }
/// <inheritdoc /> public virtual bool AppliesToController(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } return(context.EntitySet != null); }
/// <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 AppliesToController(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } // This convention only applies to "MetadataController". return(context.Controller.ControllerType == metadataTypeInfo); }
/// <inheritdoc /> public virtual bool AppliesToController(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } // bound operation supports for entity set and singleton return(context.NavigationSource != null); }
/// <inheritdoc /> public virtual bool AppliesToController(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } // structural property supports for entity set and singleton return(context.EntitySet != null || context.Singleton != null); }
/// <inheritdoc /> public virtual bool AppliesToController(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } // By convention, we look for the controller name as "ODataOperationImportController" // Each operation import will be handled by the same action name in this controller. return(context.Controller.ControllerName == "ODataOperationImport"); }
private static void AddSelector(string httpMethod, ODataControllerActionContext context, ActionModel action, IEdmNavigationSource navigationSource, IEdmStructuralProperty edmProperty, IEdmType cast, IEdmEntityType declaringType, bool dollarValue, bool dollarCount) { IEdmEntitySet entitySet = navigationSource as IEdmEntitySet; IEdmEntityType entityType = navigationSource.EntityType(); IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>(); if (entitySet != null) { segments.Add(new EntitySetSegmentTemplate(entitySet)); segments.Add(KeySegmentTemplate.CreateKeySegment(entityType, navigationSource)); } else { segments.Add(new SingletonSegmentTemplate(navigationSource as IEdmSingleton)); } if (declaringType != null && declaringType != entityType) { segments.Add(new CastSegmentTemplate(declaringType, entityType, navigationSource)); } segments.Add(new PropertySegmentTemplate(edmProperty)); if (cast != null) { if (edmProperty.Type.IsCollection()) { cast = new EdmCollectionType(cast.ToEdmTypeReference(edmProperty.Type.IsNullable)); } // TODO: maybe create the collection type for the collection???? segments.Add(new CastSegmentTemplate(cast, edmProperty.Type.Definition, navigationSource)); } if (dollarValue) { segments.Add(new ValueSegmentTemplate(edmProperty.Type.Definition)); } if (dollarCount) { segments.Add(CountSegmentTemplate.Instance); } ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(httpMethod.NormalizeHttpMethod(), context.Prefix, context.Model, template, context.Options?.RouteOptions); }
/// <inheritdoc /> public virtual bool AppliesToController(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } // It allows to use attribute routing without ODataRoutePrefixAttribute. // In this case, we only use the ODataRouteAttrbute to construct the route template. // Otherwise, we combine each route prefix with each route attribute to construct the route template. foreach (var pathTemplatePrefix in GetODataPathTemplatePrefixes(context.Prefix, context.Controller)) { foreach (var action in context.Controller.Actions) { var routeAttributes = action.Attributes.OfType <ODataRouteAttribute>(); foreach (ODataRouteAttribute routeAttribute in routeAttributes) { // If we have the route prefix name setting, make sure we only let the attribute with the same route prefx to pass. if (routeAttribute.RoutePrefix != null && !string.Equals(routeAttribute.RoutePrefix, context.Prefix, StringComparison.OrdinalIgnoreCase)) { continue; } try { string routeTemplate = GetODataPathTemplateString(pathTemplatePrefix, routeAttribute.PathTemplate); ODataPathTemplate pathTemplate = _templateParser.Parse(context.Model, routeTemplate, context.ServiceProvider); // Add the httpMethod? action.AddSelector(null, context.Prefix, context.Model, pathTemplate, context.RouteOptions); } catch (ODataException ex) { // use the logger to log the wrong odata attribute template. Shall we log the others? string warning = string.Format(CultureInfo.CurrentCulture, SRResources.InvalidODataRouteOnAction, routeAttribute.PathTemplate, action.ActionMethod.Name, context.Controller.ControllerName, ex.Message); _logger.LogWarning(warning); } } } } // We execute this convention on all actions in the controller level. // So, returns false to make sure we don't want to call the AppliesToAction for this convention. return(false); }
private static IEdmOperation[] FindCandidates(ODataControllerActionContext context, string operationName) { // TODO: refactor here // If we have multiple same function defined, we should match the best one? StringComparison actionNameComparison = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; return(context.Model.SchemaElements .OfType <IEdmOperation>() .Where(f => f.IsBound && f.Name.Equals(operationName, actionNameComparison)) .ToArray()); }
/// <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) { return(false); } IEdmEntitySet entitySet = context.EntitySet; if (action.Parameters.Count != 0) { // TODO: improve here to accept other parameters, for example ODataQueryOptions<T> return(false); } string actionName = action.ActionMethod.Name; if (actionName == "Get" || actionName == $"Get{entitySet.Name}") { ODataPathTemplate template = new ODataPathTemplate(new EntitySetSegmentTemplate(entitySet)); action.AddSelector(context.Prefix, context.Model, template); // $count template = new ODataPathTemplate(new EntitySetSegmentTemplate(entitySet), CountSegmentTemplate.Instance); action.AddSelector(context.Prefix, context.Model, template); return(true); } else if (actionName == "Post" || actionName == $"Post{entitySet.EntityType().Name}") { ODataPathTemplate template = new ODataPathTemplate(new EntitySetSegmentTemplate(entitySet)); action.AddSelector(context.Prefix, context.Model, template); return(true); } else { // process the derive type (cast) // search all derived types } return(false); }
/// <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 || action.Parameters.Count < 1) { // At lease one parameter for the key. return(false); } IEdmEntitySet entitySet = context.EntitySet; var entityType = entitySet.EntityType(); var entityTypeName = entitySet.EntityType().Name; var keys = entitySet.EntityType().Key().ToArray(); string actionName = action.ActionMethod.Name; if ((actionName == "Get" || actionName == $"Get{entityTypeName}" || actionName == "Put" || actionName == $"Put{entityTypeName}" || actionName == "Patch" || actionName == $"Patch{entityTypeName}" || actionName == "Delete" || actionName == $"Delete{entityTypeName}") && keys.Length == action.Parameters.Count) { ODataPathTemplate template = new ODataPathTemplate( new EntitySetSegmentTemplate(entitySet), new KeySegmentTemplate(entityType) ); // support key in parenthesis action.AddSelector(context.Prefix, context.Model, template); // support key as segment ODataPathTemplate newTemplate = template.Clone(); newTemplate.KeyAsSegment = true; action.AddSelector(context.Prefix, context.Model, newTemplate); return(true); } return(false); }
private void AddSelector(string httpMethod, ODataControllerActionContext context, ActionModel action, IEdmNavigationSource navigationSource, string declared, IEdmEntityType declaringEntityType, IEdmNavigationProperty navigationProperty, bool hasKey, bool dollarCount) { IEdmEntitySet entitySet = navigationSource as IEdmEntitySet; IEdmEntityType entityType = navigationSource.EntityType(); // Starts the routing template IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>(); if (entitySet != null) { segments.Add(new EntitySetSegmentTemplate(entitySet)); } else { segments.Add(new SingletonSegmentTemplate(navigationSource as IEdmSingleton)); } if (hasKey) { segments.Add(KeySegmentTemplate.CreateKeySegment(entityType, navigationSource)); } if (declared != null) { // It should be always single type if (entityType != declaringEntityType) { segments.Add(new CastSegmentTemplate(declaringEntityType, entityType, navigationSource)); } } IEdmNavigationSource targetNavigationSource = navigationSource.FindNavigationTarget(navigationProperty, segments, out _); segments.Add(new NavigationSegmentTemplate(navigationProperty, targetNavigationSource)); if (dollarCount) { segments.Add(CountSegmentTemplate.Instance); } ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(httpMethod.NormalizeHttpMethod(), context.Prefix, context.Model, template, context.RouteOptions); Log.AddedODataSelector(_logger, action, template); }
/// <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)); }
/// <summary> /// /// </summary> /// <param name="context"></param> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } ActionModel action = context.Action; ODataRouteAttribute routeAttr = action.GetAttribute <ODataRouteAttribute>(); if (routeAttr == null) { return(false); } string prefix = context.Prefix; IEdmModel model = context.Model; string routeTemplate = ""; ODataRoutePrefixAttribute prefixAttr = action.Controller.GetAttribute <ODataRoutePrefixAttribute>(); if (prefixAttr != null) { routeTemplate = prefixAttr.Prefix + "/"; } routeTemplate += routeAttr.PathTemplate; SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null); if (selectorModel == null) { selectorModel = new SelectorModel(); action.Selectors.Add(selectorModel); } string templateStr = string.IsNullOrEmpty(prefix) ? routeTemplate : $"{prefix}/{routeTemplate}"; selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr) { Name = templateStr }); selectorModel.EndpointMetadata.Add(new ODataEndpointMetadata(prefix, model, templateStr)); return(true); }
/// <inheritdoc /> public override bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } IEdmNavigationSource navigationSource = context.NavigationSource; IEdmEntityType entityType = navigationSource.EntityType(); // function should have the [HttpGet] if (!context.Action.Attributes.Any(a => a is HttpGetAttribute)) { return(false); } ProcessOperations(context, entityType, navigationSource); return(false); }
/// <summary> /// /// </summary> /// <param name="context"></param> public bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } ActionModel action = context.Action; if (action.Controller.ControllerName != "ODataOperationImport") { return(false); } IEdmModel model = context.Model; // By convention, we use the operation name as the action name in the controller string actionMethodName = action.ActionMethod.Name; var edmOperationImports = model.EntityContainer.FindOperationImports(actionMethodName); foreach (var edmOperationImport in edmOperationImports) { IEdmEntitySetBase targetSet = null; edmOperationImport.TryGetStaticEntitySet(model, out targetSet); if (edmOperationImport.IsActionImport()) { ODataPathTemplate template = new ODataPathTemplate(new ActionImportSegmentTemplate((IEdmActionImport)edmOperationImport)); action.AddSelector(context.Prefix, context.Model, template); } else { IEdmFunctionImport functionImport = (IEdmFunctionImport)edmOperationImport; ODataPathTemplate template = new ODataPathTemplate(new FunctionImportSegmentTemplate(functionImport)); action.AddSelector(context.Prefix, context.Model, template); } } // in OData operationImport routing convention, all action are processed by default // even it's not a really edm operation import call. return(true); }
/// <summary> /// /// </summary> /// <param name="context"></param> /// <returns></returns> public virtual bool AppliesToController(ODataControllerActionContext context) { //if (model == null) //{ // throw new ArgumentNullException(nameof(model)); //} //if (controller == null) //{ // throw new ArgumentNullException(nameof(controller)); //} //string controllerName = controller.ControllerName; //NavigationSource = model.EntityContainer?.FindEntitySet(controllerName); //// Cached the singleton, because we call this method first, then AppliesToAction //// FindSingleton maybe time consuming. //return NavigationSource != null; return(true); }
/// <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); }
private static bool IsSupportedActionName(ODataControllerActionContext context, string actionName, string singletonName, out string httpMethod) { StringComparison actionNameComparison = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; if (actionName.Equals("Get", actionNameComparison) || actionName.Equals($"Get{singletonName}", actionNameComparison)) { httpMethod = "Get"; return(true); } else if (actionName.Equals("Put", actionNameComparison) || actionName.Equals($"Put{singletonName}", actionNameComparison)) { httpMethod = "Put"; return(true); } else if (actionName.Equals("Patch", actionNameComparison) || actionName.Equals($"Patch{singletonName}", actionNameComparison)) { httpMethod = "Patch"; return(true); } httpMethod = ""; return(false); }
/// <summary> /// /// </summary> /// <param name="context"></param> /// <returns></returns> public virtual bool AppliesToController(ODataControllerActionContext context) { // Apply to all controllers return(true); }
/// <inheritdoc /> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } ActionModel action = context.Action; IEdmModel model = context.Model; // By convention, we use the operation import name as the action name in the controller string actionMethodName = action.ActionName; var edmOperationImports = model.ResolveOperationImports(actionMethodName, enableCaseInsensitive: true); if (!edmOperationImports.Any()) { return(true); } (var actionImports, var functionImports) = edmOperationImports.SplitOperationImports(); // It's not allowed to have an action import and function import with the same name. if (actionImports.Count > 0 && functionImports.Count > 0) { throw new ODataException(Error.Format(SRResources.OperationMustBeUniqueInEntitySetContainer, actionMethodName)); } else if (actionImports.Count > 0 && context.Action.Attributes.Any(a => a is HttpPostAttribute)) { if (actionImports.Count != 1) { throw new ODataException(Error.Format(SRResources.MultipleActionImportFound, actionMethodName)); } IEdmActionImport actionImport = actionImports[0]; IEdmEntitySetBase targetEntitySet; actionImport.TryGetStaticEntitySet(model, out targetEntitySet); // TODO: // 1. shall we check the [HttpPost] attribute, or does the ASP.NET Core have the default? // 2) shall we check the action has "ODataActionParameters" parameter type? ODataPathTemplate template = new ODataPathTemplate(new ActionImportSegmentTemplate(actionImport, targetEntitySet)); action.AddSelector("Post", context.Prefix, context.Model, template, context.Options?.RouteOptions); return(true); } else if (functionImports.Count > 0 && context.Action.Attributes.Any(a => a is HttpGetAttribute)) { IEdmFunctionImport functionImport = FindFunctionImport(functionImports, action); if (functionImport == null) { return(false); } IEdmEntitySetBase targetSet; functionImport.TryGetStaticEntitySet(model, out targetSet); // TODO: // 1) shall we check the [HttpGet] attribute, or does the ASP.NET Core have the default? ODataPathTemplate template = new ODataPathTemplate(new FunctionImportSegmentTemplate(functionImport, targetSet)); action.AddSelector("Get", context.Prefix, context.Model, template, context.Options?.RouteOptions); return(true); } else { // doesn't find an operation, return true means to skip the remaining conventions. return(false); } }
/// <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); }