static OrderBySetting FindProperties(IEdmEntityType entity) { var propertyNames = entity.Key().Any() switch { true => entity.Key().Select(k => k.Name), false => entity.StructuralProperties() .Where(p => p.Type.IsPrimitive() && !p.Type.IsStream()) .Select(p => p.Name) .OrderBy(n => n) .Take(1) }; var orderBySettings = new OrderBySetting(); propertyNames.Aggregate(orderBySettings, (settings, name) => { if (settings.Name is null) { settings.Name = name; return(settings); } settings.ThenBy = new() { Name = name }; return(settings.ThenBy); }); return(orderBySettings.Name is null ? null : orderBySettings); } } }
/// <summary> /// Create key parameters for the <see cref="ODataKeySegment"/>. /// </summary> /// <param name="context">The OData context.</param> /// <param name="keySegment">The key segment.</param> /// <param name="parameterNameMapping">The parameter name mapping.</param> /// <returns>The created the list of <see cref="OpenApiParameter"/>.</returns> public static IList <OpenApiParameter> CreateKeyParameters(this ODataContext context, ODataKeySegment keySegment, IDictionary <string, string> parameterNameMapping = null) { Utils.CheckArgumentNull(context, nameof(context)); Utils.CheckArgumentNull(keySegment, nameof(keySegment)); IList <OpenApiParameter> parameters = new List <OpenApiParameter>(); IEdmEntityType entityType = keySegment.EntityType; IList <IEdmStructuralProperty> keys = entityType.Key().ToList(); if (keys.Count() == 1) { string keyName = keys.First().Name; // If dictionary parameterNameMapping is defined, there's no need of setting the // keyName, we will retrieve this from the dictionary key. if (context.Settings.PrefixEntityTypeNameBeforeKey && parameterNameMapping == null) { keyName = entityType.Name + "-" + keys.First().Name; } OpenApiParameter parameter = new OpenApiParameter { Name = parameterNameMapping == null ? keyName: parameterNameMapping[keyName], In = ParameterLocation.Path, Required = true, Description = "key: " + keyName + " of " + entityType.Name, Schema = context.CreateEdmTypeSchema(keys.First().Type) }; parameter.Extensions.Add(Constants.xMsKeyType, new OpenApiString(entityType.Name)); parameters.Add(parameter); } else { // append key parameter foreach (var keyProperty in entityType.Key()) { OpenApiParameter parameter = new OpenApiParameter { Name = parameterNameMapping == null ? keyProperty.Name: parameterNameMapping[keyProperty.Name],// By design: not prefix with type name if enable type name prefix In = ParameterLocation.Path, Required = true, Description = "key: " + keyProperty.Name + " of " + entityType.Name, Schema = context.CreateEdmTypeSchema(keyProperty.Type) }; parameter.Extensions.Add(Constants.xMsKeyType, new OpenApiString(entityType.Name)); parameters.Add(parameter); } } return(parameters); }
/// <inheritdoc /> public override bool TryTranslate(ODataTemplateTranslateContext context) { if (context == null) { throw Error.ArgumentNull(nameof(context)); } RouteValueDictionary routeValues = context.RouteValues; // the request should have the navigation property if (!routeValues.TryGetValue(ParameterName, out object rawValue)) { return(false); } string navigationProperty = rawValue as string; if (navigationProperty == null) { return(false); } // Find the navigation property IEdmNavigationProperty edmNavProperty = DeclaringType.ResolveProperty(navigationProperty) as IEdmNavigationProperty; if (edmNavProperty == null) { return(false); } IEdmNavigationSource targetNavigationSource = NavigationSource.FindNavigationTarget(edmNavProperty); // ODL implementation is complex, here i just use the NavigationPropertyLinkSegment context.Segments.Add(new NavigationPropertyLinkSegment(edmNavProperty, targetNavigationSource)); if (RelatedKey != null) { IEdmEntityType entityType = edmNavProperty.ToEntityType(); // only handle the single key IEdmStructuralProperty keyProperty = entityType.Key().SingleOrDefault(); Contract.Assert(entityType.Key().Count() == 1); Contract.Assert(keyProperty != null); IDictionary <string, string> keyValuePairs = new Dictionary <string, string> { { keyProperty.Name, $"{{{RelatedKey}}}" } }; KeySegmentTemplate keySegment = new KeySegmentTemplate(keyValuePairs, entityType, targetNavigationSource); return(keySegment.TryTranslate(context)); } return(true); }
/// <summary> /// Create key parameters for the <see cref="ODataKeySegment"/>. /// </summary> /// <param name="keySegment">The key segment.</param> /// <returns>The created the list of <see cref="OpenApiParameter"/>.</returns> public static IList <OpenApiParameter> CreateKeyParameters(this ODataContext context, ODataKeySegment keySegment) { Utils.CheckArgumentNull(context, nameof(context)); Utils.CheckArgumentNull(keySegment, nameof(keySegment)); IList <OpenApiParameter> parameters = new List <OpenApiParameter>(); IEdmEntityType entityType = keySegment.EntityType; IList <IEdmStructuralProperty> keys = entityType.Key().ToList(); if (keys.Count() == 1) { string keyName = keys.First().Name + keySegment.KeyIndex.ToString(); if (context.Settings.PrefixEntityTypeNameBeforeKey) { keyName = entityType.Name + "-" + keyName; } OpenApiParameter parameter = new OpenApiParameter { Name = keyName, In = ParameterLocation.Path, Required = true, Description = "key: " + keyName, Schema = context.CreateEdmTypeSchema(keys.First().Type) }; parameter.Extensions.Add(Constants.xMsKeyType, new OpenApiString(entityType.Name)); parameters.Add(parameter); } else { // append key parameter foreach (var keyProperty in entityType.Key()) { string keyName = keyProperty.Name + keySegment.KeyIndex.ToString(); OpenApiParameter parameter = new OpenApiParameter { Name = keyName, In = ParameterLocation.Path, Required = true, Description = "key: " + keyName, Schema = context.CreateEdmTypeSchema(keyProperty.Type) }; parameter.Extensions.Add(Constants.xMsKeyType, new OpenApiString(entityType.Name)); parameters.Add(parameter); } } return(parameters); }
// new CollectionWrapper<ElementType> { Instance = source.Select((ElementType element) => new Wrapper { }) } private Expression ProjectCollection(Expression source, Type elementType, SelectExpandClause selectExpandClause, IEdmEntityType entityType, IEdmEntitySet entitySet) { ParameterExpression element = Expression.Parameter(elementType); // expression // new Wrapper { } Expression projection = ProjectElement(element, selectExpandClause, entityType, entitySet); // expression // (ElementType element) => new Wrapper { } LambdaExpression selector = Expression.Lambda(projection, element); if (_settings.PageSize != null && _settings.PageSize.HasValue) { // nested paging. Need to apply order by first, and take one more than page size as we need to know // whether the collection was truncated or not while generating next page links. IEnumerable <IEdmStructuralProperty> properties = entityType.Key().Any() ? entityType.Key() : entityType .StructuralProperties() .Where(property => property.Type.IsPrimitive()).OrderBy(property => property.Name); bool alreadyOrdered = false; foreach (var prop in properties) { source = ExpressionHelpers.OrderByPropertyExpression(source, prop.Name, elementType, alreadyOrdered); if (!alreadyOrdered) { alreadyOrdered = true; } } source = ExpressionHelpers.Take(source, _settings.PageSize.Value + 1, elementType, _settings.EnableConstantParameterization); } // expression // source.Select((ElementType element) => new Wrapper { }) Expression selectedExpresion = Expression.Call(GetSelectMethod(elementType, projection.Type), source, selector); if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // source == null ? null : projectedCollection return(Expression.Condition( test: Expression.Equal(source, Expression.Constant(null)), ifTrue: Expression.Constant(null, selectedExpresion.Type), ifFalse: selectedExpresion)); } else { return(selectedExpresion); } }
// Returns a sorted list of all properties that may legally appear // in an OrderBy. If the entity type has keys, all are returned. // Otherwise, when no keys are present, all primitive properties are returned. private static IEnumerable <IEdmStructuralProperty> GetAvailableOrderByProperties(ODataQueryContext context) { Contract.Assert(context != null && context.EntitySet != null); IEdmEntityType entityType = context.EntitySet.ElementType; IEnumerable <IEdmStructuralProperty> properties = entityType.Key().Any() ? entityType.Key() : entityType .StructuralProperties() .Where(property => property.Type.IsPrimitive()); // Sort properties alphabetically for stable sort return(properties.OrderBy(property => property.Name)); }
/// <summary> /// Binds a key property value. /// </summary> /// <param name="namedValue">The named value to bind.</param> /// <param name="collectionItemEntityType">The type of a single item in a collection to apply the key value to.</param> /// <returns>The bound key property value node.</returns> private KeyPropertyValue BindKeyPropertyValue(NamedValue namedValue, IEdmEntityType collectionItemEntityType) { // These are exception checks because the data comes directly from the potentially user specified tree. ExceptionUtils.CheckArgumentNotNull(namedValue, "namedValue"); ExceptionUtils.CheckArgumentNotNull(namedValue.Value, "namedValue.Value"); Debug.Assert(collectionItemEntityType != null, "collectionItemType != null"); IEdmProperty keyProperty = null; if (namedValue.Name == null) { foreach (IEdmProperty p in collectionItemEntityType.Key()) { if (keyProperty == null) { keyProperty = p; } else { throw new ODataException(ODataErrorStrings.MetadataBinder_UnnamedKeyValueOnTypeWithMultipleKeyProperties(collectionItemEntityType.ODataFullName())); } } } else { keyProperty = collectionItemEntityType.Key().Where(k => string.CompareOrdinal(k.Name, namedValue.Name) == 0).SingleOrDefault(); if (keyProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclaredOrNotKeyInKeyValue(namedValue.Name, collectionItemEntityType.ODataFullName())); } } IEdmTypeReference keyPropertyType = keyProperty.Type; SingleValueNode value = (SingleValueNode)this.keyValueBindMethod(namedValue.Value); // TODO: Check that the value is of primitive type Debug.Assert(keyPropertyType.IsODataPrimitiveTypeKind(), "The key's type must be primitive."); value = MetadataBindingUtils.ConvertToTypeIfNeeded(value, keyPropertyType); Debug.Assert(keyProperty != null, "keyProperty != null"); return(new KeyPropertyValue() { KeyProperty = keyProperty, KeyValue = value }); }
public static IEdmStructuralProperty AssertHasKey(this IEdmEntityType entity, IEdmModel model, string keyName, EdmPrimitiveTypeKind primitiveTypeKind) { IEdmStructuralProperty key = entity.AssertHasPrimitiveProperty(model, keyName, primitiveTypeKind, isNullable: false); Assert.Contains(key, entity.Key()); return(key); }
/// <summary> /// Validates the <see cref="IEdmModel"/> that is being created. /// </summary> /// <param name="model">The <see cref="IEdmModel"/> that will be validated.</param> public virtual void ValidateModel(IEdmModel model) { if (model == null) { throw Error.ArgumentNull("model"); } // The type of entity set should have key(s) defined. foreach (IEdmEntitySet entitySet in model.EntityContainer.Elements.OfType <IEdmEntitySet>()) { if (!entitySet.EntityType().Key().Any()) { throw Error.InvalidOperation(SRResources.EntitySetTypeHasNoKeys, entitySet.Name, entitySet.EntityType().FullName()); } } // The type of collection navigation property should have key(s) defined. foreach (IEdmStructuredType structuredType in model.SchemaElementsAcrossModels().OfType <IEdmStructuredType>()) { foreach (var navigationProperty in structuredType.DeclaredNavigationProperties()) { if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { IEdmEntityType entityType = navigationProperty.ToEntityType(); if (!entityType.Key().Any()) { throw Error.InvalidOperation(SRResources.CollectionNavigationPropertyEntityTypeDoesntHaveKeyDefined, entityType.FullTypeName(), navigationProperty.Name, structuredType.FullTypeName()); } } } } }
/// <summary> /// Resolve keys for certain entity set, this function would be called when key is specified as positional values. E.g. EntitySet('key') /// </summary> /// <param name="type">Type for current entityset.</param> /// <param name="positionalValues">The list of positional values.</param> /// <param name="convertFunc">The convert function to be used for value converting.</param> /// <returns>The resolved key list.</returns> public virtual IEnumerable <KeyValuePair <string, object> > ResolveKeys(IEdmEntityType type, IList <string> positionalValues, Func <IEdmTypeReference, string, object> convertFunc) { var keyProperties = type.Key().ToList(); // Throw an error if key size from url doesn't match that from model. // Other derived ODataUriResolver intended for alternative key resolution, such as the built in AlternateKeysODataUriResolver, // should override this ResolveKeys method. if (keyProperties.Count != positionalValues.Count) { throw ExceptionUtil.CreateBadRequestError(Strings.BadRequest_KeyCountMismatch(type.FullName())); } var keyPairList = new List <KeyValuePair <string, object> >(positionalValues.Count); for (int i = 0; i < keyProperties.Count; i++) { string valueText = positionalValues[i]; IEdmProperty keyProperty = keyProperties[i]; object convertedValue = convertFunc(keyProperty.Type, valueText); if (convertedValue == null) { throw ExceptionUtil.CreateSyntaxError(); } keyPairList.Add(new KeyValuePair <string, object>(keyProperty.Name, convertedValue)); } return(keyPairList); }
public static string GetEntityKeyValue(ResourceContext resourceContext) { Contract.Assert(resourceContext != null); Contract.Assert(resourceContext.StructuredType != null); Contract.Assert(resourceContext.EdmObject != null); IEdmEntityType entityType = resourceContext.StructuredType as IEdmEntityType; if (entityType == null) { return(String.Empty); } IEnumerable <IEdmProperty> keys = entityType.Key(); if (keys.Count() == 1) { return(GetUriRepresentationForKeyValue(keys.First(), resourceContext)); } else { IEnumerable <string> keyValues = keys.Select(key => String.Format( CultureInfo.InvariantCulture, "{0}={1}", key.Name, GetUriRepresentationForKeyValue(key, resourceContext))); return(String.Join(",", keyValues)); } }
/// <summary> /// Create the collection of <see cref="OpenApiLink"/> object. /// </summary> /// <param name="context">The OData context.</param> /// <param name="entityType">The Entity type.</param> /// <param name ="sourceElementName">The name of the source of the <see cref="IEdmEntityType" />.</param> /// <returns>The created dictionary of <see cref="OpenApiLink"/> object.</returns> public static IDictionary <string, OpenApiLink> CreateLinks(this ODataContext context, IEdmEntityType entityType, string sourceElementName) { Utils.CheckArgumentNull(context, nameof(context)); Utils.CheckArgumentNull(entityType, nameof(entityType)); Utils.CheckArgumentNullOrEmpty(sourceElementName, nameof(sourceElementName)); IDictionary <string, OpenApiLink> links = new Dictionary <string, OpenApiLink>(); foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties()) { OpenApiLink link = new OpenApiLink { OperationId = sourceElementName + "." + entityType.Name + ".Get" + Utils.UpperFirstChar(entityType.Name), Parameters = new Dictionary <string, RuntimeExpressionAnyWrapper>() }; foreach (IEdmStructuralProperty key in entityType.Key()) { link.Parameters[key.Name] = new RuntimeExpressionAnyWrapper { Any = new OpenApiString("$request.path." + key.Name) }; } links[np.Name] = link; } return(links); }
// Convert the objects of keys in ODL path to string literals. private static string ConvertKeysToString( IEnumerable <KeyValuePair <string, object> > keys, IEdmType edmType, bool enableUriTemplateParsing) { Contract.Assert(keys != null); IEdmEntityType entityType = edmType as IEdmEntityType; Contract.Assert(entityType != null); if (keys.Count() < 2) { var keyValue = keys.First(); bool isDeclaredKey = entityType.Key().Any(k => k.Name == keyValue.Key); if (isDeclaredKey) { return(String.Join( ",", keys.Select(keyValuePair => TranslateKeySegmentValue(keyValuePair.Value, enableUriTemplateParsing)).ToArray())); } } return(String.Join( ",", keys.Select(keyValuePair => (keyValuePair.Key + "=" + TranslateKeySegmentValue(keyValuePair.Value, enableUriTemplateParsing))).ToArray())); }
private static IOpenApiAny CreateStructuredTypePropertiesExample(ODataContext context, IEdmStructuredType structuredType) { OpenApiObject example = new OpenApiObject(); IEdmEntityType entityType = structuredType as IEdmEntityType; // properties foreach (var property in structuredType.Properties()) { // IOpenApiAny item; IEdmTypeReference propertyType = property.Type; IOpenApiAny item = GetTypeNameForExample(context, propertyType); EdmTypeKind typeKind = propertyType.TypeKind(); if (typeKind == EdmTypeKind.Primitive && item is OpenApiString) { OpenApiString stringAny = item as OpenApiString; string value = stringAny.Value; if (entityType != null && entityType.Key().Any(k => k.Name == property.Name)) { value += " (identifier)"; } if (propertyType.IsDateTimeOffset() || propertyType.IsDate() || propertyType.IsTimeOfDay()) { value += " (timestamp)"; } item = new OpenApiString(value); } example.Add(property.Name, item); } return(example); }
private static string GenerateDefaultOrderBy(ODataQueryContext context) { Contract.Assert(context != null && context.EntitySet != null); IEdmEntityType entityType = context.EntitySet.ElementType; // choose the keys alphabetically. This would return a stable sort. string sortOrder = String.Join(",", entityType .Key() .OrderBy(property => property.Name) .Select(property => property.Name)); if (String.IsNullOrEmpty(sortOrder)) { // If there are no keys, choose the primitive properties alphabetically. This // might not result in a stable sort especially if there are duplicates and is only // a best effort solution. sortOrder = String.Join(",", entityType .StructuralProperties() .Where(property => property.Type.IsPrimitive()) .OrderBy(property => property.Name) .Select(property => property.Name)); } return(sortOrder); }
/// <summary> /// Returns the list of properties that should be used for generating the skiptoken value. /// </summary> /// <param name="lastMember">The last record that will be returned in the response.</param> /// <param name="model">IEdmModel</param> /// <param name="orderByNodes">OrderBy nodes in the original request.</param> /// <returns>List of properties that should be used for generating the skiptoken value.</returns> private static IEnumerable <IEdmProperty> GetPropertiesForSkipToken(object lastMember, IEdmModel model, IList <OrderByNode> orderByNodes) { IEdmType edmType = GetTypeFromObject(lastMember, model); IEdmEntityType entity = edmType as IEdmEntityType; if (entity == null) { return(null); } IEnumerable <IEdmProperty> key = entity.Key(); if (orderByNodes != null) { if (orderByNodes.OfType <OrderByOpenPropertyNode>().Any()) { //SkipToken will not support ordering on dynamic properties return(null); } IList <IEdmProperty> orderByProps = orderByNodes.OfType <OrderByPropertyNode>().Select(p => p.Property).AsList(); foreach (IEdmProperty subKey in key) { if (!orderByProps.Contains(subKey)) { orderByProps.Add(subKey); } } return(orderByProps.AsEnumerable()); } return(key); }
/// <summary> /// Test whether the action has the key parameters defined. /// </summary> /// <param name="action">The action model.</param> /// <param name="entityType">The Edm entity type.</param> /// <param name="keyPrefix">The key prefix for the action parameter.</param> /// <returns>True/false.</returns> public static bool HasODataKeyParameter(this ActionModel action, IEdmEntityType entityType, string keyPrefix = "key") { if (action == null) { throw Error.ArgumentNull(nameof(action)); } if (entityType == null) { throw Error.ArgumentNull(nameof(entityType)); } // TODO: shall we make sure the type is matching? var keys = entityType.Key().ToArray(); if (keys.Length == 1) { // one key return(action.Parameters.Any(p => p.ParameterInfo.Name == keyPrefix)); } else { // multipe keys foreach (var key in keys) { string keyName = $"{keyPrefix}{key.Name}"; if (!action.Parameters.Any(p => p.ParameterInfo.Name == keyName)) { return(false); } } return(true); } }
/// <summary> /// Create the collection of <see cref="OpenApiLink"/> object. /// </summary> /// <param name="context">The OData context.</param> /// <param name="entitySet">The Entity Set.</param> /// <returns>The created dictionary of <see cref="OpenApiLink"/> object.</returns> public static IDictionary <string, OpenApiLink> CreateLinks(this ODataContext context, IEdmEntitySet entitySet) { Utils.CheckArgumentNull(context, nameof(context)); Utils.CheckArgumentNull(entitySet, nameof(entitySet)); IDictionary <string, OpenApiLink> links = new Dictionary <string, OpenApiLink>(); IEdmEntityType entityType = entitySet.EntityType(); foreach (var np in entityType.DeclaredNavigationProperties()) { OpenApiLink link = new OpenApiLink(); string typeName = entitySet.EntityType().Name; link.OperationId = entitySet.Name + "." + typeName + ".Get" + Utils.UpperFirstChar(typeName); link.Parameters = new Dictionary <string, RuntimeExpressionAnyWrapper>(); foreach (var key in entityType.Key()) { link.Parameters[key.Name] = new RuntimeExpressionAnyWrapper { Any = new OpenApiString("$request.path." + key.Name) }; } links[np.Name] = link; } return(links); }
public static string TranslateKeyValueToString(this KeySegment segment) { Contract.Assert(segment != null); IEdmEntityType entityType = segment.EdmType as IEdmEntityType; Contract.Assert(entityType != null); var keys = segment.Keys.ToList(); if (keys.Count < 2) { var keyValue = keys.First(); bool isDeclaredKey = entityType.Key().Any(k => k.Name == keyValue.Key); // alternate keys are always using the "key=value" if (isDeclaredKey) { return(String.Join( ",", keys.Select(keyValuePair => TranslateKeySegmentValue(keyValuePair.Value)).ToArray())); } } return(String.Join( ",", keys.Select(keyValuePair => (keyValuePair.Key + "=" + TranslateKeySegmentValue(keyValuePair.Value))).ToArray())); }
/// <summary> /// Parses the key properties based on the segment's target type, then creates a new segment for the key. /// </summary> /// <param name="segment">The segment to apply the key to.</param> /// <param name="key">The key to apply.</param> /// <returns>The newly created key segment.</returns> private static IPathSegment CreateKeySegment(IPathSegment segment, KeyInstance key) { Debug.Assert(segment != null, "segment != null"); Debug.Assert(key != null && !key.IsEmpty, "key != null && !key.IsEmpty"); Debug.Assert(segment.SingleResult == false, "segment.SingleResult == false"); IEdmEntityType targetEntityType = null; WebUtil.CheckSyntaxValid(segment.TargetEdmType != null && segment.TargetEdmType.IsEntityOrEntityCollectionType(out targetEntityType)); Debug.Assert(targetEntityType != null, "targetEntityType != null"); // Make sure the keys specified in the uri matches with the number of keys in the metadata var keyProperties = targetEntityType.Key().ToList(); if (keyProperties.Count != key.ValueCount) { throw ExceptionUtil.CreateBadRequestError(ErrorStrings.BadRequest_KeyCountMismatch(targetEntityType.FullName())); } if (!key.AreValuesNamed && key.ValueCount > 1) { throw ExceptionUtil.CreateBadRequestError(ErrorStrings.RequestUriProcessor_KeysMustBeNamed); } WebUtil.CheckSyntaxValid(key.TryConvertValues(keyProperties)); return(new PathSegment(segment) { Key = key, SingleResult = true, }); }
/// <summary> /// Get key value pair array for specific odata resource using specific entity type /// </summary> /// <param name="resource">The resource instance.</param> /// <param name="serializationInfo">The serialization info of the resource for writing without model.</param> /// <param name="actualEntityType">The edm entity type of the resource</param> /// <returns>Key value pair array</returns> internal static KeyValuePair <string, object>[] GetKeyProperties( ODataResourceBase resource, ODataResourceSerializationInfo serializationInfo, IEdmEntityType actualEntityType) { KeyValuePair <string, object>[] keyProperties = null; string actualEntityTypeName = null; if (serializationInfo != null) { if (String.IsNullOrEmpty(resource.TypeName)) { throw new ODataException(Strings.ODataResourceTypeContext_ODataResourceTypeNameMissing); } actualEntityTypeName = resource.TypeName; keyProperties = ODataResourceMetadataContextWithoutModel.GetPropertiesBySerializationInfoPropertyKind(resource, ODataPropertyKind.Key, actualEntityTypeName); } else { keyProperties = GetPropertyValues(actualEntityType.Key(), resource, actualEntityType, /*isKeyProperty*/ true, /*isRequired*/ true).ToArray(); } ValidateEntityTypeHasKeyProperties(keyProperties, actualEntityTypeName); return(keyProperties); }
/// <summary> /// Create <see cref="KeySegmentTemplate"/> based on the given entity type and navigation source. /// </summary> /// <param name="entityType">The given entity type.</param> /// <param name="navigationSource">The given navigation source.</param> /// <param name="keyPrefix">The prefix used before key template.</param> /// <returns>The built <see cref="KeySegmentTemplate"/>.</returns> internal static KeySegmentTemplate CreateKeySegment(IEdmEntityType entityType, IEdmNavigationSource navigationSource, string keyPrefix = "key") { if (entityType == null) { throw Error.ArgumentNull(nameof(entityType)); } IDictionary <string, string> keyTemplates = new Dictionary <string, string>(); var keys = entityType.Key().ToArray(); if (keys.Length == 1) { // Id={key} keyTemplates[keys[0].Name] = $"{{{keyPrefix}}}"; } else { // Id1={keyId1},Id2={keyId2} foreach (var key in keys) { keyTemplates[key.Name] = $"{{{keyPrefix}{key.Name}}}"; } } return(new KeySegmentTemplate(keyTemplates, entityType, navigationSource)); }
/// <summary> /// Get key value pair array for specifc odata resource using specifc entity type /// </summary> /// <param name="resource">The resource instance.</param> /// <param name="serializationInfo">The serialization info of the resource for writing without model.</param> /// <param name="actualEntityType">The edm entity type of the resource</param> /// <returns>Key value pair array</returns> internal static KeyValuePair <string, object>[] GetKeyProperties( ODataResource resource, ODataResourceSerializationInfo serializationInfo, IEdmEntityType actualEntityType) { KeyValuePair <string, object>[] keyProperties = null; string actualEntityTypeName = null; if (serializationInfo != null) { if (String.IsNullOrEmpty(resource.TypeName)) { throw new ODataException(Strings.ODataResourceTypeContext_ODataResourceTypeNameMissing); } actualEntityTypeName = resource.TypeName; keyProperties = ODataResourceMetadataContextWithoutModel.GetPropertiesBySerializationInfoPropertyKind(resource, ODataPropertyKind.Key, actualEntityTypeName); } else { actualEntityTypeName = actualEntityType.FullName(); IEnumerable <IEdmStructuralProperty> edmKeyProperties = actualEntityType.Key(); if (edmKeyProperties != null) { keyProperties = edmKeyProperties.Select(p => new KeyValuePair <string, object>(p.Name, GetPrimitiveOrEnumPropertyValue(resource, p.Name, actualEntityTypeName, /*isKeyProperty*/ false))).ToArray(); } } ValidateEntityTypeHasKeyProperties(keyProperties, actualEntityTypeName); return(keyProperties); }
private static ISet <IEdmStructuralProperty> GetPropertiesToIncludeInQuery( SelectExpandClause selectExpandClause, IEdmEntityType entityType, out ISet <IEdmStructuralProperty> autoSelectedProperties) { autoSelectedProperties = new HashSet <IEdmStructuralProperty>(); HashSet <IEdmStructuralProperty> propertiesToInclude = new HashSet <IEdmStructuralProperty>(); IEnumerable <SelectItem> selectedItems = selectExpandClause.SelectedItems; if (!IsSelectAll(selectExpandClause)) { // only select requested properties and keys. foreach (PathSelectItem pathSelectItem in selectedItems.OfType <PathSelectItem>()) { SelectExpandNode.ValidatePathIsSupported(pathSelectItem.SelectedPath); PropertySegment structuralPropertySegment = pathSelectItem.SelectedPath.LastSegment as PropertySegment; if (structuralPropertySegment != null) { propertiesToInclude.Add(structuralPropertySegment.Property); } } // add keys foreach (IEdmStructuralProperty keyProperty in entityType.Key()) { if (!propertiesToInclude.Contains(keyProperty)) { autoSelectedProperties.Add(keyProperty); } } } return(propertiesToInclude); }
/// <summary> /// Get key value pair array for specific odata resource using specific entity type /// </summary> /// <param name="resource">The resource instance.</param> /// <param name="serializationInfo">The serialization info of the resource for writing without model.</param> /// <param name="actualEntityType">The edm entity type of the resource</param> /// <param name="requiresId">Whether key properties are required to be returned</param> /// <returns>Key value pair array</returns> internal static KeyValuePair <string, object>[] GetKeyProperties( ODataResourceBase resource, ODataResourceSerializationInfo serializationInfo, IEdmEntityType actualEntityType, bool requiresId) { Debug.Assert(resource != null, "GetKeyProperties called for a null resource."); KeyValuePair <string, object>[] keyProperties = null; string actualEntityTypeName = string.IsNullOrEmpty(resource.TypeName) ? actualEntityType?.FullName() : resource.TypeName; // if we have serializationInfo, try that first if (serializationInfo != null) { keyProperties = ODataResourceMetadataContextWithoutModel.GetPropertiesBySerializationInfoPropertyKind(resource, ODataPropertyKind.Key, actualEntityTypeName); } // if we didn't get any keys from serializationInfo, try using entity type if ((keyProperties == null || keyProperties.Length == 0) && actualEntityType != null) { keyProperties = GetPropertyValues(actualEntityType.Key(), resource, actualEntityType, requiresId).ToArray(); } if (!ValidateEntityTypeHasKeyProperties(keyProperties, actualEntityTypeName, requiresId)) { return(Enumerable.Empty <KeyValuePair <string, object> >().ToArray()); } return(keyProperties); }
/// <summary> /// Compares the properties (key, structural, navigation) of an EntityType element to those in the Edm model. /// </summary> /// <param name="typeElement">The EntityType element to compare to the model.</param> /// <param name="entityType">The corresponding EdmEntityType from the model.</param> /// <param name="typeIndex">All entity types from the model, indexed by qualified name.</param> private static void CompareEntityTypeProperties(XElement typeElement, IEdmEntityType entityType, IDictionary <string, XElement> typeIndex) { string fullTypeName = entityType.FullName(); CompareBaseType(typeElement, entityType); Func <XElement, IEnumerable <string> > getKeyPropertyNames = (element) => { var keyElement = element.EdmElements("Key").SingleOrDefault(); return(keyElement == null?Enumerable.Empty <string>() : keyElement.EdmElements("PropertyRef").Select(e => e.GetAttributeValue("Name"))); }; // Collect all key properties from the type hierachy and compare var keyPropertyNames = RecurseBaseTypes(typeElement, typeIndex, getKeyPropertyNames).ToArray(); var keyPropertiesOnModel = entityType.Key(); ExceptionUtilities.Assert(keyPropertyNames.Count() == keyPropertiesOnModel.Count(), "Unexpected number of key properties on type " + fullTypeName); var missingKeyProperties = keyPropertyNames.Except(keyPropertiesOnModel.Select(p => p.Name)).ToArray(); ExceptionUtilities.Assert(!missingKeyProperties.Any(), "Failed to find the key properties " + string.Join(",", missingKeyProperties) + " on type " + fullTypeName); // Collect all structural properties from the type hierachy and compare var propertyElements = RecurseBaseTypes(typeElement, typeIndex, (e) => e.EdmElements("Property")); CompareStructuralProperties(propertyElements, entityType); // Collect all navigation properties from the type hierachy and compare var navigationPropertyElements = RecurseBaseTypes(typeElement, typeIndex, (e) => e.EdmElements("NavigationProperty")); CompareNavigationProperties(navigationPropertyElements, entityType); }
private static KeySegment BuildKeySegment(IEdmEntityType entityType, IEdmEntitySetBase entitySet, object target) { if (entityType == null) { throw new ArgumentNullException("entityType"); } if (entitySet == null) { throw new ArgumentNullException("entitySet"); } if (target == null) { throw new ArgumentNullException("target"); } KeySegment keySegment = new KeySegment( entityType.Key().Select( (key) => { var keyValue = target.GetType().GetProperty(key.Name).GetValue(target, null); return(new KeyValuePair <string, object>(key.Name, keyValue)); }), entityType, entitySet); return(keySegment); }
private static string GetKeyInfos(int keyCount, string keyName, IEdmEntityType entityType, string keyPrefix, out IEdmTypeReference keyType) { Contract.Assert(keyName != null); Contract.Assert(entityType != null); string newKeyName; IEdmStructuralProperty keyProperty; if (String.IsNullOrEmpty(keyName)) { Contract.Assert(keyCount == 1); keyProperty = entityType.Key().First(); newKeyName = keyPrefix; } else { bool alternateKey = false; keyProperty = entityType.Key().FirstOrDefault(k => k.Name == keyName); if (keyProperty == null) { // If it's alternate key. keyProperty = entityType.Properties().OfType<IEdmStructuralProperty>().FirstOrDefault(p => p.Name == keyName); alternateKey = true; } Contract.Assert(keyProperty != null); // if there's only one key, just use the given prefix name, for example: "key, relatedKey" // otherwise, to append the key name after the given prefix name. // so for multiple keys, the parameter name is "keyId1, keyId2..." // for navigation property, the parameter name is "relatedKeyId1, relatedKeyId2 ..." // for alternate key, to append the alternate key name after the given prefix name. if (alternateKey || keyCount > 1) { newKeyName = keyPrefix + keyName; } else { newKeyName = keyPrefix; } } keyType = keyProperty.Type; return newKeyName; }
/// <summary> /// Parses the key properties based on the segment's target type, then creates a new segment for the key. /// </summary> /// <param name="segment">The segment to apply the key to.</param> /// <param name="previousKeySegment">The parent node's key segment.</param> /// <param name="key">The key to apply.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>The newly created key segment.</returns> private static KeySegment CreateKeySegment(ODataPathSegment segment, KeySegment previousKeySegment, SegmentArgumentParser key, ODataUriResolver resolver) { Debug.Assert(segment != null, "segment != null"); Debug.Assert(key != null && !key.IsEmpty, "key != null && !key.IsEmpty"); Debug.Assert(segment.SingleResult == false, "segment.SingleResult == false"); IEdmEntityType targetEntityType = null; if (!(segment.TargetEdmType != null && segment.TargetEdmType.IsEntityOrEntityCollectionType(out targetEntityType))) { throw ExceptionUtil.CreateSyntaxError(); } Debug.Assert(targetEntityType != null, "targetEntityType != null"); // Make sure the keys specified in the uri matches with the number of keys in the metadata var keyProperties = targetEntityType.Key().ToList(); if (keyProperties.Count != key.ValueCount) { NavigationPropertySegment currentNavPropSegment = segment as NavigationPropertySegment; if (currentNavPropSegment != null) { key = KeyFinder.FindAndUseKeysFromRelatedSegment(key, keyProperties, currentNavPropSegment.NavigationProperty, previousKeySegment); } // if we still didn't find any keys, then throw an error. if (keyProperties.Count != key.ValueCount && resolver.GetType() == typeof(ODataUriResolver)) { throw ExceptionUtil.CreateBadRequestError(ErrorStrings.BadRequest_KeyCountMismatch(targetEntityType.FullName())); } } if (!key.AreValuesNamed && key.ValueCount > 1 && resolver.GetType() == typeof(ODataUriResolver)) { throw ExceptionUtil.CreateBadRequestError(ErrorStrings.RequestUriProcessor_KeysMustBeNamed); } IEnumerable <KeyValuePair <string, object> > keyPairs; if (!key.TryConvertValues(targetEntityType, out keyPairs, resolver)) { throw ExceptionUtil.CreateSyntaxError(); } IEdmEntityType entityType; bool isEntity = segment.TargetEdmType.IsEntityOrEntityCollectionType(out entityType); Debug.Assert(isEntity, "Key target type should be an entity type."); var keySegment = new KeySegment(keyPairs, entityType, segment.TargetEdmNavigationSource); keySegment.CopyValuesFrom(segment); keySegment.SingleResult = true; return(keySegment); }
/// <summary>Tries to convert values to the keys of the specified type.</summary> /// <param name="targetEntityType">The specified type.</param> /// <param name="keyPairs">The converted key-value pairs.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>true if all values were converted; false otherwise.</returns> public bool TryConvertValues(IEdmEntityType targetEntityType, out IEnumerable <KeyValuePair <string, object> > keyPairs, ODataUriResolver resolver) { Debug.Assert(!this.IsEmpty, "!this.IsEmpty -- caller should check"); Debug.Assert(targetEntityType.Key().Count() == this.ValueCount || resolver.GetType() != typeof(ODataUriResolver), "type.KeyProperties.Count == this.ValueCount -- will change with containment"); if (this.NamedValues != null) { keyPairs = resolver.ResolveKeys(targetEntityType, this.NamedValues, this.ConvertValueWrapper); } else { Debug.Assert(this.positionalValues != null, "positionalValues != null -- otherwise this is Empty"); Debug.Assert(this.PositionalValues.Count == targetEntityType.Key().Count() || resolver.GetType() != typeof(ODataUriResolver), "Count of positional values does not match."); keyPairs = resolver.ResolveKeys(targetEntityType, this.PositionalValues, this.ConvertValueWrapper); } return(true); }
private void CompareEntityType(IEdmEntityType expectedEntityType, IEdmEntityType actualEntityType) { this.SatisfiesEquals(expectedEntityType.FullName(), actualEntityType.FullName(), "EntityType name does not match."); this.SatisfiesEquals(expectedEntityType.IsAbstract, actualEntityType.IsAbstract, "IsAbstract does not match for EntityType '{0}'.", expectedEntityType.FullName()); string expectedBaseTypeName = expectedEntityType.BaseType != null ? ((IEdmSchemaElement)expectedEntityType.BaseType).FullName() : null; string actualBaseTypeName = actualEntityType.BaseType != null ? ((IEdmSchemaElement)actualEntityType.BaseType).FullName() : null; this.SatisfiesEquals(expectedBaseTypeName, actualBaseTypeName, "BaseType does not match for EntityType '{0}'.", expectedEntityType.FullName()); this.CompareProperties(expectedEntityType.StructuralProperties().Cast<IEdmProperty>(), actualEntityType.StructuralProperties().Cast<IEdmProperty>()); this.CompareProperties(expectedEntityType.Key().OfType<IEdmProperty>(), actualEntityType.Key().OfType<IEdmProperty>()); this.CompareNavigationProperty(expectedEntityType.Properties().OfType<IEdmNavigationProperty>(), actualEntityType.Properties().OfType<IEdmNavigationProperty>()); this.CompareTermAnnotations(expectedEntityType, actualEntityType); }
private EntityType ConvertToTaupoEntityType(IEdmEntityType edmEntityType) { var taupoEntityType = new EntityType(edmEntityType.Namespace, edmEntityType.Name) { IsAbstract = edmEntityType.IsAbstract, IsOpen = edmEntityType.IsOpen, }; if (edmEntityType.BaseType != null) { taupoEntityType.BaseType = new EntityTypeReference(edmEntityType.BaseEntityType().Namespace, edmEntityType.BaseEntityType().Name); } foreach (var edmProperty in edmEntityType.DeclaredStructuralProperties()) { var taupoProperty = this.ConvertToTaupoProperty(edmProperty); taupoProperty.IsPrimaryKey = edmEntityType.Key().Contains(edmProperty); taupoEntityType.Add(taupoProperty); } this.ConvertAnnotationsIntoTaupo(edmEntityType, taupoEntityType); return taupoEntityType; }
private static ISet<IEdmStructuralProperty> GetPropertiesToIncludeInQuery( SelectExpandClause selectExpandClause, IEdmEntityType entityType, out ISet<IEdmStructuralProperty> autoSelectedProperties) { autoSelectedProperties = new HashSet<IEdmStructuralProperty>(); HashSet<IEdmStructuralProperty> propertiesToInclude = new HashSet<IEdmStructuralProperty>(); PartialSelection selection = selectExpandClause.Selection as PartialSelection; if (selection != null && !selection.SelectedItems.OfType<WildcardSelectionItem>().Any()) { // only select requested properties and keys. foreach (PathSelectionItem pathSelectionItem in selection.SelectedItems.OfType<PathSelectionItem>()) { SelectExpandNode.ValidatePathIsSupported(pathSelectionItem.SelectedPath); PropertySegment structuralPropertySegment = pathSelectionItem.SelectedPath.LastSegment as PropertySegment; if (structuralPropertySegment != null) { propertiesToInclude.Add(structuralPropertySegment.Property); } } // add keys foreach (IEdmStructuralProperty keyProperty in entityType.Key()) { if (!propertiesToInclude.Contains(keyProperty)) { autoSelectedProperties.Add(keyProperty); } } } return propertiesToInclude; }
/// <summary> /// Tries to bind key values to a key lookup on a collection. /// </summary> /// <param name="collectionNode">Already bound collection node.</param> /// <param name="namedValues">The named value tokens to bind.</param> /// <param name="model">The model to be used.</param> /// <param name="collectionItemEntityType">The type of a single item in a collection to apply the key value to.</param> /// <param name="keyLookupNode">The bound key lookup.</param> /// <returns>Returns true if binding succeeded.</returns> private bool TryBindToDeclaredKey(EntityCollectionNode collectionNode, IEnumerable<NamedValue> namedValues, IEdmModel model, IEdmEntityType collectionItemEntityType, out QueryNode keyLookupNode) { Dictionary<string, IEdmProperty> keys = new Dictionary<string, IEdmProperty>(StringComparer.Ordinal); foreach (IEdmStructuralProperty property in collectionItemEntityType.Key()) { keys[property.Name] = property; } return TryBindToKeys(collectionNode, namedValues, model, collectionItemEntityType, keys, out keyLookupNode); }
/// <summary> /// Get key value pair array for specifc odata entry using specifc entity type /// </summary> /// <param name="entry">The entry instance.</param> /// <param name="serializationInfo">The serialization info of the entry for writing without model.</param> /// <param name="actualEntityType">The edm entity type of the entry</param> /// <returns>Key value pair array</returns> internal static KeyValuePair<string, object>[] GetKeyProperties( ODataEntry entry, ODataFeedAndEntrySerializationInfo serializationInfo, IEdmEntityType actualEntityType) { KeyValuePair<string, object>[] keyProperties = null; string actualEntityTypeName = null; if (serializationInfo != null) { if (String.IsNullOrEmpty(entry.TypeName)) { throw new ODataException(OData.Core.Strings.ODataFeedAndEntryTypeContext_ODataEntryTypeNameMissing); } actualEntityTypeName = entry.TypeName; keyProperties = ODataEntryMetadataContextWithoutModel.GetPropertiesBySerializationInfoPropertyKind(entry, ODataPropertyKind.Key, actualEntityTypeName); } else { actualEntityTypeName = actualEntityType.FullName(); IEnumerable<IEdmStructuralProperty> edmKeyProperties = actualEntityType.Key(); if (edmKeyProperties != null) { keyProperties = edmKeyProperties.Select(p => new KeyValuePair<string, object>(p.Name, GetPrimitivePropertyClrValue(entry, p.Name, actualEntityTypeName, /*isKeyProperty*/false))).ToArray(); } } ValidateEntityTypeHasKeyProperties(keyProperties, actualEntityTypeName); return keyProperties; }
/// <summary> /// Resolve keys for certain entity set, this function would be called when key is specified as name value pairs. E.g. EntitySet(ID='key') /// </summary> /// <param name="type">Type for current entityset.</param> /// <param name="namedValues">The dictionary of name value pairs.</param> /// <param name="convertFunc">The convert function to be used for value converting.</param> /// <returns>The resolved key list.</returns> public virtual IEnumerable<KeyValuePair<string, object>> ResolveKeys(IEdmEntityType type, IDictionary<string, string> namedValues, Func<IEdmTypeReference, string, object> convertFunc) { var convertedPairs = new Dictionary<string, object>(StringComparer.Ordinal); var keyProperties = type.Key().ToList(); foreach (IEdmStructuralProperty property in keyProperties) { string valueText; if (EnableCaseInsensitive) { var list = namedValues.Keys.Where(key => string.Equals(property.Name, key, StringComparison.OrdinalIgnoreCase)).ToList(); if (list.Count > 1) { throw new ODataException(Strings.UriParserMetadata_MultipleMatchingKeysFound(property.Name)); } else if (list.Count == 0) { throw ExceptionUtil.CreateSyntaxError(); } valueText = namedValues[list.Single()]; } else if (!namedValues.TryGetValue(property.Name, out valueText)) { throw ExceptionUtil.CreateSyntaxError(); } object convertedValue = convertFunc(property.Type, valueText); if (convertedValue == null) { throw ExceptionUtil.CreateSyntaxError(); } convertedPairs[property.Name] = convertedValue; } return convertedPairs; }
/// <summary> /// Resolve keys for certain entity set, this function would be called when key is specified as positional values. E.g. EntitySet('key') /// </summary> /// <param name="type">Type for current entityset.</param> /// <param name="positionalValues">The list of positional values.</param> /// <param name="convertFunc">The convert function to be used for value converting.</param> /// <returns>The resolved key list.</returns> public virtual IEnumerable<KeyValuePair<string, object>> ResolveKeys(IEdmEntityType type, IList<string> positionalValues, Func<IEdmTypeReference, string, object> convertFunc) { var keyProperties = type.Key().ToList(); var keyPairList = new List<KeyValuePair<string, object>>(positionalValues.Count); for (int i = 0; i < keyProperties.Count; i++) { string valueText = positionalValues[i]; IEdmProperty keyProperty = keyProperties[i]; object convertedValue = convertFunc(keyProperty.Type, valueText); if (convertedValue == null) { throw ExceptionUtil.CreateSyntaxError(); } keyPairList.Add(new KeyValuePair<string, object>(keyProperty.Name, convertedValue)); } return keyPairList; }
/// <summary> /// Binds key values to a key lookup on a collection. /// </summary> /// <param name="collectionNode">Already bound collection node.</param> /// <param name="namedValues">The named value tokens to bind.</param> /// <param name="model">The model to be used.</param> /// <param name="collectionItemEntityType">The type of a single item in a collection to apply the key value to.</param> /// <param name="keys">Dictionary of aliases to structural property names for the key.</param> /// <param name="keyLookupNode">The bound key lookup.</param> /// <returns>Returns true if binding succeeded.</returns> private bool TryBindToKeys(EntityCollectionNode collectionNode, IEnumerable<NamedValue> namedValues, IEdmModel model, IEdmEntityType collectionItemEntityType, IDictionary<string, IEdmProperty> keys, out QueryNode keyLookupNode) { List<KeyPropertyValue> keyPropertyValues = new List<KeyPropertyValue>(); HashSet<string> keyPropertyNames = new HashSet<string>(StringComparer.Ordinal); foreach (NamedValue namedValue in namedValues) { KeyPropertyValue keyPropertyValue; if (!this.TryBindKeyPropertyValue(namedValue, collectionItemEntityType, keys, out keyPropertyValue)) { keyLookupNode = null; return false; } Debug.Assert(keyPropertyValue != null, "keyPropertyValue != null"); Debug.Assert(keyPropertyValue.KeyProperty != null, "keyPropertyValue.KeyProperty != null"); if (!keyPropertyNames.Add(keyPropertyValue.KeyProperty.Name)) { throw new ODataException(ODataErrorStrings.MetadataBinder_DuplicitKeyPropertyInKeyValues(keyPropertyValue.KeyProperty.Name)); } keyPropertyValues.Add(keyPropertyValue); } if (keyPropertyValues.Count == 0) { // No key values specified, for example '/Customers()', do not include the key lookup at all keyLookupNode = collectionNode; return true; } else if (keyPropertyValues.Count != collectionItemEntityType.Key().Count()) { keyLookupNode = null; return false; } else { keyLookupNode = new KeyLookupNode(collectionNode, new ReadOnlyCollection<KeyPropertyValue>(keyPropertyValues)); return true; } }
/// <summary> /// Binds a key property value. /// </summary> /// <param name="namedValue">The named value to bind.</param> /// <param name="collectionItemEntityType">The type of a single item in a collection to apply the key value to.</param> /// <returns>The bound key property value node.</returns> private KeyPropertyValue BindKeyPropertyValue(NamedValue namedValue, IEdmEntityType collectionItemEntityType) { // These are exception checks because the data comes directly from the potentially user specified tree. ExceptionUtils.CheckArgumentNotNull(namedValue, "namedValue"); ExceptionUtils.CheckArgumentNotNull(namedValue.Value, "namedValue.Value"); Debug.Assert(collectionItemEntityType != null, "collectionItemType != null"); IEdmProperty keyProperty = null; if (namedValue.Name == null) { foreach (IEdmProperty p in collectionItemEntityType.Key()) { if (keyProperty == null) { keyProperty = p; } else { throw new ODataException(Strings.MetadataBinder_UnnamedKeyValueOnTypeWithMultipleKeyProperties(collectionItemEntityType.ODataFullName())); } } } else { keyProperty = collectionItemEntityType.Key().Where(k => string.CompareOrdinal(k.Name, namedValue.Name) == 0).SingleOrDefault(); if (keyProperty == null) { throw new ODataException(Strings.MetadataBinder_PropertyNotDeclaredOrNotKeyInKeyValue(namedValue.Name, collectionItemEntityType.ODataFullName())); } } IEdmTypeReference keyPropertyType = keyProperty.Type; SingleValueQueryNode value = (SingleValueQueryNode)this.Bind(namedValue.Value); // TODO: Check that the value is of primitive type value = ConvertToType(value, keyPropertyType); Debug.Assert(keyProperty != null, "keyProperty != null"); return new KeyPropertyValue() { KeyProperty = keyProperty, KeyValue = value }; }
private static IReadOnlyDictionary<string, object> GetPathKeyValues( KeyValuePathSegment keySegment, IEdmEntityType entityType) { var result = new Dictionary<string, object>(); var keys = entityType.Key(); // TODO GitHubIssue#42 : Improve key parsing logic // this parsing implementation does not allow key values to contain commas // Depending on the WebAPI to make KeyValuePathSegment.Values collection public // (or have the parsing logic public). string[] values = keySegment.Value.Split(EntityKeySeparator); if (values.Length > 1) { foreach (string value in values) { // Split key name and key value var keyValues = value.Split(EntityKeyNameValueSeparator); if (keyValues.Length != 2) { throw new InvalidOperationException(Resources.IncorrectKeyFormat); } // Validate the key name if (!keys.Select(k => k.Name).Contains(keyValues[0])) { throw new InvalidOperationException( string.Format( CultureInfo.InvariantCulture, Resources.KeyNotValidForEntityType, keyValues[0], entityType.Name)); } result.Add(keyValues[0], ODataUriUtils.ConvertFromUriLiteral(keyValues[1], ODataVersion.V4)); } } else { // We just have the single key value // Validate it has exactly one key if (keys.Count() > 1) { throw new InvalidOperationException(Resources.MultiKeyValuesExpected); } var keyName = keys.First().Name; result.Add(keyName, ODataUriUtils.ConvertFromUriLiteral(keySegment.Value, ODataVersion.V4)); } return result; }
/// <summary> /// Creates the key segment to use in the ID/EDitLink fields of an entry. /// </summary> /// <param name="entityInstance">The instance to get the key values from</param> /// <param name="entityType">The entity type of the instance</param> /// <param name="targetVersion">The OData version this segment is targeting.</param> /// <returns>A Key segment that contains a literal encoded string key-value pairs for property names and their values</returns> private static string BuildKeyString(object entityInstance, IEdmEntityType entityType, ODataVersion targetVersion) { if (entityType.DeclaredKey != null && entityType.DeclaredKey.Count() == 1) { var keyMember = entityType.Key().Single(); PropertyInfo property = entityInstance.GetType().GetProperty(keyMember.Name); return ODataUriUtils.ConvertToUriLiteral(property.GetValue(entityInstance, null), targetVersion, DataSourceManager.GetCurrentDataSource().Model); } else { var keyStringFragments = new List<string>(); foreach (var keyMember in entityType.Key()) { PropertyInfo property = entityInstance.GetType().GetProperty(keyMember.Name); keyStringFragments.Add(String.Format("{0}={1}", keyMember.Name, ODataUriUtils.ConvertToUriLiteral(property.GetValue(entityInstance, null), targetVersion, DataSourceManager.GetCurrentDataSource().Model))); } return String.Join(",", keyStringFragments); } }
private static KeySegment BuildKeySegment(IEdmEntityType entityType, IEdmEntitySetBase entitySet, object target) { if (entityType == null) { throw new ArgumentNullException("entityType"); } if (entitySet == null) { throw new ArgumentNullException("entitySet"); } if (target == null) { throw new ArgumentNullException("target"); } KeySegment keySegment = new KeySegment( entityType.Key().Select( (key) => { var keyValue = target.GetType().GetProperty(key.Name).GetValue(target, null); return new KeyValuePair<string, object>(key.Name, keyValue); }), entityType, entitySet); return keySegment; }
internal void WriteEntityType(IEdmEntityType entityType, Dictionary<IEdmStructuredType, List<IEdmOperation>> boundOperationsMap) { var entityTypeName = entityType.Name; entityTypeName = Context.EnableNamingAlias ? Customization.CustomizeNaming(entityTypeName) : entityTypeName; WriteSummaryCommentForStructuredType(entityTypeName + SingleSuffix); WriteStructurdTypeDeclaration(entityType, ClassInheritMarker + string.Format(DataServiceQuerySingleStructureTemplate, GetFixedName(entityTypeName)), SingleSuffix); var singleTypeName = (Context.EnableNamingAlias ? Customization.CustomizeNaming(entityType.Name) : entityType.Name) + SingleSuffix; WriteConstructorForSingleType(GetFixedName(singleTypeName), string.Format(DataServiceQuerySingleStructureTemplate, GetFixedName(entityTypeName))); var current = entityType; while (current != null) { WritePropertiesForSingleType(current.DeclaredProperties); current = (IEdmEntityType)current.BaseType; } WriteClassEndForStructuredType(); WriteSummaryCommentForStructuredType(Context.EnableNamingAlias ? Customization.CustomizeNaming(entityType.Name) : entityType.Name); if (entityType.Key().Any()) { var keyProperties = entityType.Key().Select(k => k.Name); WriteKeyPropertiesCommentAndAttribute( Context.EnableNamingAlias ? keyProperties.Select(k => Customization.CustomizeNaming(k)) : keyProperties, string.Join("\", \"", keyProperties)); } else { WriteEntityTypeAttribute(); } if (Context.UseDataServiceCollection) { List<IEdmNavigationSource> navigationSourceList; if (Context.ElementTypeToNavigationSourceMap.TryGetValue(entityType, out navigationSourceList)) { if (navigationSourceList.Count == 1) { WriteEntitySetAttribute(navigationSourceList[0].Name); } } } if (entityType.HasStream) { WriteEntityHasStreamAttribute(); } WriteStructurdTypeDeclaration(entityType, BaseEntityType); SetPropertyIdentifierMappingsIfNameConflicts(entityType.Name, entityType); WriteTypeStaticCreateMethod(entityType.Name, entityType); WritePropertiesForStructuredType(entityType.DeclaredProperties); if (entityType.BaseType == null && Context.UseDataServiceCollection) { WriteINotifyPropertyChangedImplementation(); } WriteBoundOperations(entityType, boundOperationsMap); WriteClassEndForStructuredType(); }