/// <summary> /// If the source node is not of the specified type, then we check if type promotion is possible and inject a convert node. /// If the source node is the same type as the target type (or if the target type is null), we just return the source node as is. /// </summary> /// <param name="source">The source node to apply the convertion to.</param> /// <param name="targetTypeReference">The target primitive type. May be null - this method will do nothing in that case.</param> /// <returns>The converted query node, or the original source node unchanged.</returns> internal static SingleValueNode ConvertToTypeIfNeeded(SingleValueNode source, IEdmTypeReference targetTypeReference) { Debug.Assert(source != null, "source != null"); if (targetTypeReference == null) { return(source); } if (source.TypeReference != null) { if (source.TypeReference.IsEquivalentTo(targetTypeReference)) { // For source is type definition, if source's underlying type == target type. // We create a conversion node from source to its underlying type (target type) // so that the service can convert value of source clr type to underlying clr type. if (source.TypeReference.IsTypeDefinition()) { return(new ConvertNode(source, targetTypeReference)); } return(source); } if (!TypePromotionUtils.CanConvertTo(source, source.TypeReference, targetTypeReference)) { throw new ODataException(ODataErrorStrings.MetadataBinder_CannotConvertToType(source.TypeReference.ODataFullName(), targetTypeReference.ODataFullName())); } else { ConstantNode constantNode = source as ConstantNode; if (source.TypeReference.IsEnum() && constantNode != null) { return(new ConstantNode(constantNode.Value, ODataUriUtils.ConvertToUriLiteral(constantNode.Value, ODataVersion.V4), targetTypeReference)); } object primitiveValue; if (MetadataUtilsCommon.TryGetConstantNodePrimitiveLDMF(source, out primitiveValue) && (primitiveValue != null)) { // L F D M types : directly create a ConvertNode. // 1. NodeToExpressionTranslator.cs won't allow implicitly converting single/double to decimal, which should be done here at Node tree level. // 2. And prevent losing precision in float -> double, e.g. (double)1.234f => 1.2339999675750732d not 1.234d object primitiveValue2 = ODataUriConversionUtils.CoerceNumericType(primitiveValue, targetTypeReference.AsPrimitive().Definition as IEdmPrimitiveType); if (string.IsNullOrEmpty(constantNode.LiteralText)) { return(new ConstantNode(primitiveValue2)); } return(new ConstantNode(primitiveValue2, constantNode.LiteralText)); } else { // other type conversion : ConvertNode return(new ConvertNode(source, targetTypeReference)); } } } else { // If the source doesn't have a type (possibly an open property), then it's possible to convert it // cause we don't know for sure. return(new ConvertNode(source, targetTypeReference)); } }
public HttpResponseMessage Post(AssetValue val) { if (val == null) { return(Request.CreateResponse(HttpStatusCode.NotFound)); } _rep.Create(val); var response = Request.CreateResponse(HttpStatusCode.Created, val); var odataPath = Request.GetODataPath(); if (odataPath == null) { throw new InvalidOperationException("There is no ODataPath in the request."); } var entitySetPathSegment = odataPath.Segments.FirstOrDefault() as EntitySetPathSegment; if (entitySetPathSegment == null) { throw new InvalidOperationException("ODataPath doesn't start with EntitySetPathSegment."); } response.Headers.Location = new Uri(Url.ODataLink(entitySetPathSegment, new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(val.Id, ODataVersion.V3)))); //response.Headers.Location = new Uri(Url.ODataLink(new EntitySetPathSegment("Governors"), new KeyValuePathSegment(gov.Id.ToString()))); return(response); }
public static HttpResponseMessage PostResponse <TEntity, TKey>(ApiController controller, TEntity createdEntity, TKey entityKey) { HttpResponseMessage response = null; HttpRequestMessage request = controller.Request; if (RequestPrefersReturnNoContent(request)) { response = request.CreateResponse(HttpStatusCode.NoContent); response.Headers.Add(PreferenceAppliedHeaderName, ReturnNoContentHeaderValue); } else { response = request.CreateResponse(HttpStatusCode.Created, createdEntity); } ODataPath odataPath = request.ODataProperties().Path; if (odataPath == null) { throw Error.InvalidOperation(SRResources.LocationHeaderMissingODataPath); } EntitySetPathSegment entitySetSegment = odataPath.Segments.FirstOrDefault() as EntitySetPathSegment; if (entitySetSegment == null) { throw Error.InvalidOperation(SRResources.LocationHeaderDoesNotStartWithEntitySet); } UrlHelper urlHelper = controller.Url ?? new UrlHelper(request); response.Headers.Location = new Uri(urlHelper.CreateODataLink(entitySetSegment, new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(entityKey, ODataVersion.V3)))); return(response); }
/// <summary> /// Generates a string to be used as the skip token value within the next link. /// </summary> /// <param name="lastMember"> Object based on which SkipToken value will be generated.</param> /// <param name="model">The edm model.</param> /// <param name="orderByNodes">List of orderByNodes used to generate the skiptoken value.</param> /// <returns>Value for the skiptoken to be used in the next link.</returns> private static string GenerateSkipTokenValue(Object lastMember, IEdmModel model, IList <OrderByNode> orderByNodes) { if (lastMember == null) { return(String.Empty); } IEnumerable <IEdmProperty> propertiesForSkipToken = GetPropertiesForSkipToken(lastMember, model, orderByNodes); StringBuilder skipTokenBuilder = new StringBuilder(String.Empty); if (propertiesForSkipToken == null) { return(skipTokenBuilder.ToString()); } int count = 0; string uriLiteral; object value; int lastIndex = propertiesForSkipToken.Count() - 1; IEdmStructuredObject obj = lastMember as IEdmStructuredObject; foreach (IEdmProperty edmProperty in propertiesForSkipToken) { bool islast = count == lastIndex; string clrPropertyName = EdmLibHelpers.GetClrPropertyName(edmProperty, model); if (obj != null) { obj.TryGetPropertyValue(clrPropertyName, out value); } else { value = lastMember.GetType().GetProperty(clrPropertyName).GetValue(lastMember); } if (value == null) { uriLiteral = ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V401); } else if (edmProperty.Type.IsEnum()) { ODataEnumValue enumValue = new ODataEnumValue(value.ToString(), value.GetType().FullName); uriLiteral = ODataUriUtils.ConvertToUriLiteral(enumValue, ODataVersion.V401, model); } else if (edmProperty.Type.IsDateTimeOffset() && value is DateTime) { var dateTime = (DateTime)value; var dateTimeOffsetValue = TimeZoneInfoHelper.ConvertToDateTimeOffset(dateTime); uriLiteral = ODataUriUtils.ConvertToUriLiteral(dateTimeOffsetValue, ODataVersion.V401, model); } else { uriLiteral = ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V401, model); } var encodedUriLiteral = WebUtility.UrlEncode(uriLiteral); skipTokenBuilder.Append(edmProperty.Name).Append(propertyDelimiter).Append(encodedUriLiteral).Append(islast ? String.Empty : CommaDelimiter.ToString()); count++; } return(skipTokenBuilder.ToString()); }
public void TestLongConvertFromUriLiteral(string value) { object longNumber = ODataUriUtils.ConvertFromUriLiteral(value, ODataVersion.V4); Assert.True(longNumber is long); }
public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup <string, HttpActionDescriptor> actionMap) { if (odataPath.PathTemplate == "~/entityset/key") { KeyValuePathSegment segment = (KeyValuePathSegment)odataPath.Segments.Last(); var keyValues = segment.Value.Split(','); if (keyValues.Length <= 1) { return(null); // let other conventions to process } foreach (var item in keyValues) { var keyValue = item.Split('='); if (keyValue.Length != 2) { continue; } string key = keyValue[0].Trim(); string valueString = keyValue[1].Trim(); object value = ODataUriUtils.ConvertFromUriLiteral(valueString, ODataVersion.V4); controllerContext.RouteData.Values.Add("key" + key, value); // use a prefix } /* * IEdmModel model = controllerContext.Request.ODataProperties().Model; * * string a = controllerContext.Request.GetUrlHelper().CreateODataLink(odataPath.Segments); * //Uri b = new Uri(); * * ODataUriParser parser = new ODataUriParser(model, new Uri(a, UriKind.Absolute)); * Sematic.ODataPath path = parser.ParsePath(); * Sematic.KeySegment keySegment = path.LastSegment as Sematic.KeySegment; * foreach (var key in keySegment.Keys) * { * controllerContext.RouteData.Values.Add("key" + key.Key, key.Value); * }*/ HttpMethod httpMethod = controllerContext.Request.Method; string httpMethodName; switch (httpMethod.ToString().ToUpperInvariant()) { case "GET": httpMethodName = "Get"; break; case "PUT": httpMethodName = "Put"; break; case "PATCH": case "MERGE": httpMethodName = "Patch"; break; case "DELETE": httpMethodName = "Delete"; break; default: return(null); } IEdmEntityType entityType = odataPath.EdmType as IEdmEntityType; string actionName = FindMatchingAction(actionMap, httpMethodName + entityType.Name, httpMethodName); return(actionName); } return(null); }
/// <inheritdoc /> public override bool TryTranslate(ODataTemplateTranslateContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } RouteValueDictionary routeValues = context.RouteValues; RouteValueDictionary updateValues = context.UpdatedValues; IDictionary <string, object> keysValues = new Dictionary <string, object>(); foreach (var key in KeyMappings) { string keyName = key.Key; string templateName = key.Value; if (routeValues.TryGetValue(templateName, out object rawValue)) { IEdmProperty keyProperty = KeyProperties.FirstOrDefault(k => k.Key == keyName).Value; Contract.Assert(keyProperty != null); IEdmTypeReference edmType = keyProperty.Type; string strValue = rawValue as string; string newStrValue = context.GetParameterAliasOrSelf(strValue); if (newStrValue != strValue) { updateValues[templateName] = newStrValue; strValue = newStrValue; } // If it's key as segment and the key type is Edm.String, we support non-single quoted string. // Since we can't identify key as segment and key in parenthesis easy so far, // we use the key literal with "/" to test in the whole route template. // Why we can't create two key segment templates, one reason is that in attribute routing template, // we can't identify key as segment or key in parenthesis also. if (edmType.IsString() && context.IsPartOfRouteTemplate($"/{_keyLiteral}")) { if (!strValue.StartsWith('\'') && !strValue.EndsWith('\'')) { strValue = $"'{strValue}'"; // prefix and suffix single quote } } object newValue; try { newValue = ODataUriUtils.ConvertFromUriLiteral(strValue, ODataVersion.V4, context.Model, edmType); } catch (ODataException ex) { string message = Error.Format(SRResources.InvalidKeyInUriFound, strValue, edmType.FullName()); throw new ODataException(message, ex); } // for non FromODataUri, so update it, for example, remove the single quote for string value. updateValues[templateName] = newValue; // For FromODataUri, let's refactor it later. string prefixName = ODataParameterValue.ParameterValuePrefix + templateName; updateValues[prefixName] = new ODataParameterValue(newValue, edmType); keysValues[keyName] = newValue; } } context.Segments.Add(new KeySegment(keysValues, EntityType, NavigationSource)); return(true); }
public void TestDoubleConvertFromUriLiteral(string value) { object doubleNumber = ODataUriUtils.ConvertFromUriLiteral(value, ODataVersion.V4); Assert.True(doubleNumber is double); }
public void ODataModelBinderProvider_Throws(object value, string action) { string url = String.Format("http://localhost/ODataModelBinderProviderThrowsTest/{0}({1})", action, Uri.EscapeDataString(ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V3))); HttpResponseMessage response = _client.GetAsync(url).Result; Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); }
internal static object ConvertTo(string valueString, Type type, TimeZoneInfo timeZone, IEdmModel edmModel = null) { if (valueString == null) { return(null); } if (TypeHelper.IsNullable(type) && String.Equals(valueString, "null", StringComparison.Ordinal)) { return(null); } // TODO 1668: ODL beta1's ODataUriUtils.ConvertFromUriLiteral does not support converting uri literal // to ODataEnumValue, but beta1's ODataUriUtils.ConvertToUriLiteral supports converting ODataEnumValue // to uri literal. if (TypeHelper.IsEnum(type)) { string[] values = valueString.Split(new[] { '\'' }, StringSplitOptions.None); if (values.Length == 3 && String.IsNullOrEmpty(values[2])) { // Remove the type name if the enum value is a fully qualified literal. valueString = values[1]; } Type enumType = TypeHelper.GetUnderlyingTypeOrSelf(type); object[] parameters = new[] { valueString, Enum.ToObject(enumType, 0) }; bool isSuccessful = (bool)EnumTryParseMethod.MakeGenericMethod(enumType).Invoke(null, parameters); if (!isSuccessful) { throw Error.InvalidOperation(SRResources.ModelBinderUtil_ValueCannotBeEnum, valueString, type.Name); } return(parameters[1]); } // The logic of "public static object ConvertFromUriLiteral(string value, ODataVersion version);" treats // the date value string (for example: 2015-01-02) as DateTimeOffset literal, and return a DateTimeOffset // object. However, the logic of // "object ConvertFromUriLiteral(string value, ODataVersion version, IEdmModel model, IEdmTypeReference typeReference);" // can return the correct Date object. if (type == typeof(Date) || type == typeof(Date?)) { IEdmModel model = edmModel ?? EdmCoreModel.Instance; IEdmPrimitiveTypeReference dateTypeReference = model.GetEdmPrimitiveTypeReference(type); return(ODataUriUtils.ConvertFromUriLiteral(valueString, ODataVersion.V4, model, dateTypeReference)); } object value; try { value = ODataUriUtils.ConvertFromUriLiteral(valueString, ODataVersion.V4); } catch { if (type == typeof(string)) { return(valueString); } throw; } bool isNonStandardEdmPrimitive; edmModel.IsNonstandardEdmPrimitive(type, out isNonStandardEdmPrimitive); if (isNonStandardEdmPrimitive) { // shall we get the timezone? return(EdmPrimitiveHelper.ConvertPrimitiveValue(value, type, timeZone)); } else { type = Nullable.GetUnderlyingType(type) ?? type; return(System.Convert.ChangeType(value, type, CultureInfo.InvariantCulture)); } }
public void ODataModelBinderProvider_Works(object value, string action) { string url = String.Format("http://localhost/ODataModelBinderProviderTest/{0}({1})", action, Uri.EscapeDataString(ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V3))); HttpResponseMessage response = _client.GetAsync(url).Result; response.EnsureSuccessStatusCode(); Assert.Equal( value, response.Content.ReadAsAsync(value.GetType(), _configuration.Formatters).Result); }
public static string GetUriRepresentationForDateTime(DateTime dateTime, TimeZoneInfo timeZoneInfo) { object value = ODataPrimitiveSerializer.ConvertUnsupportedDateTime(dateTime, timeZoneInfo); return(ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V4)); }
private static IEdmModel GetEdmModel() { ODataModelBuilder modelBuilder = new ODataConventionModelBuilder(); modelBuilder.ContainerName = "WebApiCtx"; //modelBuilder.EntitySet<TestDataObj>("Values"); var holidays = modelBuilder.EntitySet <Model.WorkCalendar>("Holidays"); holidays.EntityType.Property(h => h.Comment).IsOptional(); holidays.EntityType.Ignore(h => h.DayType); holidays.EntityType.Ignore(h => h.BankDate); holidays.EntityType.Ignore(h => h.BankDate_Key); //var companies = modelBuilder.EntitySet<Model.Company>("Companies"); var govs = modelBuilder.EntitySet <Model.Governor>("Governors"); govs.HasEditLink( entityContext => entityContext.Url.ODataLink( new EntitySetPathSegment(entityContext.EntitySet.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(entityContext.EntityInstance.Id, ODataVersion.V3))), followsConventions: true); var c = modelBuilder.ComplexType <Model.Company>(); c.Property(o => o.lawFormValue); c.Ignore(o => o.FullName); c.Ignore(o => o.LawForm); govs.EntityType.ComplexProperty(g => g.Company).IsRequired(); var assets = modelBuilder.EntitySet <Model.AssetValue>("Assets"); assets.HasEditLink( entityContext => entityContext.Url.ODataLink( new EntitySetPathSegment(entityContext.EntitySet.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(entityContext.EntityInstance.Id, ODataVersion.V3))), followsConventions: true); assets.HasRequiredBinding(a => a.Governor, govs); assets.EntityType.Property(a => a.InsuranceTypeValue).IsRequired(); //assets.EntityType.ComplexProperty(a => a.Governor); assets.EntityType.Ignore(a => a.InsuranceType); assets.EntityType.Ignore(a => a.InsuranceTypeString); assets.HasNavigationPropertiesLink( assets.EntityType.NavigationProperties, (entityContext, navigationProperty) => new Uri(entityContext.Url.ODataLink( new EntitySetPathSegment(entityContext.EntitySet.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(entityContext.EntityInstance.Id, ODataVersion.V3)), new NavigationPathSegment(navigationProperty.Name))), followsConventions: true); govs.HasNavigationPropertiesLink( govs.EntityType.NavigationProperties, (entityContext, navigationProperty) => new Uri(entityContext.Url.ODataLink( new EntitySetPathSegment(entityContext.EntitySet.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(entityContext.EntityInstance.Id, ODataVersion.V3)), new NavigationPathSegment(navigationProperty.Name))), followsConventions: true); var actionRep = assets.EntityType.Collection.Action("Report"); actionRep.Parameter <DateTime>("DateBegin"); actionRep.Parameter <DateTime>("DateEnd"); actionRep.Parameter <byte>("InsuranceType"); actionRep.Parameter <Guid?>("GovernorId"); actionRep.Returns <string>(); var actionBatch = assets.EntityType.Collection.Action("CreateBatch"); actionBatch.CollectionParameter <string>("Values"); actionBatch.Returns <bool>(); IEdmModel model = modelBuilder.GetEdmModel(); return(model); }
/// <summary> /// This is temp work around for $filter $orderby parameter expression which contains complex or collection /// like "Fully.Qualified.Namespace.CanMoveToAddresses(addresses=[{\"Street\":\"NE 24th St.\",\"City\":\"Redmond\"},{\"Street\":\"Pine St.\",\"City\":\"Seattle\"}])"; /// TODO: $filter $orderby parameter expression which contains nested complex or collection should NOT be supported in this way /// but should be parsed into token tree, and binded to node tree: parsedParameters.Select(p => this.bindMethod(p)); /// </summary> /// <param name="model">The model.</param> /// <param name="operation">IEdmFunction or IEdmOperation</param> /// <param name="parameterTokens">The tokens to bind.</param> /// <param name="enableCaseInsensitive">Whether to enable case-insensitive when resolving parameter name.</param> /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param> /// <returns>The FunctionParameterTokens with complex or collection values converted from string like "{...}", or "[..,..,..]".</returns> private static ICollection <FunctionParameterToken> HandleComplexOrCollectionParameterValueIfExists(IEdmModel model, IEdmOperation operation, ICollection <FunctionParameterToken> parameterTokens, bool enableCaseInsensitive, bool enableUriTemplateParsing = false) { ICollection <FunctionParameterToken> partiallyParsedParametersWithComplexOrCollection = new Collection <FunctionParameterToken>(); foreach (FunctionParameterToken paraToken in parameterTokens) { FunctionParameterToken funcParaToken; IEdmOperationParameter functionParameter = operation.FindParameter(paraToken.ParameterName); if (enableCaseInsensitive && functionParameter == null) { functionParameter = ODataUriResolver.ResolveOperationParameterNameCaseInsensitive(operation, paraToken.ParameterName); // The functionParameter can not be null here, else this method won't be called. funcParaToken = new FunctionParameterToken(functionParameter.Name, paraToken.ValueToken); } else { funcParaToken = paraToken; } FunctionParameterAliasToken aliasToken = funcParaToken.ValueToken as FunctionParameterAliasToken; if (aliasToken != null) { aliasToken.ExpectedParameterType = functionParameter.Type; } LiteralToken valueToken = funcParaToken.ValueToken as LiteralToken; string valueStr = null; if (valueToken != null && (valueStr = valueToken.Value as string) != null && !string.IsNullOrEmpty(valueToken.OriginalText)) { ExpressionLexer lexer = new ExpressionLexer(valueToken.OriginalText, true /*moveToFirstToken*/, false /*useSemicolonDelimiter*/, true /*parsingFunctionParameters*/); if (lexer.CurrentToken.Kind == ExpressionTokenKind.BracketedExpression || lexer.CurrentToken.Kind == ExpressionTokenKind.BracedExpression) { object result; UriTemplateExpression expression; if (enableUriTemplateParsing && UriTemplateParser.TryParseLiteral(lexer.CurrentToken.Text, functionParameter.Type, out expression)) { result = expression; } else if (!functionParameter.Type.IsStructured() && !functionParameter.Type.IsStructuredCollectionType()) { // ExpressionTokenKind.BracketedExpression means text like [1,2] // so now try convert it to collection type value: result = ODataUriUtils.ConvertFromUriLiteral(valueStr, ODataVersion.V4, model, functionParameter.Type); } else { // For complex & colleciton of complex directly return the raw string. partiallyParsedParametersWithComplexOrCollection.Add(funcParaToken); continue; } LiteralToken newValueToken = new LiteralToken(result, valueToken.OriginalText); FunctionParameterToken newFuncParaToken = new FunctionParameterToken(funcParaToken.ParameterName, newValueToken); partiallyParsedParametersWithComplexOrCollection.Add(newFuncParaToken); continue; } } partiallyParsedParametersWithComplexOrCollection.Add(funcParaToken); } return(partiallyParsedParametersWithComplexOrCollection); }
/// <summary> /// Match the function parameter /// </summary> /// <param name="context">The context.</param> /// <param name="function">The Edm function.</param> /// <param name="parameterMappings">The parameter mapping.</param> /// <returns></returns> public static IList <OperationSegmentParameter> Match(ODataTemplateTranslateContext context, IEdmFunction function, IDictionary <string, string> parameterMappings) { Contract.Assert(context != null); Contract.Assert(function != null); Contract.Assert(parameterMappings != null); RouteValueDictionary routeValues = context.RouteValues; RouteValueDictionary updatedValues = context.UpdatedValues; IList <OperationSegmentParameter> parameters = new List <OperationSegmentParameter>(); foreach (var parameter in parameterMappings) { string parameterName = parameter.Key; string parameterTemp = parameter.Value; IEdmOperationParameter edmParameter = function.Parameters.FirstOrDefault(p => p.Name == parameterName); Contract.Assert(edmParameter != null); // For a parameter mapping like: minSalary={min} // and a request like: ~/MyFunction(minSalary=2) // the routeValue includes the [min=2], so we should use the mapping name to retrieve the value. if (routeValues.TryGetValue(parameterTemp, out object rawValue)) { string strValue = rawValue as string; string newStrValue = context.GetParameterAliasOrSelf(strValue); newStrValue = Uri.UnescapeDataString(newStrValue); if (newStrValue != strValue) { updatedValues[parameterTemp] = newStrValue; strValue = newStrValue; } string originalStrValue = strValue; // for resource or collection resource, this method will return "ODataResourceValue, ..." we should support it. if (edmParameter.Type.IsResourceOrCollectionResource()) { // For FromODataUri string prefixName = ODataParameterValue.ParameterValuePrefix + parameterTemp; updatedValues[prefixName] = new ODataParameterValue(strValue, edmParameter.Type); parameters.Add(new OperationSegmentParameter(parameterName, strValue)); } else { if (edmParameter.Type.IsEnum() && strValue.StartsWith("'", StringComparison.Ordinal) && strValue.EndsWith("'", StringComparison.Ordinal)) { // related implementation at: https://github.com/OData/odata.net/blob/master/src/Microsoft.OData.Core/UriParser/Resolver/StringAsEnumResolver.cs#L131 strValue = edmParameter.Type.FullName() + strValue; } object newValue; try { newValue = ODataUriUtils.ConvertFromUriLiteral(strValue, ODataVersion.V4, context.Model, edmParameter.Type); } catch (ODataException ex) { string message = Error.Format(SRResources.InvalidParameterValueInUriFound, originalStrValue, edmParameter.Type.FullName()); throw new ODataException(message, ex); } // for without FromODataUri, so update it, for example, remove the single quote for string value. updatedValues[parameterTemp] = newValue; // For FromODataUri string prefixName = ODataParameterValue.ParameterValuePrefix + parameterTemp; updatedValues[prefixName] = new ODataParameterValue(newValue, edmParameter.Type); parameters.Add(new OperationSegmentParameter(parameterName, newValue)); } } else { return(null); } } return(parameters); }
public void TestDateConvertFromUriLiteral() { Date dateValue = (Date)ODataUriUtils.ConvertFromUriLiteral("1997-07-01", ODataVersion.V4, HardCodedTestModel.TestModel, EdmCoreModel.Instance.GetDate(false)); dateValue.Should().Be(new Date(1997, 7, 1)); }
/// <summary> /// ConstantExpression visit method /// </summary> /// <param name="c">The ConstantExpression expression to visit</param> /// <returns>The visited ConstantExpression expression </returns> internal override Expression VisitConstant(ConstantExpression c) { if (c.Value == null) { this.builder.Append(UriHelper.NULL); return(c); } // DEVNOTE: // Rather than forcing every other codepath to have the 'Try...' pattern for formatting, // we catch the InvalidOperationException here to change the exception type. // This is exceedingly rare, and not a scenario where performance is meaningful, so the // reduced complexity in all other call sites is worth the extra logic here. string result; BinaryExpression b = this.parent as BinaryExpression; MethodCallExpression m = this.parent as MethodCallExpression; if ((b != null && HasEnumInBinaryExpression(b)) || (m != null && m.Method.Name == "HasFlag")) { c = this.ConvertConstantExpressionForEnum(c); ClientEdmModel model = this.context.Model; IEdmType edmType = model.GetOrCreateEdmType(c.Type.IsEnum() ? c.Type : c.Type.GetGenericArguments()[0]); ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); string typeNameInEdm = this.context.ResolveNameFromTypeInternal(typeAnnotation.ElementType); MemberInfo member = typeAnnotation.ElementType.GetField(c.Value.ToString()); string memberValue = ClientTypeUtil.GetServerDefinedName(member); ODataEnumValue enumValue = new ODataEnumValue(memberValue, typeNameInEdm ?? typeAnnotation.ElementTypeName); result = ODataUriUtils.ConvertToUriLiteral(enumValue, CommonUtil.ConvertToODataVersion(this.uriVersion), null); } else if (m != null && ReflectionUtil.IsSequenceMethod(m.Method, SequenceMethod.Contains)) { StringBuilder listExpr = new StringBuilder(); ODataVersion version = CommonUtil.ConvertToODataVersion(this.uriVersion); foreach (object item in (IEnumerable)c.Value) { if (listExpr.Length != 0) { listExpr.Append(UriHelper.COMMA); } string uriLiteral = ODataUriUtils.ConvertToUriLiteral(item, version); listExpr.Append(uriLiteral); } // Contains cannot be used with an empty static collection if (listExpr.Length == 0) { throw new InvalidOperationException(Strings.ALinq_ContainsNotValidOnEmptyCollection); } listExpr.Insert(0, UriHelper.LEFTPAREN); listExpr.Append(UriHelper.RIGHTPAREN); result = listExpr.ToString(); } else { try { result = LiteralFormatter.ForConstants.Format(c.Value); } catch (InvalidOperationException) { if (this.cantTranslateExpression) { // there's already a problem in the parents. // we should just return here, because caller somewhere up the stack will throw a better exception return(c); } throw new NotSupportedException(Strings.ALinq_CouldNotConvert(c.Value)); } } Debug.Assert(result != null, "result != null"); this.builder.Append(result); return(c); }
public void TestCollectionConvertWithMismatchedBracket() { Action parse = () => ODataUriUtils.ConvertFromUriLiteral("[1,2,3)", ODataVersion.V4, HardCodedTestModel.TestModel, new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetInt32(false)))); parse.Throws <ODataException>(Strings.ExpressionLexer_UnbalancedBracketExpression); }
private string ConvertToEscapedUriValue(string paramName, object value) { Debug.Assert(!string.IsNullOrEmpty(paramName), "!string.IsNullOrEmpty(paramName)"); Object valueInODataFormat = null; // Literal values with single quotes need special escaping due to System.Uri changes in behavior between .NET 4.0 and 4.5. // We need to ensure that our escaped values do not change between those versions, so we need to escape values differently when they could contain single quotes. bool needsSpecialEscaping = false; if (value == null) { needsSpecialEscaping = true; } else { if (value.GetType() == typeof(ODataUriNullValue)) { valueInODataFormat = value; needsSpecialEscaping = true; } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(value.GetType()); Debug.Assert(edmType != null, "edmType != null"); switch (edmType.TypeKind) { case EdmTypeKind.Primitive: valueInODataFormat = value; needsSpecialEscaping = true; break; case EdmTypeKind.Complex: ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = this.propertyConverter.CreateODataComplexValue( typeAnnotation.ElementType, value, null /*propertyName*/, false /*isCollectionItemType*/, null /*visitedComplexTypeObjects*/); // When using JsonVerbose to format query string parameters for Actions, // we cannot write out Complex values in the URI without the type name of the complex type in the JSON payload. // If this value is null, the client has to set the ResolveName property on the DataServiceContext instance. ODataComplexValue complexValue = (ODataComplexValue)valueInODataFormat; SerializationTypeNameAnnotation serializedTypeNameAnnotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializedTypeNameAnnotation == null || string.IsNullOrEmpty(serializedTypeNameAnnotation.TypeName)) { throw Error.InvalidOperation(Strings.DataServiceException_GeneralError); } break; case EdmTypeKind.Collection: IEdmCollectionType edmCollectionType = edmType as IEdmCollectionType; Debug.Assert(edmCollectionType != null, "edmCollectionType != null"); IEdmTypeReference itemTypeReference = edmCollectionType.ElementType; Debug.Assert(itemTypeReference != null, "itemTypeReference != null"); ClientTypeAnnotation itemTypeAnnotation = model.GetClientTypeAnnotation(itemTypeReference.Definition); Debug.Assert(itemTypeAnnotation != null, "itemTypeAnnotation != null"); switch (itemTypeAnnotation.EdmType.TypeKind) { // We only support primitive or complex type as a collection item type. case EdmTypeKind.Primitive: case EdmTypeKind.Complex: break; default: throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(paramName, itemTypeAnnotation.EdmType.TypeKind)); } valueInODataFormat = this.propertyConverter.CreateODataCollection( itemTypeAnnotation.ElementType, null /*propertyName*/, value, null /*visitedComplexTypeObjects*/); break; default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference // EdmTypeKind.Enum. throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } // In the released WCF Data Services 5.0, we didn't pass the model for JSON Verbose literals, so continuing that behavior for backward compatibility ODataFormat literalFormat = this.requestInfo.Format.UriLiteralFormat; IEdmModel edmModel = literalFormat == ODataFormat.VerboseJson ? null : this.requestInfo.Model; // ODL can handle null values so we can send null values here. string literal = ODataUriUtils.ConvertToUriLiteral(valueInODataFormat, CommonUtil.ConvertToODataVersion(this.requestInfo.MaxProtocolVersionAsVersion), edmModel, literalFormat); // The value from ConvertToUriValue will not be escaped, but will already contain literal delimiters like single quotes, so we // need to use our own escape method that will preserve those characters instead of directly calling Uri.EscapeDataString that may escape them. // This is only necessary for primitives and nulls because the other structures are serialized using the JSON format and it uses double quotes // which have always been escaped. if (needsSpecialEscaping) { return(DataStringEscapeBuilder.EscapeDataString(literal)); } return(Uri.EscapeDataString(literal)); }
/// <summary> /// Generates a model explicitly. /// </summary> static IEdmModel GetExplicitEdmModel() { ODataModelBuilder modelBuilder = new ODataModelBuilder(); var products = modelBuilder.EntitySet <Product>("Products"); products.HasIdLink( entityContext => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4))))); }, followsConventions: true); products.HasEditLink( entityContext => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4))))); }, followsConventions: true); var suppliers = modelBuilder.EntitySet <Supplier>("Suppliers"); suppliers.HasIdLink( entityContext => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4))))); }, followsConventions: true); suppliers.HasEditLink( entityContext => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4))))); }, followsConventions: true); var families = modelBuilder.EntitySet <ProductFamily>("ProductFamilies"); families.HasIdLink( entityContext => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4))))); }, followsConventions: true); families.HasEditLink( entityContext => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4))) )); }, followsConventions: true); var product = products.EntityType; product.HasKey(p => p.Id); product.Property(p => p.Name); product.Property(p => p.ReleaseDate); product.Property(p => p.SupportedUntil); modelBuilder.EntityType <RatedProduct>().DerivesFrom <Product>().Property(rp => rp.Rating); var address = modelBuilder.ComplexType <Address>(); address.Property(a => a.City); address.Property(a => a.Country); address.Property(a => a.State); address.Property(a => a.Street); address.Property(a => a.ZipCode); var supplier = suppliers.EntityType; supplier.HasKey(s => s.Id); supplier.Property(s => s.Name); supplier.ComplexProperty(s => s.Address); var productFamily = families.EntityType; productFamily.HasKey(pf => pf.Id); productFamily.Property(pf => pf.Name); productFamily.Property(pf => pf.Description); // Create relationships and bindings in one go products.HasRequiredBinding(p => p.Family, families); families.HasManyBinding(pf => pf.Products, products); families.HasOptionalBinding(pf => pf.Supplier, suppliers); suppliers.HasManyBinding(s => s.ProductFamilies, families); // Create navigation Link builders products.HasNavigationPropertiesLink( product.NavigationProperties, (entityContext, navigationProperty) => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4)), new NavigationPathSegment(navigationProperty.Name)))); }, followsConventions: true); families.HasNavigationPropertiesLink( productFamily.NavigationProperties, (entityContext, navigationProperty) => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4)), new NavigationPathSegment(navigationProperty.Name)))); }, followsConventions: true); suppliers.HasNavigationPropertiesLink( supplier.NavigationProperties, (entityContext, navigationProperty) => { object id; entityContext.EdmObject.TryGetPropertyValue("Id", out id); return(new Uri(entityContext.Url.CreateODataLink( new EntitySetPathSegment(entityContext.NavigationSource.Name), new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(id, ODataVersion.V4)), new NavigationPathSegment(navigationProperty.Name)))); }, followsConventions: true); ActionConfiguration createProduct = productFamily.Action("CreateProduct"); createProduct.Parameter <string>("Name"); createProduct.Returns <int>(); modelBuilder.Namespace = typeof(ProductFamily).Namespace; return(modelBuilder.GetEdmModel()); }
public static IList <OperationSegmentParameter> Match(ODataTemplateTranslateContext context, IEdmFunction function, IDictionary <string, string> parameterMappings) { Contract.Assert(context != null); Contract.Assert(function != null); Contract.Assert(parameterMappings != null); RouteValueDictionary routeValues = context.UpdatedValues; IList <OperationSegmentParameter> parameters = new List <OperationSegmentParameter>(); foreach (var parameter in parameterMappings) { string parameterName = parameter.Key; string parameterTemp = parameter.Value; IEdmOperationParameter edmParameter = function.Parameters.FirstOrDefault(p => p.Name == parameterName); Contract.Assert(edmParameter != null); // For a parameter mapping like: minSalary={min} // and a request like: ~/MyFunction(minSalary=2) // the routeValue includes the [min=2], so we should use the mapping name to retrieve the value. if (routeValues.TryGetValue(parameterTemp, out object rawValue)) { string strValue = rawValue as string; string newStrValue = context.GetParameterAliasOrSelf(strValue); if (newStrValue != strValue) { routeValues[parameterTemp] = newStrValue; strValue = newStrValue; } // for resource or collection resource, this method will return "ODataResourceValue, ..." we should support it. if (edmParameter.Type.IsResourceOrCollectionResource()) { // For FromODataUri string prefixName = ODataParameterValue.ParameterValuePrefix + parameterTemp; routeValues[prefixName] = new ODataParameterValue(strValue, edmParameter.Type); parameters.Add(new OperationSegmentParameter(parameterName, strValue)); } else { object newValue = ODataUriUtils.ConvertFromUriLiteral(strValue, ODataVersion.V4, context.Model, edmParameter.Type); // for without FromODataUri, so update it, for example, remove the single quote for string value. routeValues[parameterTemp] = newValue; // For FromODataUri string prefixName = ODataParameterValue.ParameterValuePrefix + parameterTemp; routeValues[prefixName] = new ODataParameterValue(newValue, edmParameter.Type); parameters.Add(new OperationSegmentParameter(parameterName, newValue)); } } else { return(null); } } return(parameters); }