/// <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); }
/// <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); }
/// <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); }
/// <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); }
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); }
/// <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); }
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); }
public void AddSelector_ThrowsArgumentNull_ForInputParameter() { // Arrange & Act & Assert ActionModel action = null; ExceptionAssert.ThrowsArgumentNull(() => action.AddSelector(null, null, null, null), "action"); // Arrange & Act & Assert MethodInfo methodInfo = typeof(TestController).GetMethod("Get"); action = methodInfo.BuildActionModel(); ExceptionAssert.ThrowsArgumentNullOrEmpty(() => action.AddSelector(null, null, null, null), "httpMethods"); // Arrange & Act & Assert string httpMethods = "get"; ExceptionAssert.ThrowsArgumentNull(() => action.AddSelector(httpMethods, null, null, null), "model"); // Arrange & Act & Assert IEdmModel model = new Mock <IEdmModel>().Object; ExceptionAssert.ThrowsArgumentNull(() => action.AddSelector(httpMethods, null, model, null), "path"); }
private static void AddSelector(IEdmEntitySet entitySet, IEdmEntityType entityType, IEdmStructuredType castType, string prefix, IEdmModel model, ActionModel action, string httpMethod) { IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate> { new EntitySetSegmentTemplate(entitySet), KeySegmentTemplate.CreateKeySegment(entityType, entitySet) }; // If we have the type cast if (castType != null) { if (castType == entityType) { // If cast type is the entity type of the entity set. // we support two templates // ~/Customers({key}) action.AddSelector(httpMethod, prefix, model, new ODataPathTemplate(segments)); // ~/Customers({key})/Ns.Customer segments.Add(new CastSegmentTemplate(castType, entityType, entitySet)); action.AddSelector(httpMethod, prefix, model, new ODataPathTemplate(segments)); } else { // ~/Customers({key})/Ns.VipCustomer segments.Add(new CastSegmentTemplate(castType, entityType, entitySet)); action.AddSelector(httpMethod, prefix, model, new ODataPathTemplate(segments)); } } else { // ~/Customers({key}) action.AddSelector(httpMethod, prefix, model, new ODataPathTemplate(segments)); } }
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); }
public void AddSelector_AddsCors_ForActionsWithCorsAttribute(Type controllerType, string actionName, bool expectedCorsSetting) { // Arrange IEdmModel model = new Mock <IEdmModel>().Object; MethodInfo methodInfo = controllerType.GetMethod(actionName); ActionModel action = methodInfo.BuildActionModel(); action.Controller = ControllerModelHelpers.BuildControllerModel(controllerType); // Act action.AddSelector("Get", string.Empty, model, new ODataPathTemplate(CountSegmentTemplate.Instance)); // Assert SelectorModel newSelector = action.Selectors.FirstOrDefault(); Assert.NotNull(newSelector); HttpMethodMetadata httpMethodMetadata = newSelector.EndpointMetadata.OfType <HttpMethodMetadata>().FirstOrDefault(); Assert.NotNull(httpMethodMetadata); Assert.Equal(httpMethodMetadata.AcceptCorsPreflight, expectedCorsSetting); }
/// <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); }
private static bool ProcessEntitySetAction(string actionName, IEdmEntitySet entitySet, IEdmStructuredType castType, ODataControllerActionContext context, ActionModel action) { if (actionName == "Get" || actionName == $"Get{entitySet.Name}") { IEdmCollectionType castCollectionType = null; if (castType != null) { castCollectionType = castType.ToCollection(true); } IEdmCollectionType entityCollectionType = entitySet.EntityType().ToCollection(true); // GET ~/Customers or GET ~/Customers/Ns.VipCustomer IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate> { new EntitySetSegmentTemplate(entitySet) }; if (castType != null) { segments.Add(new CastSegmentTemplate(castCollectionType, entityCollectionType, entitySet)); } ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector("Get", context.Prefix, context.Model, template, context.Options?.RouteOptions); // GET ~/Customers/$count or GET ~/Customers/Ns.VipCustomer/$count segments = new List <ODataSegmentTemplate> { new EntitySetSegmentTemplate(entitySet) }; if (castType != null) { segments.Add(new CastSegmentTemplate(castCollectionType, entityCollectionType, entitySet)); } segments.Add(CountSegmentTemplate.Instance); template = new ODataPathTemplate(segments); action.AddSelector("Get", context.Prefix, context.Model, template, context.Options?.RouteOptions); return(true); } else if (actionName == "Post" || actionName == $"Post{entitySet.EntityType().Name}") { // POST ~/Customers IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate> { new EntitySetSegmentTemplate(entitySet) }; if (castType != null) { IEdmCollectionType castCollectionType = castType.ToCollection(true); IEdmCollectionType entityCollectionType = entitySet.EntityType().ToCollection(true); segments.Add(new CastSegmentTemplate(castCollectionType, entityCollectionType, entitySet)); } ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector("Post", context.Prefix, context.Model, template, context.Options?.RouteOptions); return(true); } else if (actionName == "Patch" || actionName == $"Patch{entitySet.Name}") { // PATCH ~/Patch , ~/PatchCustomers IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate> { new EntitySetSegmentTemplate(entitySet) }; if (castType != null) { IEdmCollectionType castCollectionType = castType.ToCollection(true); IEdmCollectionType entityCollectionType = entitySet.EntityType().ToCollection(true); segments.Add(new CastSegmentTemplate(castCollectionType, entityCollectionType, entitySet)); } ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector("Patch", context.Prefix, context.Model, template, context.Options?.RouteOptions); return(true); } return(false); }
/// <summary> /// /// </summary> /// <param name="context"></param> public bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } // use the cached Debug.Assert(context.Singleton != null); Debug.Assert(context.Action != null); ActionModel action = context.Action; string singletonName = context.Singleton.Name; string prefix = context.Prefix; IEdmModel model = context.Model; string actionMethodName = action.ActionMethod.Name; if (IsSupportedActionName(actionMethodName, singletonName)) { ODataPathTemplate template = new ODataPathTemplate(new SingletonSegmentTemplate(context.Singleton)); action.AddSelector(context.Prefix, context.Model, template); // processed return(true); } // type cast // Get{SingletonName}From{EntityTypeName} or GetFrom{EntityTypeName} int index = actionMethodName.IndexOf("From", StringComparison.Ordinal); if (index == -1) { return(false); } string actionPrefix = actionMethodName.Substring(0, index); if (IsSupportedActionName(actionPrefix, singletonName)) { IEdmEntityType entityType = context.Singleton.EntityType(); string castTypeName = actionMethodName.Substring(index + 4); // Shall we cast to base type and the type itself? I think yes. IEdmEntityType baseType = entityType; while (baseType != null) { if (baseType.Name == castTypeName) { ODataPathTemplate template = new ODataPathTemplate(new SingletonSegmentTemplate(context.Singleton), new CastSegmentTemplate(baseType)); action.AddSelector(context.Prefix, context.Model, template); return(true); } baseType = baseType.BaseEntityType(); } // shall we cast to derived type IEdmEntityType castType = model.FindAllDerivedTypes(entityType).OfType <IEdmEntityType>().FirstOrDefault(c => c.Name == castTypeName); if (castType != null) { ODataPathTemplate template = new ODataPathTemplate(new SingletonSegmentTemplate(context.Singleton), new CastSegmentTemplate(castType)); action.AddSelector(context.Prefix, context.Model, template); return(true); } } return(false); }
/// <inheritdoc /> public bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } Debug.Assert(context.Action != null); ActionModel action = context.Action; string actionMethodName = action.ActionMethod.Name; // 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); } // Action parameter should have a (string navigationProperty) parameter if (!action.HasParameter <string>("navigationProperty")) { return(false); } IEdmNavigationSource navigationSource; IEdmEntityType entityType; if (context.EntitySet != null) { entityType = context.EntitySet.EntityType(); navigationSource = context.EntitySet; } else { entityType = context.Singleton.EntityType(); navigationSource = context.Singleton; } // 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); } } // 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(new KeySegmentTemplate(entityType)); } else { segments.Add(new SingletonSegmentTemplate(context.Singleton)); } if (entityType != declaringType) { segments.Add(new CastSegmentTemplate(declaringType, entityType, navigationSource)); } if (navigationProperty != null) { segments.Add(new NavigationSegmentTemplate(navigationProperty)); } else { //TODO: Add the navigation template segment template, // Or add the template for all navigation properties? return(false); } IEdmEntityType navigationPropertyType = navigationProperty.Type.GetElementTypeOrSelf().AsEntity().EntityDefinition(); bool hasNavigationPropertyKeyParameter = action.HasODataKeyParameter(navigationPropertyType, "relatedKey"); if (hasNavigationPropertyKeyParameter) { segments.Add(new KeySegmentTemplate(navigationPropertyType, "relatedKey")); } IEdmNavigationSource targetNavigationSource = navigationSource.FindNavigationTarget(navigationProperty, segments, out _); segments.Add(new RefSegmentTemplate(navigationProperty, targetNavigationSource)); // TODO: support key as segment? ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(httpMethod, context.Prefix, context.Model, template); // processed 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); }
/// <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; IEdmModel model = context.Model; string prefix = context.Prefix; IEdmEntityType entityType = navigationSource.EntityType(); bool hasKeyParameter = HasKeyParameter(entityType, action); // found int keyNumber = entityType.Key().Count(); IEdmType bindType = entityType; if (!hasKeyParameter) { // bond to collection bindType = new EdmCollectionType(new EdmEntityTypeReference(entityType, true)); keyNumber = 0; } string actionName = action.ActionMethod.Name; var operations = model.FindBoundOperations(bindType).Where(p => p.Name == actionName); var actions = operations.OfType <IEdmAction>().ToList(); if (actions.Count == 1) // action overload on binding type, only one action overload on the same binding type { if (action.Parameters.Any(p => p.ParameterType == typeof(ODataActionParameters))) { // 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)); } segments.Add(new ActionSegmentTemplate(actions[0], false)); ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(prefix, model, template); return(true); } } var functions = operations.OfType <IEdmFunction>().ToList(); IEdmFunction function = FindMatchFunction(keyNumber, functions, action); if (function != null) { 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)); } segments.Add(new FunctionSegmentTemplate(function, false)); ODataPathTemplate template = new ODataPathTemplate(segments); action.AddSelector(prefix, model, template); return(true); } // in OData operationImport routing convention, all action are processed by default // even it's not a really edm operation import call. return(false); }