/// <summary>Gets a delegate that can be invoked to add an item to a collection of the specified type.</summary> /// <param name='listType'>Type of list to use.</param> /// <returns>The delegate to invoke.</returns> internal static Action <object, object> GetAddToCollectionDelegate(Type listType) { Debug.Assert(listType != null, "listType != null"); Type listElementType; MethodInfo addMethod = ClientTypeUtil.GetAddToCollectionMethod(listType, out listElementType); ParameterExpression list = Expression.Parameter(typeof(object), "list"); ParameterExpression item = Expression.Parameter(typeof(object), "element"); Expression body = Expression.Call(Expression.Convert(list, listType), addMethod, Expression.Convert(item, listElementType)); LambdaExpression lambda = Expression.Lambda(body, list, item); return((Action <object, object>)lambda.Compile()); }
/// <summary> /// Get the enum string split by "," with their server side names /// </summary> /// <param name="enumString">enum string with names in client side</param> /// <param name="enumType">the source enum type</param> /// <returns>The enum string split by "," with their server side names</returns> internal static string GetEnumValuesString(string enumString, Type enumType) { string[] enums = enumString.Split(',').Select(v => v.Trim()).ToArray(); List <string> memberValues = new List <string>(); foreach (var enumValue in enums) { MemberInfo member = enumType.GetField(enumValue); if (member == null) { throw new NotSupportedException(Strings.Serializer_InvalidEnumMemberValue(enumType.Name, enumValue)); } memberValues.Add(ClientTypeUtil.GetServerDefinedName(member)); } return(string.Join(",", memberValues)); }
/// <summary> /// get property wrapper for a property name, might be method around open types for otherwise unknown properties /// </summary> /// <param name="propertyName">property name</param> /// <param name="undeclaredPropertyBehavior">UndeclaredPropertyBehavior</param> /// <returns>property wrapper</returns> /// <exception cref="InvalidOperationException">for unknown properties on closed types</exception> internal ClientPropertyAnnotation GetProperty(string propertyName, UndeclaredPropertyBehavior undeclaredPropertyBehavior) { Debug.Assert(propertyName != null, "property name"); if (this.clientPropertyCache == null) { this.BuildPropertyCache(); } ClientPropertyAnnotation property; if (!this.clientPropertyCache.TryGetValue(propertyName, out property)) { string propertyClientName = ClientTypeUtil.GetClientPropertyName(this.ElementType, propertyName, undeclaredPropertyBehavior); if ((string.IsNullOrEmpty(propertyClientName) || !this.clientPropertyCache.TryGetValue(propertyClientName, out property)) && (undeclaredPropertyBehavior == UndeclaredPropertyBehavior.ThrowException)) { throw Microsoft.OData.Client.Error.InvalidOperation(Microsoft.OData.Client.Strings.ClientType_MissingProperty(this.ElementTypeName, propertyName)); } } return(property); }
/// <summary> /// get property wrapper for a property name, might be method around open types for otherwise unknown properties /// </summary> /// <param name="propertyName">property name</param> /// <param name="ignoreMissingProperties">are missing properties ignored</param> /// <returns>property wrapper</returns> /// <exception cref="InvalidOperationException">for unknown properties on closed types</exception> internal ClientPropertyAnnotation GetProperty(string propertyName, bool ignoreMissingProperties) { Debug.Assert(propertyName != null, "property name"); if (this.clientPropertyCache == null) { this.BuildPropertyCache(); } ClientPropertyAnnotation property; if (!this.clientPropertyCache.TryGetValue(propertyName, out property)) { string propertyClientName = ClientTypeUtil.GetClientPropertyName(this.ElementType, propertyName, ignoreMissingProperties); if ((string.IsNullOrEmpty(propertyClientName) || !this.clientPropertyCache.TryGetValue(propertyClientName, out property)) && !ignoreMissingProperties) { throw Microsoft.OData.Client.Error.InvalidOperation(Microsoft.OData.Client.Strings.ClientType_MissingProperty(this.ElementTypeName, propertyName)); } } return(property); }
/// <summary> /// Returns MethodInfo instance for a generic type retrieved by using <paramref name="methodName"/> and gets /// element type for the provided <paramref name="genericTypeDefinition"/>. /// </summary> /// <param name="propertyType">starting type</param> /// <param name="genericTypeDefinition">the generic type definition to find</param> /// <param name="methodName">the method to search for</param> /// <param name="type">the element type for <paramref name="genericTypeDefinition" /> if found</param> /// <returns>element types</returns> internal static MethodInfo GetMethodForGenericType(Type propertyType, Type genericTypeDefinition, string methodName, out Type type) { Debug.Assert(propertyType != null, "null propertyType"); Debug.Assert(genericTypeDefinition != null, "null genericTypeDefinition"); Debug.Assert(genericTypeDefinition.IsGenericTypeDefinition(), "!IsGenericTypeDefinition"); type = null; Type implementationType = ClientTypeUtil.GetImplementationType(propertyType, genericTypeDefinition); if (implementationType != null) { Type[] genericArguments = implementationType.GetGenericArguments(); MethodInfo methodInfo = implementationType.GetMethod(methodName); Debug.Assert(methodInfo != null, "should have found the method"); #if DEBUG Debug.Assert(genericArguments != null, "null genericArguments"); ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length > 0) { // following assert was disabled for Contains which returns bool //// Debug.Assert(typeof(void) == methodInfo.ReturnParameter.ParameterType, "method doesn't return void"); Debug.Assert(genericArguments.Length == parameters.Length, "genericArguments don't match parameters"); for (int i = 0; i < genericArguments.Length; ++i) { Debug.Assert(genericArguments[i] == parameters[i].ParameterType, "parameter doesn't match generic argument"); } } #endif type = genericArguments[genericArguments.Length - 1]; return(methodInfo); } return(null); }
/// <summary> /// Returns the list of key properties defined on <paramref name="type"/>; null if <paramref name="type"/> is complex. /// </summary> /// <param name="type">Type in question.</param> /// <param name="hasProperties">true if <paramref name="type"/> has any (declared or inherited) properties; otherwise false.</param> /// <returns>Returns the list of key properties defined on <paramref name="type"/>; null if <paramref name="type"/> is complex.</returns> internal static PropertyInfo[] GetKeyPropertiesOnType(Type type, out bool hasProperties) { if (CommonUtil.IsUnsupportedType(type)) { throw new InvalidOperationException(c.Strings.ClientType_UnsupportedType(type)); } string typeName = type.ToString(); IEnumerable <object> customAttributes = type.GetCustomAttributes(true); bool isEntity = customAttributes.OfType <EntityTypeAttribute>().Any(); KeyAttribute dataServiceKeyAttribute = customAttributes.OfType <KeyAttribute>().FirstOrDefault(); List <PropertyInfo> keyProperties = new List <PropertyInfo>(); PropertyInfo[] properties = ClientTypeUtil.GetPropertiesOnType(type, false /*declaredOnly*/).ToArray(); hasProperties = properties.Length > 0; KeyKind currentKeyKind = KeyKind.NotKey; KeyKind newKeyKind = KeyKind.NotKey; foreach (PropertyInfo propertyInfo in properties) { if ((newKeyKind = ClientTypeUtil.IsKeyProperty(propertyInfo, dataServiceKeyAttribute)) != KeyKind.NotKey) { if (newKeyKind > currentKeyKind) { keyProperties.Clear(); currentKeyKind = newKeyKind; keyProperties.Add(propertyInfo); } else if (newKeyKind == currentKeyKind) { keyProperties.Add(propertyInfo); } } } Type keyPropertyDeclaringType = null; foreach (PropertyInfo key in keyProperties) { if (keyPropertyDeclaringType == null) { keyPropertyDeclaringType = key.DeclaringType; } else if (keyPropertyDeclaringType != key.DeclaringType) { throw c.Error.InvalidOperation(c.Strings.ClientType_KeysOnDifferentDeclaredType(typeName)); } if (!PrimitiveType.IsKnownType(key.PropertyType) && !(key.PropertyType.GetGenericTypeDefinition() == typeof(System.Nullable <>) && key.PropertyType.GetGenericArguments().First().IsEnum())) { throw c.Error.InvalidOperation(c.Strings.ClientType_KeysMustBeSimpleTypes(key.Name, typeName, key.PropertyType.FullName)); } } if (newKeyKind == KeyKind.AttributedKey && keyProperties.Count != dataServiceKeyAttribute?.KeyNames.Count) { var m = (from string a in dataServiceKeyAttribute.KeyNames where (from b in properties where b.Name == a select b).FirstOrDefault() == null select a).First <string>(); throw c.Error.InvalidOperation(c.Strings.ClientType_MissingProperty(typeName, m)); } return(keyProperties.Count > 0 ? keyProperties.ToArray() : (isEntity ? ClientTypeUtil.EmptyPropertyInfoArray : null)); }
/// <summary> /// Is the type or element type (in the case of nullableOfT or IEnumOfT) a Entity Type? /// </summary> /// <param name="type">Type to examine</param> /// <returns>bool indicating whether or not entity type</returns> internal static bool TypeOrElementTypeIsEntity(Type type) { type = TypeSystem.GetElementType(type); type = Nullable.GetUnderlyingType(type) ?? type; return(!PrimitiveType.IsKnownType(type) && ClientTypeUtil.GetKeyPropertiesOnType(type) != null); }
/// <summary> /// Gets the Add method to add items to a collection of the specified type. /// </summary> /// <param name="collectionType">Type for the collection.</param> /// <param name="type">The element type in the collection if found; null otherwise.</param> /// <returns>The method to invoke to add to a collection of the specified type.</returns> internal static MethodInfo GetAddToCollectionMethod(Type collectionType, out Type type) { return(ClientTypeUtil.GetMethodForGenericType(collectionType, typeof(ICollection <>), "Add", out type)); }
/// <summary> /// constructor /// </summary> /// <param name="edmProperty">Back reference to the EdmProperty this annotation is part of.</param> /// <param name="propertyInfo">propertyInfo instance.</param> /// <param name="model">The client model.</param> internal ClientPropertyAnnotation(IEdmProperty edmProperty, PropertyInfo propertyInfo, ClientEdmModel model) { Debug.Assert(edmProperty != null, "edmProperty != null"); Debug.Assert(propertyInfo != null, "null propertyInfo"); // Property should always have DeclaringType Debug.Assert(propertyInfo.DeclaringType != null, "Property without a declaring type"); this.EdmProperty = edmProperty; this.PropertyName = ClientTypeUtil.GetServerDefinedName(propertyInfo); this.NullablePropertyType = propertyInfo.PropertyType; this.PropertyType = Nullable.GetUnderlyingType(this.NullablePropertyType) ?? this.NullablePropertyType; this.DeclaringClrType = propertyInfo.DeclaringType; MethodInfo propertyGetMethod = propertyInfo.GetGetMethod(); MethodInfo propertySetMethod = propertyInfo.GetSetMethod(); ParameterExpression instance = Expression.Parameter(typeof(Object), "instance"); ParameterExpression value = Expression.Parameter(typeof(Object), "value"); // instance => (Object)(((T)instance).get_PropertyName()); <-- we need to box the value back to object to return this.propertyGetter = propertyGetMethod == null ? null : (Func <object, object>)Expression.Lambda( Expression.Convert( Expression.Call( Expression.Convert(instance, this.DeclaringClrType), propertyGetMethod), typeof(Object)), instance).Compile(); // (instance, value) => ((T)instance).set_PropertyName((T1)value); this.propertySetter = propertySetMethod == null ? null : (Action <object, object>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.DeclaringClrType), propertySetMethod, Expression.Convert(value, this.NullablePropertyType)), instance, value).Compile(); this.Model = model; this.IsKnownType = PrimitiveType.IsKnownType(this.PropertyType); // non primitive types: dictionary/collections if (!this.IsKnownType) { var setMethodInfo = ClientTypeUtil.GetMethodForGenericType(this.PropertyType, typeof(IDictionary <,>), "set_Item", out this.DictionaryValueType); if (setMethodInfo != null) { ParameterExpression propertyNameParam = Expression.Parameter(typeof(String), "propertyName"); // (instance, propertyName, value) => ((IDictionary<string, DictionaryValueType>)instance)[propertyName] = (DictionaryValueType)value; this.dictionarySetter = (Action <Object, String, Object>)Expression.Lambda( Expression.Call( Expression.Convert(instance, typeof(IDictionary <,>).MakeGenericType(typeof(String), this.DictionaryValueType)), setMethodInfo, propertyNameParam, Expression.Convert(value, this.DictionaryValueType)), instance, propertyNameParam, value).Compile(); } else { var containsMethod = ClientTypeUtil.GetMethodForGenericType(this.PropertyType, typeof(ICollection <>), "Contains", out this.collectionGenericType); var addMethod = ClientTypeUtil.GetAddToCollectionMethod(this.PropertyType, out this.collectionGenericType); var removeMethod = ClientTypeUtil.GetMethodForGenericType(this.PropertyType, typeof(ICollection <>), "Remove", out this.collectionGenericType); var clearMethod = ClientTypeUtil.GetMethodForGenericType(this.PropertyType, typeof(ICollection <>), "Clear", out this.collectionGenericType); // (instance, value) => ((PropertyType)instance).Contains((CollectionType)value); returns boolean this.collectionContains = containsMethod == null ? null : (Func <Object, Object, Boolean>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.PropertyType), containsMethod, Expression.Convert(value, this.collectionGenericType)), instance, value).Compile(); // (instance, value) => ((PropertyType)instance).Add((CollectionType)value); this.collectionAdd = addMethod == null ? null : (Action <Object, Object>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.PropertyType), addMethod, Expression.Convert(value, this.collectionGenericType)), instance, value).Compile(); // (instance, value) => ((PropertyType)instance).Remove((CollectionType)value); returns boolean this.collectionRemove = removeMethod == null ? null : (Func <Object, Object, Boolean>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.PropertyType), removeMethod, Expression.Convert(value, this.collectionGenericType)), instance, value).Compile(); // (instance) => ((PropertyType)instance).Clear(); this.collectionClear = clearMethod == null ? null : (Action <Object>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.PropertyType), clearMethod), instance).Compile(); } } Debug.Assert(this.collectionGenericType == null || this.DictionaryValueType == null, "collectionGenericType and DictionaryItemType mutually exclusive. (They both can be null though)."); }