private DataServiceInfo GetDataServiceInfo <T>() { Type type = typeof(T); if (this.dataServiceInfoDictionary.ContainsKey(type)) { return(this.dataServiceInfoDictionary[type]); } DataServiceInfo info = new DataServiceInfo(); object[] attribs = type.GetCustomAttributes(typeof(DataServiceEntitySetNameAttribute), true); if (attribs.Length > 0) { info.EntitySetName = ((DataServiceEntitySetNameAttribute)attribs[0]).EntitySetName; } attribs = type.GetCustomAttributes(typeof(DataServiceKeyAttribute), true); if (attribs.Length > 0) { DataServiceKeyAttribute attribute = (DataServiceKeyAttribute)attribs[0]; if (attribute.KeyNames.Count > 1) { throw new NotImplementedException("Multiple KeyNames"); } info.Key = attribute.KeyNames[0]; } this.dataServiceInfoDictionary.Add(type, info); return(info); }
/// <summary> /// Returns the KeyKind if <paramref name="propertyInfo"/> is declared as a key in <paramref name="dataServiceKeyAttribute"/> or it follows the key naming convension. /// </summary> /// <param name="propertyInfo">Property in question.</param> /// <param name="dataServiceKeyAttribute">DataServiceKeyAttribute instance.</param> /// <returns>Returns the KeyKind if <paramref name="propertyInfo"/> is declared as a key in <paramref name="dataServiceKeyAttribute"/> or it follows the key naming convension.</returns> private static KeyKind IsKeyProperty(PropertyInfo propertyInfo, DataServiceKeyAttribute dataServiceKeyAttribute) { Debug.Assert(propertyInfo != null, "propertyInfo != null"); string propertyName = propertyInfo.Name; KeyKind keyKind = KeyKind.NotKey; if (dataServiceKeyAttribute != null && dataServiceKeyAttribute.KeyNames.Contains(propertyName)) { keyKind = KeyKind.AttributedKey; } else if (propertyName.EndsWith("ID", StringComparison.Ordinal)) { string declaringTypeName = propertyInfo.DeclaringType.Name; if ((propertyName.Length == (declaringTypeName.Length + 2)) && propertyName.StartsWith(declaringTypeName, StringComparison.Ordinal)) { // matched "DeclaringType.Name+ID" pattern keyKind = KeyKind.TypeNameId; } else if (2 == propertyName.Length) { // matched "ID" pattern keyKind = KeyKind.Id; } } return(keyKind); }
private static KeyKind IsKeyProperty(PropertyInfo propertyInfo, DataServiceKeyAttribute dataServiceKeyAttribute) { string name = propertyInfo.Name; KeyKind notKey = KeyKind.NotKey; if ((dataServiceKeyAttribute != null) && dataServiceKeyAttribute.KeyNames.Contains(name)) { return(KeyKind.AttributedKey); } if (name.EndsWith("ID", StringComparison.Ordinal)) { string str2 = propertyInfo.DeclaringType.Name; if ((name.Length == (str2.Length + 2)) && name.StartsWith(str2, StringComparison.Ordinal)) { return(KeyKind.TypeNameId); } if (2 == name.Length) { notKey = KeyKind.Id; } } return(notKey); }
internal static bool IsPropertyKeyProperty(PropertyInfo property, out ResourceKeyKind keyKind) { keyKind = ~ResourceKeyKind.AttributedKey; if (WebUtil.IsPrimitiveType(property.PropertyType) && !property.PropertyType.IsGenericType) { DataServiceKeyAttribute attribute = property.ReflectedType.GetCustomAttributes(true).OfType <DataServiceKeyAttribute>().FirstOrDefault <DataServiceKeyAttribute>(); if ((attribute != null) && attribute.KeyNames.Contains(property.Name)) { keyKind = ResourceKeyKind.AttributedKey; return(true); } if (property.Name == (property.DeclaringType.Name + "ID")) { keyKind = ResourceKeyKind.TypeNameId; return(true); } if (property.Name == "ID") { keyKind = ResourceKeyKind.Id; return(true); } } return(false); }
internal static PropertyInfo[] GetKeyPropertiesOnType(Type type, out bool hasProperties) { Func <string, bool> predicate = null; if (CommonUtil.IsUnsupportedType(type)) { throw new InvalidOperationException(System.Data.Services.Client.Strings.ClientType_UnsupportedType(type)); } string str = type.ToString(); IEnumerable <object> customAttributes = type.GetCustomAttributes(true); bool flag = customAttributes.OfType <DataServiceEntityAttribute>().Any <DataServiceEntityAttribute>(); DataServiceKeyAttribute dataServiceKeyAttribute = customAttributes.OfType <DataServiceKeyAttribute>().FirstOrDefault <DataServiceKeyAttribute>(); List <PropertyInfo> list = new List <PropertyInfo>(); PropertyInfo[] properties = GetPropertiesOnType(type, false).ToArray <PropertyInfo>(); hasProperties = properties.Length > 0; KeyKind notKey = KeyKind.NotKey; KeyKind kind2 = KeyKind.NotKey; foreach (PropertyInfo info in properties) { if ((kind2 = IsKeyProperty(info, dataServiceKeyAttribute)) != KeyKind.NotKey) { if (kind2 > notKey) { list.Clear(); notKey = kind2; list.Add(info); } else if (kind2 == notKey) { list.Add(info); } } } Type declaringType = null; foreach (PropertyInfo info2 in list) { if (null == declaringType) { declaringType = info2.DeclaringType; } else if (declaringType != info2.DeclaringType) { throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.ClientType_KeysOnDifferentDeclaredType(str)); } if (!PrimitiveType.IsKnownType(info2.PropertyType)) { throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.ClientType_KeysMustBeSimpleTypes(str)); } } if ((kind2 == KeyKind.AttributedKey) && (list.Count != dataServiceKeyAttribute.KeyNames.Count)) { if (predicate == null) { predicate = a => null == (from b in properties where b.Name == a select b).FirstOrDefault <PropertyInfo>(); } string str2 = dataServiceKeyAttribute.KeyNames.Cast <string>().Where <string>(predicate).First <string>(); throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.ClientType_MissingProperty(str, str2)); } if (list.Count > 0) { return(list.ToArray()); } if (!flag) { return(null); } return(EmptyPropertyInfoArray); }
private ClientType(Type type, string typeName, bool skipSettableCheck) { Debug.Assert(null != type, "null type"); Debug.Assert(!String.IsNullOrEmpty(typeName), "empty typeName"); this.ElementTypeName = typeName; this.ElementType = Nullable.GetUnderlyingType(type) ?? type; #if ASTORIA_OPEN_OBJECT string openObjectPropertyName = null; #endif if (!ClientConvert.IsKnownType(this.ElementType)) { #if ASTORIA_OPEN_OBJECT #region OpenObject determined by walking type hierarchy and looking for [OpenObjectAttribute("PropertyName")] Type openObjectDeclared = this.ElementType; for (Type tmp = openObjectDeclared; (null != tmp) && (typeof(object) != tmp); tmp = tmp.BaseType) { object[] attributes = openObjectDeclared.GetCustomAttributes(typeof(OpenObjectAttribute), false); if (1 == attributes.Length) { if (null != openObjectPropertyName) { throw Error.InvalidOperation(Strings.Clienttype_MultipleOpenProperty(this.ElementTypeName)); } openObjectPropertyName = ((OpenObjectAttribute)attributes[0]).OpenObjectPropertyName; openObjectDeclared = tmp; } } #endregion #endif Type keyPropertyDeclaredType = null; bool isEntity = type.GetCustomAttributes(true).OfType <DataServiceEntityAttribute>().Any(); DataServiceKeyAttribute dska = type.GetCustomAttributes(true).OfType <DataServiceKeyAttribute>().FirstOrDefault(); foreach (PropertyInfo pinfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { Type ptype = pinfo.PropertyType; ptype = Nullable.GetUnderlyingType(ptype) ?? ptype; if (ptype.IsPointer || (ptype.IsArray && (typeof(byte[]) != ptype) && typeof(char[]) != ptype) || (typeof(IntPtr) == ptype) || (typeof(UIntPtr) == ptype)) { continue; } Debug.Assert(!ptype.ContainsGenericParameters, "remove when test case is found that encounters this"); if (pinfo.CanRead && (!ptype.IsValueType || pinfo.CanWrite) && !ptype.ContainsGenericParameters && (0 == pinfo.GetIndexParameters().Length)) { #region IsKey? bool keyProperty = dska != null?dska.KeyNames.Contains(pinfo.Name) : false; if (keyProperty) { if (null == keyPropertyDeclaredType) { keyPropertyDeclaredType = pinfo.DeclaringType; } else if (keyPropertyDeclaredType != pinfo.DeclaringType) { throw Error.InvalidOperation(Strings.ClientType_KeysOnDifferentDeclaredType(this.ElementTypeName)); } if (!ClientConvert.IsKnownType(ptype)) { throw Error.InvalidOperation(Strings.ClientType_KeysMustBeSimpleTypes(this.ElementTypeName)); } this.KeyCount++; } #endregion #if ASTORIA_OPEN_OBJECT #region IsOpenObjectProperty? bool openProperty = (openObjectPropertyName == pinfo.Name) && typeof(IDictionary <string, object>).IsAssignableFrom(ptype); Debug.Assert(keyProperty != openProperty || (!keyProperty && !openProperty), "key can't be open type"); #endregion ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty, openProperty); if (!property.OpenObjectProperty) #else ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty); #endif { if (!this.properties.Add(property, ClientProperty.NameEquality)) { int shadow = this.IndexOfProperty(property.PropertyName); if (!property.DeclaringType.IsAssignableFrom(this.properties[shadow].DeclaringType)) { this.properties.RemoveAt(shadow); this.properties.Add(property, null); } } } #if ASTORIA_OPEN_OBJECT else { if (pinfo.DeclaringType == openObjectDeclared) { this.openProperties = property; } } #endif } } #region No KeyAttribute, discover key by name pattern { DeclaringType.Name+ID, ID } if (null == keyPropertyDeclaredType) { ClientProperty key = null; for (int i = this.properties.Count - 1; 0 <= i; --i) { string propertyName = this.properties[i].PropertyName; if (propertyName.EndsWith("ID", StringComparison.Ordinal)) { string declaringTypeName = this.properties[i].DeclaringType.Name; if ((propertyName.Length == (declaringTypeName.Length + 2)) && propertyName.StartsWith(declaringTypeName, StringComparison.Ordinal)) { if ((null == keyPropertyDeclaredType) || this.properties[i].DeclaringType.IsAssignableFrom(keyPropertyDeclaredType)) { keyPropertyDeclaredType = this.properties[i].DeclaringType; key = this.properties[i]; } } else if ((null == keyPropertyDeclaredType) && (2 == propertyName.Length)) { keyPropertyDeclaredType = this.properties[i].DeclaringType; key = this.properties[i]; } } } if (null != key) { Debug.Assert(0 == this.KeyCount, "shouldn't have a key yet"); key.KeyProperty = true; this.KeyCount++; } } else if (this.KeyCount != dska.KeyNames.Count) { var m = (from string a in dska.KeyNames where null == (from b in this.properties where b.PropertyName == a select b).FirstOrDefault() select a).First <string>(); throw Error.InvalidOperation(Strings.ClientType_MissingProperty(this.ElementTypeName, m)); } #endregion this.IsEntityType = (null != keyPropertyDeclaredType) || isEntity; Debug.Assert(this.KeyCount == this.Properties.Where(k => k.KeyProperty).Count(), "KeyCount mismatch"); this.WireUpMimeTypeProperties(); this.CheckMediaLinkEntry(); if (!skipSettableCheck) { #if ASTORIA_OPEN_OBJECT if ((0 == this.properties.Count) && (null == this.openProperties)) #else if (0 == this.properties.Count) #endif { throw Error.InvalidOperation(Strings.ClientType_NoSettableFields(this.ElementTypeName)); } } }
/// <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 <DataServiceEntityAttribute>().Any(); DataServiceKeyAttribute dataServiceKeyAttribute = customAttributes.OfType <DataServiceKeyAttribute>().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 (null == keyPropertyDeclaringType) { keyPropertyDeclaringType = key.DeclaringType; } else if (keyPropertyDeclaringType != key.DeclaringType) { throw c.Error.InvalidOperation(c.Strings.ClientType_KeysOnDifferentDeclaredType(typeName)); } if (!PrimitiveType.IsKnownType(key.PropertyType)) { throw c.Error.InvalidOperation(c.Strings.ClientType_KeysMustBeSimpleTypes(typeName)); } } if (newKeyKind == KeyKind.AttributedKey && keyProperties.Count != dataServiceKeyAttribute.KeyNames.Count) { var m = (from string a in dataServiceKeyAttribute.KeyNames where null == (from b in properties where b.Name == a select b).FirstOrDefault() 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> /// discover and prepare properties for usage /// </summary> /// <param name="type">type being processed</param> /// <param name="typeName">parameter name</param> /// <param name="skipSettableCheck">Whether the skip the check for settable properties.</param> private ClientType(Type type, string typeName, bool skipSettableCheck) { Debug.Assert(null != type, "null type"); Debug.Assert(!String.IsNullOrEmpty(typeName), "empty typeName"); this.ElementTypeName = typeName; this.ElementType = Nullable.GetUnderlyingType(type) ?? type; #if ASTORIA_OPEN_OBJECT string openObjectPropertyName = null; #endif if (!ClientConvert.IsKnownType(this.ElementType)) { #if ASTORIA_OPEN_OBJECT #region OpenObject determined by walking type hierarchy and looking for [OpenObjectAttribute("PropertyName")] Type openObjectDeclared = this.ElementType; for (Type tmp = openObjectDeclared; (null != tmp) && (typeof(object) != tmp); tmp = tmp.BaseType) { object[] attributes = openObjectDeclared.GetCustomAttributes(typeof(OpenObjectAttribute), false); if (1 == attributes.Length) { if (null != openObjectPropertyName) { throw Error.InvalidOperation(Strings.Clienttype_MultipleOpenProperty(this.ElementTypeName)); } openObjectPropertyName = ((OpenObjectAttribute)attributes[0]).OpenObjectPropertyName; openObjectDeclared = tmp; } } #endregion #endif Type keyPropertyDeclaredType = null; bool isEntity = type.GetCustomAttributes(true).OfType <DataServiceEntityAttribute>().Any(); DataServiceKeyAttribute dska = type.GetCustomAttributes(true).OfType <DataServiceKeyAttribute>().FirstOrDefault(); foreach (PropertyInfo pinfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { //// examples where class<PropertyType> //// the normal examples //// PropertyType Property { get; set } //// Nullable<PropertyType> Property { get; set; } //// if 'Property: struct' then we would be unable set the property during construction (and have them stick) //// but when its a class, we can navigate if non-null and set the nested properties //// PropertyType Property { get; } where PropertyType: class //// we do support adding elements to collections //// ICollection<PropertyType> { get; /*ignored set;*/ } //// indexed properties are not suported because //// we don't have anything to use as the index //// PropertyType Property[object x] { /*ignored get;*/ /*ignored set;*/ } //// also ignored //// if PropertyType.IsPointer (like byte*) //// if PropertyType.IsArray except for byte[] and char[] //// if PropertyType == IntPtr or UIntPtr Type ptype = pinfo.PropertyType; // class / interface / value ptype = Nullable.GetUnderlyingType(ptype) ?? ptype; if (ptype.IsPointer || (ptype.IsArray && (typeof(byte[]) != ptype) && typeof(char[]) != ptype) || (typeof(IntPtr) == ptype) || (typeof(UIntPtr) == ptype)) { continue; } Debug.Assert(!ptype.ContainsGenericParameters, "remove when test case is found that encounters this"); if (pinfo.CanRead && (!ptype.IsValueType || pinfo.CanWrite) && !ptype.ContainsGenericParameters && (0 == pinfo.GetIndexParameters().Length)) { #region IsKey? bool keyProperty = dska != null?dska.KeyNames.Contains(pinfo.Name) : false; if (keyProperty) { if (null == keyPropertyDeclaredType) { keyPropertyDeclaredType = pinfo.DeclaringType; } else if (keyPropertyDeclaredType != pinfo.DeclaringType) { throw Error.InvalidOperation(Strings.ClientType_KeysOnDifferentDeclaredType(this.ElementTypeName)); } if (!ClientConvert.IsKnownType(ptype)) { throw Error.InvalidOperation(Strings.ClientType_KeysMustBeSimpleTypes(this.ElementTypeName)); } this.KeyCount++; } #endregion #if ASTORIA_OPEN_OBJECT #region IsOpenObjectProperty? bool openProperty = (openObjectPropertyName == pinfo.Name) && typeof(IDictionary <string, object>).IsAssignableFrom(ptype); Debug.Assert(keyProperty != openProperty || (!keyProperty && !openProperty), "key can't be open type"); #endregion ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty, openProperty); if (!property.OpenObjectProperty) #else ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty); #endif { if (!this.properties.Add(property, ClientProperty.NameEquality)) { // 2nd property with same name shadows another property int shadow = this.IndexOfProperty(property.PropertyName); if (!property.DeclaringType.IsAssignableFrom(this.properties[shadow].DeclaringType)) { // the new property is on the most derived class this.properties.RemoveAt(shadow); this.properties.Add(property, null); } } } #if ASTORIA_OPEN_OBJECT else { if (pinfo.DeclaringType == openObjectDeclared) { this.openProperties = property; } } #endif } } #region No KeyAttribute, discover key by name pattern { DeclaringType.Name+ID, ID } if (null == keyPropertyDeclaredType) { ClientProperty key = null; for (int i = this.properties.Count - 1; 0 <= i; --i) { string propertyName = this.properties[i].PropertyName; if (propertyName.EndsWith("ID", StringComparison.Ordinal)) { string declaringTypeName = this.properties[i].DeclaringType.Name; if ((propertyName.Length == (declaringTypeName.Length + 2)) && propertyName.StartsWith(declaringTypeName, StringComparison.Ordinal)) { // matched "DeclaringType.Name+ID" pattern if ((null == keyPropertyDeclaredType) || this.properties[i].DeclaringType.IsAssignableFrom(keyPropertyDeclaredType)) { keyPropertyDeclaredType = this.properties[i].DeclaringType; key = this.properties[i]; } } else if ((null == keyPropertyDeclaredType) && (2 == propertyName.Length)) { // matched "ID" pattern keyPropertyDeclaredType = this.properties[i].DeclaringType; key = this.properties[i]; } } } if (null != key) { Debug.Assert(0 == this.KeyCount, "shouldn't have a key yet"); key.KeyProperty = true; this.KeyCount++; } } else if (this.KeyCount != dska.KeyNames.Count) { var m = (from string a in dska.KeyNames where null == (from b in this.properties where b.PropertyName == a select b).FirstOrDefault() select a).First <string>(); throw Error.InvalidOperation(Strings.ClientType_MissingProperty(this.ElementTypeName, m)); } #endregion this.IsEntityType = (null != keyPropertyDeclaredType) || isEntity; Debug.Assert(this.KeyCount == this.Properties.Where(k => k.KeyProperty).Count(), "KeyCount mismatch"); this.WireUpMimeTypeProperties(); this.CheckMediaLinkEntry(); if (!skipSettableCheck) { #if ASTORIA_OPEN_OBJECT if ((0 == this.properties.Count) && (null == this.openProperties)) #else if (0 == this.properties.Count) #endif { // implicit construction? throw Error.InvalidOperation(Strings.ClientType_NoSettableFields(this.ElementTypeName)); } } }