From 434ea5b851d62eee1d85800023d1917e84113b60 Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Mon, 9 Jul 2018 13:22:16 +0300 Subject: [PATCH 01/11] Add support navigation property as extension method --- .../DataContractAttributeEdmTypeConvention.cs | 2 +- .../ForeignKeyAttributeConvention.cs | 4 +- ...ataMemberAttributeEdmPropertyConvention.cs | 2 +- .../KeyAttributeEdmPropertyConvention.cs | 2 +- ...NotMappedAttributeEdmPropertyConvention.cs | 2 +- .../Conventions/EntityKeyConvention.cs | 2 +- .../Builder/EdmModelHelperMethods.cs | 13 +- .../Builder/EdmTypeBuilder.cs | 8 +- .../Builder/EdmTypeMap.cs | 4 +- .../EntityTypeConfigurationOfTEntityType.cs | 2 +- .../NavigationPropertyConfiguration.cs | 43 ++++- .../Builder/PrimitivePropertyConfiguration.cs | 3 +- .../Builder/PropertyConfiguration.cs | 4 +- .../Builder/PropertyDescriptor.cs | 174 ++++++++++++++++++ .../Builder/PropertySelectorVisitor.cs | 58 +++++- .../StructuralPropertyConfiguration.cs | 2 +- .../Builder/StructuralTypeConfiguration.cs | 68 ++++++- ...turalTypeConfigurationOfTStructuralType.cs | 18 +- .../ClrPropertyInfoAnnotation.cs | 19 +- .../Formatter/EdmLibHelpers.cs | 27 ++- .../Microsoft.AspNet.OData.Shared.projitems | 1 + .../Query/Expressions/SelectExpandBinder.cs | 18 +- .../SelectExpandWrapperConverter.cs | 9 +- .../CollectionPropertyConfigurationTest.cs | 4 +- .../AttributeEdmPropertyConventionTests.cs | 7 +- ...ContractAttributeEdmTypeConventionTests.cs | 10 +- ...mberAttributeEdmPropertyConventionTests.cs | 11 +- .../NotMappedAttributeConventionTests.cs | 7 +- ...tampAttributeEdmPropertyConventionTests.cs | 14 +- .../Conventions/EntityKeyConventionTests.cs | 10 +- .../EdmTypeConfigurationExtensionsTest.cs | 28 +-- .../Builder/PropertyConfigurationTest.cs | 7 +- .../Common/MockPropertyInfo.cs | 6 + .../Microsoft.AspNetCore.OData.PublicApi.bsl | 33 +++- 34 files changed, 520 insertions(+), 102 deletions(-) create mode 100644 src/Microsoft.AspNet.OData.Shared/Builder/PropertyDescriptor.cs diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConvention.cs b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConvention.cs index 642581dded..840b076f7f 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConvention.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConvention.cs @@ -65,7 +65,7 @@ public override void Apply(StructuralTypeConfiguration edmTypeConfiguration, ODa { if (!property.AddedExplicitly) { - edmTypeConfiguration.RemoveProperty(property.PropertyInfo); + edmTypeConfiguration.RemoveProperty(property.PropertyInfo.PropertyInfo); } } } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/ForeignKeyAttributeConvention.cs b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/ForeignKeyAttributeConvention.cs index 7ded2e39f4..440ef3d8b6 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/ForeignKeyAttributeConvention.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/ForeignKeyAttributeConvention.cs @@ -97,7 +97,7 @@ private static void ApplyNavigation(NavigationPropertyConfiguration navProperty, { Type dependentType = Nullable.GetUnderlyingType(dependent.PropertyInfo.PropertyType) ?? dependent.PropertyInfo.PropertyType; PrimitivePropertyConfiguration principal = principalEntity.Keys.FirstOrDefault( - k => k.PropertyInfo.PropertyType == dependentType && navProperty.PrincipalProperties.All(p => p != k.PropertyInfo)); + k => k.PropertyInfo.PropertyType == dependentType && navProperty.PrincipalProperties.All(p => p != k.PropertyInfo.MemberInfo)); if (principal != null) { @@ -136,7 +136,7 @@ private static void ApplyPrimitive(PrimitivePropertyConfiguration dependent, Ent Type dependentType = Nullable.GetUnderlyingType(dependent.PropertyInfo.PropertyType) ?? dependent.PropertyInfo.PropertyType; PrimitivePropertyConfiguration principal = principalEntity.Keys.FirstOrDefault( - k => k.PropertyInfo.PropertyType == dependentType && navProperty.PrincipalProperties.All(p => p != k.PropertyInfo)); + k => k.PropertyInfo.PropertyType == dependentType && navProperty.PrincipalProperties.All(p => p != k.PropertyInfo.MemberInfo)); if (principal != null) { navProperty.HasConstraint(dependent.PropertyInfo, principal.PropertyInfo); diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConvention.cs b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConvention.cs index 21e9a6db85..9d1212fe22 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConvention.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConvention.cs @@ -52,7 +52,7 @@ public override void Apply(PropertyConfiguration edmProperty, } else { - structuralTypeConfiguration.RemoveProperty(edmProperty.PropertyInfo); + structuralTypeConfiguration.RemoveProperty(edmProperty.PropertyInfo.PropertyInfo); } } } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConvention.cs b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConvention.cs index 745c1e6f7d..ae06e595a2 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConvention.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConvention.cs @@ -40,7 +40,7 @@ public override void Apply(StructuralPropertyConfiguration edmProperty, EntityTypeConfiguration entity = structuralTypeConfiguration as EntityTypeConfiguration; if (entity != null) { - entity.HasKey(edmProperty.PropertyInfo); + entity.HasKey(edmProperty.PropertyInfo.PropertyInfo); } } } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/NotMappedAttributeEdmPropertyConvention.cs b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/NotMappedAttributeEdmPropertyConvention.cs index 2b2b189107..8522dff0af 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/NotMappedAttributeEdmPropertyConvention.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/Attributes/NotMappedAttributeEdmPropertyConvention.cs @@ -42,7 +42,7 @@ public override void Apply(PropertyConfiguration edmProperty, if (!edmProperty.AddedExplicitly) { - structuralTypeConfiguration.RemoveProperty(edmProperty.PropertyInfo); + structuralTypeConfiguration.RemoveProperty(edmProperty.PropertyInfo.PropertyInfo); } } } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/EntityKeyConvention.cs b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/EntityKeyConvention.cs index 14703f3010..958531813d 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/EntityKeyConvention.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/Conventions/EntityKeyConvention.cs @@ -42,7 +42,7 @@ public override void Apply(EntityTypeConfiguration entity, ODataConventionModelB PropertyConfiguration key = GetKeyProperty(entity); if (key != null) { - entity.HasKey(key.PropertyInfo); + entity.HasKey(key.PropertyInfo.PropertyInfo); } } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs b/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs index 988a3b9ce5..30b90bbe59 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs @@ -184,7 +184,8 @@ private static string ConvertBindingPath(EdmTypeMap edmMap, NavigationPropertyBi } else if (propertyInfo != null) { - bindings.Add(edmMap.EdmProperties[propertyInfo].Name); + PropertyDescriptor propDescr = new PropertyDescriptor(propertyInfo); + bindings.Add(edmMap.EdmProperties[propDescr].Name); } } @@ -424,7 +425,7 @@ private static Dictionary AddTypes(this EdmModel model, EdmTypeM model.AddClrTypeAnnotations(edmTypes); // add annotation for properties - Dictionary edmProperties = edmTypeMap.EdmProperties; + Dictionary edmProperties = edmTypeMap.EdmProperties; model.AddClrPropertyInfoAnnotations(edmProperties); model.AddPropertyRestrictionsAnnotations(edmTypeMap.EdmPropertiesRestrictions); model.AddPropertiesQuerySettings(edmTypeMap.EdmPropertiesQuerySettings); @@ -488,13 +489,13 @@ private static void AddClrTypeAnnotations(this EdmModel model, Dictionary edmProperties) + private static void AddClrPropertyInfoAnnotations(this EdmModel model, Dictionary edmProperties) { - foreach (KeyValuePair edmPropertyMap in edmProperties) + foreach (KeyValuePair edmPropertyMap in edmProperties) { IEdmProperty edmProperty = edmPropertyMap.Value; - PropertyInfo clrProperty = edmPropertyMap.Key; - if (edmProperty.Name != clrProperty.Name) + PropertyDescriptor clrProperty = edmPropertyMap.Key; + if (clrProperty.MethodInfo != null || edmProperty.Name != clrProperty.Name) { model.SetAnnotationValue(edmProperty, new ClrPropertyInfoAnnotation(clrProperty)); } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/EdmTypeBuilder.cs b/src/Microsoft.AspNet.OData.Shared/Builder/EdmTypeBuilder.cs index 971e210c34..982d65a1ed 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/EdmTypeBuilder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/EdmTypeBuilder.cs @@ -22,7 +22,7 @@ internal class EdmTypeBuilder { private readonly List _configurations; private readonly Dictionary _types = new Dictionary(); - private readonly Dictionary _properties = new Dictionary(); + private readonly Dictionary _properties = new Dictionary(); private readonly Dictionary _propertiesRestrictions = new Dictionary(); private readonly Dictionary _propertiesQuerySettings = new Dictionary(); private readonly Dictionary _structuredTypeQuerySettings = new Dictionary(); @@ -433,7 +433,8 @@ private IList GetDeclaringPropertyInfo(IEnumerable GetDeclaringPropertyInfo(IEnumerable edmTypes, - Dictionary edmProperties, + Dictionary edmProperties, Dictionary edmPropertiesRestrictions, Dictionary edmPropertiesQuerySettings, Dictionary edmStructuredTypeQuerySettings, @@ -31,7 +31,7 @@ public EdmTypeMap( public Dictionary EdmTypes { get; private set; } - public Dictionary EdmProperties { get; private set; } + public Dictionary EdmProperties { get; private set; } public Dictionary EdmPropertiesRestrictions { get; private set; } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/EntityTypeConfigurationOfTEntityType.cs b/src/Microsoft.AspNet.OData.Shared/Builder/EntityTypeConfigurationOfTEntityType.cs index e6cd007f9c..599ff99448 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/EntityTypeConfigurationOfTEntityType.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/EntityTypeConfigurationOfTEntityType.cs @@ -123,7 +123,7 @@ public EntityTypeConfiguration DerivesFrom() where TBase [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Explicit Expression generic type is more clear")] public EntityTypeConfiguration HasKey(Expression> keyDefinitionExpression) { - ICollection properties = PropertySelectorVisitor.GetSelectedProperties(keyDefinitionExpression); + ICollection properties = PropertySelectorVisitor.GetSelectedProperties(keyDefinitionExpression, false); foreach (PropertyInfo property in properties) { _configuration.HasKey(property); diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs index 286ce47c0d..4bea1a8f9f 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs @@ -29,22 +29,34 @@ public class NavigationPropertyConfiguration : PropertyConfiguration /// The . /// The declaring structural type. public NavigationPropertyConfiguration(PropertyInfo property, EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) - : base(property, declaringType) + :this(CreatePropertyDescriptor(property), multiplicity, declaringType) { - if (property == null) + + } + + /// + /// Initializes a new instance of the class. + /// + /// The backing CLR property. + /// The . + /// The declaring structural type. + public NavigationPropertyConfiguration(PropertyDescriptor propertyDecriptor, EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) + : base(propertyDecriptor, declaringType) + { + if (propertyDecriptor == null) { - throw Error.ArgumentNull("property"); + throw Error.ArgumentNull("propertyDescripor"); } Multiplicity = multiplicity; - _relatedType = property.PropertyType; + _relatedType = propertyDecriptor.PropertyType; if (multiplicity == EdmMultiplicity.Many) { Type elementType; if (!TypeHelper.IsCollection(_relatedType, out elementType)) { - throw Error.Argument("property", SRResources.ManyToManyNavigationPropertyMustReturnCollection, property.Name, TypeHelper.GetReflectedType(property).Name); + throw Error.Argument("property", SRResources.ManyToManyNavigationPropertyMustReturnCollection, propertyDecriptor.Name, TypeHelper.GetReflectedType(propertyDecriptor).Name); } _relatedType = elementType; @@ -53,6 +65,15 @@ public NavigationPropertyConfiguration(PropertyInfo property, EdmMultiplicity mu OnDeleteAction = EdmOnDeleteAction.None; } + private static PropertyDescriptor CreatePropertyDescriptor(PropertyInfo property) + { + if(property == null) + { + throw Error.ArgumentNull("property"); + } + return new PropertyDescriptor(property); + } + /// /// Gets the of this navigation property. /// @@ -192,6 +213,18 @@ public NavigationPropertyConfiguration HasConstraint(PropertyInfo dependentPrope principalPropertyInfo)); } + /// + /// Configures the referential constraint with the specified + /// and . + /// + /// The dependent property info for the referential constraint. + /// The principal property info for the referential constraint. + public NavigationPropertyConfiguration HasConstraint(PropertyDescriptor dependentPropertyInfo, + PropertyDescriptor principalPropertyInfo) + { + return HasConstraint(dependentPropertyInfo.PropertyInfo, principalPropertyInfo.PropertyInfo); + } + /// /// Configures the referential constraint with the dependent and principal property pair. /// diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/PrimitivePropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/PrimitivePropertyConfiguration.cs index 38aaa4372a..78eda0d09b 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/PrimitivePropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/PrimitivePropertyConfiguration.cs @@ -19,8 +19,9 @@ public class PrimitivePropertyConfiguration : StructuralPropertyConfiguration /// The name of the property. /// The declaring EDM type of the property. public PrimitivePropertyConfiguration(PropertyInfo property, StructuralTypeConfiguration declaringType) - : base(property, declaringType) + :base(property, declaringType) { + } /// diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/PropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/PropertyConfiguration.cs index 8891e030b1..f519e100ce 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/PropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/PropertyConfiguration.cs @@ -20,7 +20,7 @@ public abstract class PropertyConfiguration /// /// The name of the property. /// The declaring EDM type of the property. - protected PropertyConfiguration(PropertyInfo property, StructuralTypeConfiguration declaringType) + protected PropertyConfiguration(PropertyDescriptor property, StructuralTypeConfiguration declaringType) { if (property == null) { @@ -67,7 +67,7 @@ public string Name /// /// Gets the mapping CLR . /// - public PropertyInfo PropertyInfo { get; private set; } + public PropertyDescriptor PropertyInfo { get; private set; } /// /// Gets the CLR of the property. diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/PropertyDescriptor.cs b/src/Microsoft.AspNet.OData.Shared/Builder/PropertyDescriptor.cs new file mode 100644 index 0000000000..2efa5a7016 --- /dev/null +++ b/src/Microsoft.AspNet.OData.Shared/Builder/PropertyDescriptor.cs @@ -0,0 +1,174 @@ +using Microsoft.AspNet.OData.Common; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Microsoft.AspNet.OData.Builder +{ + /// + /// Property descriptor + /// + public class PropertyDescriptor + { + private readonly PropertyInfo _propertyInfo; + private readonly MethodInfo _methodInfo; + + /// + /// Initializes a new instance of the class. + /// + /// Property information + public PropertyDescriptor(PropertyInfo propertyInfo) + { + if (propertyInfo == null) + { + throw Error.ArgumentNull("propertyInfo"); + } + + this._propertyInfo = propertyInfo; + } + + /// + /// Initializes a new instance of the class. + /// + /// Extension property information + public PropertyDescriptor(MethodInfo methodInfo) + { + if(methodInfo==null) + { + throw Error.ArgumentNull("methodInfo"); + } + this._methodInfo = methodInfo; + } + + /// + /// Obtains information about member + /// + public MemberInfo MemberInfo + { + get + { + if (_propertyInfo != null) + return _propertyInfo; + return _methodInfo; + } + } + + /// + /// Provide access to property metadata + /// + public PropertyInfo PropertyInfo + { + get + { + return _propertyInfo; + } + } + + /// + /// Provide access to extension property metadata + /// + public MethodInfo MethodInfo + { + get + { + return _methodInfo; + } + } + + /// + /// Get the name of the this property + /// + public string Name + { + get + { + return MemberInfo.Name; + } + } + + /// + /// Get the type of the this property + /// + public Type PropertyType + { + get + { + if (_propertyInfo != null) + return _propertyInfo.PropertyType; + return _methodInfo.ReturnType; + } + } + + /// + /// Gets the class that declares this member + /// + public Type DeclaringType + { + get + { + if (_propertyInfo != null) + return _propertyInfo.DeclaringType; + return _methodInfo.GetParameters()[0].ParameterType; + } + } + + /// + /// Return the reflected type from a member info. + /// + public Type ReflectedType + { + get + { + if (_propertyInfo != null) + return TypeHelper.GetReflectedType(_propertyInfo); + return _methodInfo.GetParameters()[0].ParameterType; + } + } + + /// + public static implicit operator MemberInfo(PropertyDescriptor propertyDescriptor) + { + return propertyDescriptor.MemberInfo; + } + + /// + public object[] GetCustomAttributes(Type type, bool inherit) + { + return MemberInfo.GetCustomAttributes(type, inherit); + } + + /// + public IEnumerable GetCustomAttributes(bool inherit=false) + where T:Attribute + { + return MemberInfo.GetCustomAttributes(inherit); + } + + /// + public T GetCustomAttribute(bool inherit = false) + where T : Attribute + { + return MemberInfo.GetCustomAttribute(inherit); + } + + /// + public override int GetHashCode() + { + return MemberInfo.GetHashCode(); + } + + /// + public override bool Equals(object obj) + { + PropertyDescriptor propDescr = obj as PropertyDescriptor; + if (propDescr == null) + return false; + + if (PropertyInfo != null) + return PropertyInfo.Equals(propDescr.PropertyInfo); + else + return MethodInfo.Equals(propDescr.MethodInfo); + } + } +} diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/PropertySelectorVisitor.cs b/src/Microsoft.AspNet.OData.Shared/Builder/PropertySelectorVisitor.cs index 25896a8ab7..04cbf1b15d 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/PropertySelectorVisitor.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/PropertySelectorVisitor.cs @@ -6,21 +6,24 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; using Microsoft.AspNet.OData.Common; namespace Microsoft.AspNet.OData.Builder { internal class PropertySelectorVisitor : ExpressionVisitor { - private List _properties = new List(); + private List _properties = new List(); + private readonly bool includeExtensionProperty; [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "Class is internal, virtual call okay")] - internal PropertySelectorVisitor(Expression exp) + internal PropertySelectorVisitor(Expression exp, bool includeExtensionProperty) { + this.includeExtensionProperty = includeExtensionProperty; Visit(exp); } - public PropertyInfo Property + public MemberInfo Property { get { @@ -28,7 +31,7 @@ public PropertyInfo Property } } - public ICollection Properties + public ICollection Properties { get { @@ -59,14 +62,50 @@ protected override Expression VisitMember(MemberExpression node) return node; } + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node == null) + { + throw Error.ArgumentNull("node"); + } + + MethodInfo minfo = node.Method; + + if (!IsExtensionProperty(minfo)) + { + throw Error.InvalidOperation(SRResources.MemberExpressionsMustBeProperties, TypeHelper.GetReflectedType(node.Method).FullName, node.Method.Name); + } + + if (node.Arguments.First().NodeType != ExpressionType.Parameter) + { + throw Error.InvalidOperation(SRResources.MemberExpressionsMustBeBoundToLambdaParameter); + } + + _properties.Add(minfo); + return node; + } + + private static bool IsExtensionProperty(MethodInfo methodInfo) + { + return methodInfo!=null + && methodInfo.IsStatic + && methodInfo.IsDefined(typeof(ExtensionAttribute), false) + && methodInfo.GetParameters().Length == 1; + } + public static PropertyInfo GetSelectedProperty(Expression exp) { - return new PropertySelectorVisitor(exp).Property; + return GetSelectedProperty(exp, false) as PropertyInfo; + } + + public static MemberInfo GetSelectedProperty(Expression exp, bool includeExtensionProperty) + { + return new PropertySelectorVisitor(exp, includeExtensionProperty).Property; } - public static ICollection GetSelectedProperties(Expression exp) + public static ICollection GetSelectedProperties(Expression exp, bool includeExtensionProperty=false) { - return new PropertySelectorVisitor(exp).Properties; + return new PropertySelectorVisitor(exp, includeExtensionProperty).Properties; } public override Expression Visit(Expression exp) @@ -82,6 +121,11 @@ public override Expression Visit(Expression exp) case ExpressionType.MemberAccess: case ExpressionType.Lambda: return base.Visit(exp); + case ExpressionType.Call: + if (includeExtensionProperty) + return base.Visit(exp); + else + goto default; default: throw Error.NotSupported(SRResources.UnsupportedExpressionNodeType); } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralPropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralPropertyConfiguration.cs index f31d7a13b9..1eaf468cfd 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralPropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralPropertyConfiguration.cs @@ -17,7 +17,7 @@ public abstract class StructuralPropertyConfiguration : PropertyConfiguration /// The property of the configuration. /// The declaring type of the property. protected StructuralPropertyConfiguration(PropertyInfo property, StructuralTypeConfiguration declaringType) - : base(property, declaringType) + : base(new PropertyDescriptor(property), declaringType) { OptionalProperty = EdmLibHelpers.IsNullable(property.PropertyType); } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs index 5e8ce64bcd..cf5efa236e 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs @@ -31,7 +31,7 @@ public abstract class StructuralTypeConfiguration : IEdmTypeConfiguration /// The default constructor is intended for use by unit testing only. protected StructuralTypeConfiguration() { - ExplicitProperties = new Dictionary(); + ExplicitProperties = new Dictionary(); RemovedProperties = new List(); QueryConfiguration = new QueryConfiguration(); } @@ -213,7 +213,7 @@ public virtual IEnumerable NavigationProperties /// /// Gets the collection of explicitly added properties. /// - protected internal IDictionary ExplicitProperties { get; private set; } + protected internal IDictionary ExplicitProperties { get; private set; } /// /// Gets the base type of this structural type. @@ -312,7 +312,7 @@ public virtual PrimitivePropertyConfiguration AddProperty(PropertyInfo propertyI propertyConfiguration = new PrecisionPropertyConfiguration(propertyInfo, this); } } - ExplicitProperties[propertyInfo] = propertyConfiguration; + ExplicitProperties[propertyConfiguration.PropertyInfo] = propertyConfiguration; } return propertyConfiguration; @@ -355,12 +355,17 @@ public virtual EnumPropertyConfiguration AddEnumProperty(PropertyInfo propertyIn if (propertyConfiguration == null) { propertyConfiguration = new EnumPropertyConfiguration(propertyInfo, this); - ExplicitProperties[propertyInfo] = propertyConfiguration; + ExplicitProperties[propertyConfiguration.PropertyInfo] = propertyConfiguration; } return propertyConfiguration; } + internal ComplexPropertyConfiguration AddComplexProperty(PropertyDescriptor propertyDescriptor) + { + return AddComplexProperty(propertyDescriptor.PropertyInfo); + } + /// /// Adds a complex property to this edm type. /// @@ -394,7 +399,7 @@ public virtual ComplexPropertyConfiguration AddComplexProperty(PropertyInfo prop if (propertyConfiguration == null) { propertyConfiguration = new ComplexPropertyConfiguration(propertyInfo, this); - ExplicitProperties[propertyInfo] = propertyConfiguration; + ExplicitProperties[propertyConfiguration.PropertyInfo] = propertyConfiguration; // Make sure the complex type is in the model. ModelBuilder.AddComplexType(propertyInfo.PropertyType); @@ -403,6 +408,11 @@ public virtual ComplexPropertyConfiguration AddComplexProperty(PropertyInfo prop return propertyConfiguration; } + internal CollectionPropertyConfiguration AddCollectionProperty(PropertyDescriptor propertyDescriptor) + { + return AddCollectionProperty(propertyDescriptor.PropertyInfo); + } + /// /// Adds a collection property to this edm type. /// @@ -435,7 +445,7 @@ public virtual CollectionPropertyConfiguration AddCollectionProperty(PropertyInf if (propertyConfiguration == null) { propertyConfiguration = new CollectionPropertyConfiguration(propertyInfo, this); - ExplicitProperties[propertyInfo] = propertyConfiguration; + ExplicitProperties[propertyConfiguration.PropertyInfo] = propertyConfiguration; // If the ElementType is not primitive or enum treat as a ComplexType and Add to the model. IEdmPrimitiveTypeReference edmType = @@ -488,6 +498,20 @@ public virtual void AddDynamicPropertyDictionary(PropertyInfo propertyInfo) _dynamicPropertyDictionary = propertyInfo; } + /// + /// Removes the given property. + /// + /// The property being removed. + public virtual void RemoveProperty(PropertyDescriptor propertyDescriptor) + { + if (propertyDescriptor == null) + { + throw new ArgumentNullException("propertyDescriptor"); + } + + RemoveProperty(propertyDescriptor.PropertyInfo); + } + /// /// Removes the given property. /// @@ -527,6 +551,18 @@ public virtual void RemoveProperty(PropertyInfo propertyInfo) /// The of the navigation property. /// Returns the of the added property. public virtual NavigationPropertyConfiguration AddNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity) + { + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(navigationProperty); + return AddNavigationProperty(propertyDescriptor, multiplicity, containsTarget: false); + } + + /// + /// Adds a non-contained EDM navigation property to this entity type. + /// + /// The backing CLR property. + /// The of the navigation property. + /// Returns the of the added property. + public virtual NavigationPropertyConfiguration AddNavigationProperty(PropertyDescriptor navigationProperty, EdmMultiplicity multiplicity) { return AddNavigationProperty(navigationProperty, multiplicity, containsTarget: false); } @@ -538,18 +574,30 @@ public virtual NavigationPropertyConfiguration AddNavigationProperty(PropertyInf /// The of the navigation property. /// Returns the of the added property. public virtual NavigationPropertyConfiguration AddContainedNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity) + { + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(navigationProperty); + return AddNavigationProperty(propertyDescriptor, multiplicity, containsTarget: true); + } + + /// + /// Adds a contained EDM navigation property to this entity type. + /// + /// The backing CLR property. + /// The of the navigation property. + /// Returns the of the added property. + public virtual NavigationPropertyConfiguration AddContainedNavigationProperty(PropertyDescriptor navigationProperty, EdmMultiplicity multiplicity) { return AddNavigationProperty(navigationProperty, multiplicity, containsTarget: true); } - private NavigationPropertyConfiguration AddNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity, bool containsTarget) + private NavigationPropertyConfiguration AddNavigationProperty(PropertyDescriptor navigationProperty, EdmMultiplicity multiplicity, bool containsTarget) { if (navigationProperty == null) { throw Error.ArgumentNull("navigationProperty"); } - if (!TypeHelper.GetReflectedType(navigationProperty).IsAssignableFrom(ClrType)) + if (!navigationProperty.ReflectedType.IsAssignableFrom(ClrType)) { throw Error.Argument("navigationProperty", SRResources.PropertyDoesNotBelongToType, navigationProperty.Name, ClrType.FullName); } @@ -608,7 +656,7 @@ internal T ValidatePropertyNotAlreadyDefinedOtherTypes(PropertyInfo propertyI return propertyConfiguration; } - internal void ValidatePropertyNotAlreadyDefinedInBaseTypes(PropertyInfo propertyInfo) + internal void ValidatePropertyNotAlreadyDefinedInBaseTypes(MemberInfo propertyInfo) { PropertyConfiguration baseProperty = this.DerivedProperties().FirstOrDefault(p => p.Name == propertyInfo.Name); @@ -619,7 +667,7 @@ internal void ValidatePropertyNotAlreadyDefinedInBaseTypes(PropertyInfo property } } - internal void ValidatePropertyNotAlreadyDefinedInDerivedTypes(PropertyInfo propertyInfo) + internal void ValidatePropertyNotAlreadyDefinedInDerivedTypes(MemberInfo propertyInfo) { foreach (StructuralTypeConfiguration derivedType in ModelBuilder.DerivedTypes(this)) { diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfigurationOfTStructuralType.cs b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfigurationOfTStructuralType.cs index 90a1e100d7..034217be58 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfigurationOfTStructuralType.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfigurationOfTStructuralType.cs @@ -761,14 +761,24 @@ public StructuralTypeConfiguration Expand() internal NavigationPropertyConfiguration GetOrCreateNavigationProperty(Expression navigationPropertyExpression, EdmMultiplicity multiplicity) { - PropertyInfo navigationProperty = PropertySelectorVisitor.GetSelectedProperty(navigationPropertyExpression); - return _configuration.AddNavigationProperty(navigationProperty, multiplicity); + MemberInfo navigationProperty = PropertySelectorVisitor.GetSelectedProperty(navigationPropertyExpression, true); + PropertyInfo pinfo = navigationProperty as PropertyInfo; + MethodInfo minfo = navigationProperty as MethodInfo; + + PropertyDescriptor propDescr = pinfo!=null ? new PropertyDescriptor(pinfo) + : new PropertyDescriptor(minfo); + return _configuration.AddNavigationProperty(propDescr, multiplicity); } internal NavigationPropertyConfiguration GetOrCreateContainedNavigationProperty(Expression navigationPropertyExpression, EdmMultiplicity multiplicity) { - PropertyInfo navigationProperty = PropertySelectorVisitor.GetSelectedProperty(navigationPropertyExpression); - return _configuration.AddContainedNavigationProperty(navigationProperty, multiplicity); + MemberInfo navigationProperty = PropertySelectorVisitor.GetSelectedProperty(navigationPropertyExpression, true); + PropertyInfo pinfo = navigationProperty as PropertyInfo; + MethodInfo minfo = navigationProperty as MethodInfo; + + PropertyDescriptor propDescr = pinfo != null ? new PropertyDescriptor(pinfo) + : new PropertyDescriptor(minfo); + return _configuration.AddContainedNavigationProperty(propDescr, multiplicity); } private PrimitivePropertyConfiguration GetPrimitivePropertyConfiguration(Expression propertyExpression, bool optional) diff --git a/src/Microsoft.AspNet.OData.Shared/ClrPropertyInfoAnnotation.cs b/src/Microsoft.AspNet.OData.Shared/ClrPropertyInfoAnnotation.cs index 51dfc22ae7..94fde282bc 100644 --- a/src/Microsoft.AspNet.OData.Shared/ClrPropertyInfoAnnotation.cs +++ b/src/Microsoft.AspNet.OData.Shared/ClrPropertyInfoAnnotation.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System.Reflection; +using Microsoft.AspNet.OData.Builder; using Microsoft.AspNet.OData.Common; using Microsoft.OData.Edm; @@ -23,12 +24,26 @@ public ClrPropertyInfoAnnotation(PropertyInfo clrPropertyInfo) throw Error.ArgumentNull("clrPropertyInfo"); } - ClrPropertyInfo = clrPropertyInfo; + ClrPropertyInfo = new PropertyDescriptor(clrPropertyInfo); + } + + /// + /// Initializes a new instance of class. + /// + /// The backing CLR property info for the EDM property. + public ClrPropertyInfoAnnotation(PropertyDescriptor propertyDescriptor) + { + if (propertyDescriptor == null) + { + throw Error.ArgumentNull("propetyDescriptor"); + } + + ClrPropertyInfo = propertyDescriptor; } /// /// Gets the backing CLR property info for the EDM property. /// - public PropertyInfo ClrPropertyInfo { get; private set; } + public PropertyDescriptor ClrPropertyInfo { get; private set; } } } diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs index 14def882d4..00d38a0a75 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs @@ -694,16 +694,37 @@ public static string GetClrPropertyName(IEdmProperty edmProperty, IEdmModel edmM ClrPropertyInfoAnnotation annotation = edmModel.GetAnnotationValue(edmProperty); if (annotation != null) { - PropertyInfo propertyInfo = annotation.ClrPropertyInfo; - if (propertyInfo != null) + PropertyDescriptor propDescr = annotation.ClrPropertyInfo; + if (propDescr != null) { - propertyName = propertyInfo.Name; + propertyName = propDescr.Name; } } return propertyName; } + public static PropertyDescriptor GetClrPropertyDescriptor(IEdmProperty edmProperty, IEdmModel edmModel) + { + if (edmProperty == null) + { + throw Error.ArgumentNull("edmProperty"); + } + + if (edmModel == null) + { + throw Error.ArgumentNull("edmModel"); + } + + ClrPropertyInfoAnnotation annotation = edmModel.GetAnnotationValue(edmProperty); + if (annotation != null) + { + return annotation.ClrPropertyInfo; + } + + return null; + } + public static PropertyInfo GetDynamicPropertyDictionary(IEdmStructuredType edmType, IEdmModel edmModel) { if (edmType == null) diff --git a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems index a5e0cf84f3..bdf1646702 100644 --- a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems +++ b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems @@ -15,6 +15,7 @@ + diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs index 457ba18339..646033bb39 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs @@ -184,8 +184,22 @@ internal Expression CreatePropertyValueExpressionWithFilter(IEdmEntityType eleme source = Expression.TypeAs(source, castType); } - string propertyName = EdmLibHelpers.GetClrPropertyName(property, _model); - Expression propertyValue = Expression.Property(source, propertyName); + var propDescr = EdmLibHelpers.GetClrPropertyDescriptor(property, _model); + string propertyName = null; + Expression propertyValue; + if (propDescr != null) + { + propertyName = propDescr.Name; + if (propDescr.PropertyInfo != null) + propertyValue = Expression.Property(source, propDescr.PropertyInfo); + else + propertyValue = Expression.Call(propDescr.MethodInfo, source); + } + else + { + propertyName = EdmLibHelpers.GetClrPropertyName(property, _model); + propertyValue = Expression.Property(source, propertyName); + } Type nullablePropertyType = TypeHelper.ToNullable(propertyValue.Type); Expression nullablePropertyValue = ExpressionHelpers.ToNullable(propertyValue); diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandWrapperConverter.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandWrapperConverter.cs index 355754a222..79c5c0a936 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandWrapperConverter.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandWrapperConverter.cs @@ -5,6 +5,7 @@ using System.Diagnostics.Contracts; using System.Linq; using System.Reflection; +using Microsoft.AspNet.OData.Builder; using Microsoft.AspNet.OData.Common; using Microsoft.OData.Edm; using Newtonsoft.Json; @@ -58,7 +59,7 @@ public JsonPropertyNameMapper(IEdmModel model, IEdmStructuredType type) public string MapProperty(string propertyName) { IEdmProperty property = _type.Properties().Single(s => s.Name == propertyName); - PropertyInfo info = GetPropertyInfo(property); + PropertyDescriptor info = GetPropertyInfo(property); JsonPropertyAttribute jsonProperty = GetJsonProperty(info); if (jsonProperty != null && !String.IsNullOrWhiteSpace(jsonProperty.PropertyName)) { @@ -70,7 +71,7 @@ public string MapProperty(string propertyName) } } - private PropertyInfo GetPropertyInfo(IEdmProperty property) + private PropertyDescriptor GetPropertyInfo(IEdmProperty property) { ClrPropertyInfoAnnotation clrPropertyAnnotation = _model.GetAnnotationValue(property); if (clrPropertyAnnotation != null) @@ -84,10 +85,10 @@ private PropertyInfo GetPropertyInfo(IEdmProperty property) PropertyInfo info = clrTypeAnnotation.ClrType.GetProperty(property.Name); Contract.Assert(info != null); - return info; + return new PropertyDescriptor(info); } - private static JsonPropertyAttribute GetJsonProperty(PropertyInfo property) + private static JsonPropertyAttribute GetJsonProperty(PropertyDescriptor property) { return property.GetCustomAttributes(typeof(JsonPropertyAttribute), inherit: false) .OfType().SingleOrDefault(); diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/CollectionPropertyConfigurationTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/CollectionPropertyConfigurationTest.cs index 17e6185e64..5742a131f6 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/CollectionPropertyConfigurationTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/CollectionPropertyConfigurationTest.cs @@ -61,7 +61,7 @@ public void HasCorrectKindPropertyInfoAndName(PropertyInfo property, Type elemen Assert.Equal(PropertyKind.Collection, configuration.Kind); Assert.Equal(elementType, configuration.ElementType); Assert.Equal(elementType, configuration.RelatedClrType); - Assert.Equal(property, configuration.PropertyInfo); + Assert.Equal(property, configuration.PropertyInfo.PropertyInfo); Assert.Equal(property.Name, configuration.Name); Assert.Equal(structuralType.Object, configuration.DeclaringType); } @@ -83,7 +83,7 @@ public void CanCorrectlyDetectCollectionProperties(PropertyInfo property, Type e { Mock structuralType = new Mock(); CollectionPropertyConfiguration configuration = new CollectionPropertyConfiguration(property, structuralType.Object); - Assert.Same(property, configuration.PropertyInfo); + Assert.Same(property, configuration.PropertyInfo.PropertyInfo); Assert.Same(elementType, configuration.ElementType); Assert.Same(property.Name, configuration.Name); } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs index b67b8864ba..9c3a9b9063 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs @@ -58,7 +58,12 @@ public static bool Apply() property.Setup(p => p.GetCustomAttributes(It.IsAny())).Returns(new[] { attribute }); Mock propertyConfiguration; - if (typeof(TProperty) == typeof(NavigationPropertyConfiguration)) + if(typeof(TProperty) == typeof(PropertyConfiguration)) + { + Mock propertyDescriptor = new Mock(property.Object); + propertyConfiguration = new Mock(propertyDescriptor.Object, structuralType.Object); + } + else if (typeof(TProperty) == typeof(NavigationPropertyConfiguration)) { propertyConfiguration = new Mock(property.Object, EdmMultiplicity.ZeroOrOne, structuralType.Object); } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs index 2690b2a5f1..1764f23f18 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs @@ -35,11 +35,11 @@ public void Apply_RemovesAllPropertiesThatAreNotDataMembers() type.Setup(t => t.ClrType).Returns(clrType.Object); var mockPropertyWithoutAttributes = CreateMockProperty(); - type.Object.ExplicitProperties.Add(new MockPropertyInfo(), CreateMockProperty(new DataMemberAttribute())); - type.Object.ExplicitProperties.Add(new MockPropertyInfo(), CreateMockProperty(new DataMemberAttribute())); - type.Object.ExplicitProperties.Add(new MockPropertyInfo(), mockPropertyWithoutAttributes); + type.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), CreateMockProperty(new DataMemberAttribute())); + type.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), CreateMockProperty(new DataMemberAttribute())); + type.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), mockPropertyWithoutAttributes); - type.Setup(t => t.RemoveProperty(mockPropertyWithoutAttributes.PropertyInfo)).Verifiable(); + type.Setup(t => t.RemoveProperty(mockPropertyWithoutAttributes.PropertyInfo.PropertyInfo)).Verifiable(); // Act _convention.Apply(type.Object, builder); @@ -61,7 +61,7 @@ public void Apply_DoesnotRemove_ExplicitlyAddedProperties() _convention.Apply(entity, builder); // Assert - Assert.Contains(propertyInfo, entity.ExplicitProperties.Keys); + Assert.Contains(new PropertyDescriptor(propertyInfo), entity.ExplicitProperties.Keys); Assert.DoesNotContain(propertyInfo, entity.RemovedProperties); } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs index 76a1a63e90..96a809b139 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs @@ -31,12 +31,14 @@ public void Apply_Calls_RemovesProperty() property.Setup(p => p.PropertyType).Returns(typeof(int)); property.Setup(p => p.GetCustomAttributes(It.IsAny())).Returns(new[] { new IgnoreDataMemberAttribute() }); + Mock propertyDescriptor = new Mock(property.Object); + Mock type = new Mock(); Mock structuralType = new Mock(MockBehavior.Strict); structuralType.Setup(e => e.RemoveProperty(property.Object)).Verifiable(); structuralType.Setup(s => s.ClrType).Returns(type.Object); - Mock primitiveProperty = new Mock(property.Object, structuralType.Object); + Mock primitiveProperty = new Mock(propertyDescriptor.Object, structuralType.Object); primitiveProperty.Object.AddedExplicitly = false; // Act @@ -65,13 +67,15 @@ public void Apply_DoesnotRemoveProperty_TypeIsDataContractAndPropertyHasDataMemb new DataContractAttribute() }); + Mock propertyDescriptor = new Mock(property.Object); + Mock type = new Mock(); type.Setup(t => t.GetCustomAttributes(It.IsAny(), It.IsAny())).Returns(new[] { new DataContractAttribute() }); Mock structuralType = new Mock(MockBehavior.Strict); structuralType.Setup(s => s.ClrType).Returns(type.Object); - Mock primitiveProperty = new Mock(property.Object, structuralType.Object); + Mock primitiveProperty = new Mock(propertyDescriptor.Object, structuralType.Object); // Act new IgnoreDataMemberAttributeEdmPropertyConvention().Apply(primitiveProperty.Object, structuralType.Object, builder); @@ -86,6 +90,7 @@ public void Apply_DoesnotRemove_ExplicitlyAddedProperties() // Arrange ODataConventionModelBuilder builder = ODataConventionModelBuilderFactory.Create(); PropertyInfo propertyInfo = typeof(TestEntity).GetProperty("ExplicitlyAddedProperty"); + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyInfo); EntityTypeConfiguration entity = builder.AddEntityType(typeof(TestEntity)); PropertyConfiguration property = entity.AddProperty(propertyInfo); @@ -93,7 +98,7 @@ public void Apply_DoesnotRemove_ExplicitlyAddedProperties() new IgnoreDataMemberAttributeEdmPropertyConvention().Apply(property, entity, builder); // Assert - Assert.Contains(propertyInfo, entity.ExplicitProperties.Keys); + Assert.Contains(propertyDescriptor, entity.ExplicitProperties.Keys); Assert.DoesNotContain(propertyInfo, entity.RemovedProperties); } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs index 183aba38f5..3fe644cd12 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs @@ -31,10 +31,12 @@ public void Apply_Calls_RemovesProperty_ForInferredProperties() property.Setup(p => p.PropertyType).Returns(typeof(int)); property.Setup(p => p.GetCustomAttributes(It.IsAny())).Returns(new[] { new NotMappedAttribute() }); + Mock propertyDescriptor = new Mock(property.Object); + Mock structuralType = new Mock(MockBehavior.Strict); structuralType.Setup(e => e.RemoveProperty(property.Object)).Verifiable(); - Mock primitiveProperty = new Mock(property.Object, structuralType.Object); + Mock primitiveProperty = new Mock(propertyDescriptor.Object, structuralType.Object); primitiveProperty.Object.AddedExplicitly = false; // Act @@ -51,6 +53,7 @@ public void Apply_DoesnotRemove_ExplicitlyAddedProperties() ODataConventionModelBuilder builder = ODataConventionModelBuilderFactory.Create(); PropertyInfo propertyInfo = typeof(TestEntity).GetProperty("Property"); + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyInfo); EntityTypeConfiguration entity = builder.AddEntityType(typeof(TestEntity)); PropertyConfiguration property = entity.AddProperty(propertyInfo); @@ -58,7 +61,7 @@ public void Apply_DoesnotRemove_ExplicitlyAddedProperties() new NotMappedAttributeConvention().Apply(property, entity, builder); // Assert - Assert.Contains(propertyInfo, entity.ExplicitProperties.Keys); + Assert.Contains(propertyDescriptor, entity.ExplicitProperties.Keys); Assert.DoesNotContain(propertyInfo, entity.RemovedProperties); } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/TimestampAttributeEdmPropertyConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/TimestampAttributeEdmPropertyConventionTests.cs index a3c1940f70..925b812735 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/TimestampAttributeEdmPropertyConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/TimestampAttributeEdmPropertyConventionTests.cs @@ -21,7 +21,7 @@ public void TimestampConvention_AppliesWhenTheAttributeIsAppliedToASinglePropert PropertyInfo property = CreateMockPropertyInfo("TestProperty"); EntityTypeConfiguration entityType = new EntityTypeConfiguration(); PrimitivePropertyConfiguration primitiveProperty = new PrimitivePropertyConfiguration(property, entityType); - entityType.ExplicitProperties.Add(property, primitiveProperty); + entityType.ExplicitProperties.Add(primitiveProperty.PropertyInfo, primitiveProperty); TimestampAttributeEdmPropertyConvention convention = new TimestampAttributeEdmPropertyConvention(); // Act @@ -38,7 +38,7 @@ public void TimestampConvention_DoesntApplyWhenTheAttributeIsAppliedOnANonEntity PropertyInfo property = CreateMockPropertyInfo("TestProperty"); ComplexTypeConfiguration complexType = new ComplexTypeConfiguration(); PrimitivePropertyConfiguration primitiveProperty = new PrimitivePropertyConfiguration(property, complexType); - complexType.ExplicitProperties.Add(property, primitiveProperty); + complexType.ExplicitProperties.Add(primitiveProperty.PropertyInfo, primitiveProperty); TimestampAttributeEdmPropertyConvention convention = new TimestampAttributeEdmPropertyConvention(); // Act @@ -56,8 +56,9 @@ public void TimestampConvention_DoesntApplyWhenTheAttributeIsAppliedToMultiplePr PropertyInfo otherProperty = CreateMockPropertyInfo("OtherTestProperty"); EntityTypeConfiguration entityType = new EntityTypeConfiguration(); PrimitivePropertyConfiguration primitiveProperty = new PrimitivePropertyConfiguration(property, entityType); - entityType.ExplicitProperties.Add(property, primitiveProperty); - entityType.ExplicitProperties.Add(otherProperty, new PrimitivePropertyConfiguration(otherProperty, entityType)); + PrimitivePropertyConfiguration otherPrimitiveProperty = new PrimitivePropertyConfiguration(otherProperty, entityType); + entityType.ExplicitProperties.Add(primitiveProperty.PropertyInfo, primitiveProperty); + entityType.ExplicitProperties.Add(otherPrimitiveProperty.PropertyInfo, otherPrimitiveProperty); TimestampAttributeEdmPropertyConvention convention = new TimestampAttributeEdmPropertyConvention(); // Act @@ -80,8 +81,9 @@ public void TimestampConvention_DoesntApplyWhenTheAttributeIsAppliedToMultiplePr entityType.BaseType = baseEntityType; PrimitivePropertyConfiguration primitiveProperty = new PrimitivePropertyConfiguration(property, entityType); - entityType.ExplicitProperties.Add(property, primitiveProperty); - baseEntityType.ExplicitProperties.Add(otherProperty, new PrimitivePropertyConfiguration(otherProperty, baseEntityType)); + PrimitivePropertyConfiguration otherPrimitiveProperty = new PrimitivePropertyConfiguration(otherProperty, baseEntityType); + entityType.ExplicitProperties.Add(primitiveProperty.PropertyInfo, primitiveProperty); + baseEntityType.ExplicitProperties.Add(otherPrimitiveProperty.PropertyInfo, otherPrimitiveProperty); TimestampAttributeEdmPropertyConvention convention = new TimestampAttributeEdmPropertyConvention(); // Act diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/EntityKeyConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/EntityKeyConventionTests.cs index def91bbe10..1d3187932f 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/EntityKeyConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/EntityKeyConventionTests.cs @@ -24,11 +24,12 @@ public void Apply_Calls_HasKey_OnEdmType(string propertyName) { // Arrange Mock mockEntityType = new Mock(); - Mock property = new Mock(typeof(EntityKeyConventionTests_EntityType).GetProperty(propertyName), mockEntityType.Object); + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(typeof(EntityKeyConventionTests_EntityType).GetProperty(propertyName)); + Mock property = new Mock(propertyDescriptor, mockEntityType.Object); mockEntityType.Setup(e => e.Name).Returns("SampleEntity"); mockEntityType.Setup(entityType => entityType.HasKey(typeof(EntityKeyConventionTests_EntityType).GetProperty(propertyName))).Returns(mockEntityType.Object).Verifiable(); - mockEntityType.Object.ExplicitProperties.Add(new MockPropertyInfo(), property.Object); + mockEntityType.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), property.Object); // Act new EntityKeyConvention().Apply(mockEntityType.Object, null); @@ -42,8 +43,9 @@ public void Apply_Calls_HasKey_ForEnumProperty_OnEdmType() { // Arrange Mock mockEntityType = new Mock(); + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(typeof(EntityKeyConventionTests_EntityType).GetProperty("ColorId")); Mock property = - new Mock(typeof(EntityKeyConventionTests_EntityType).GetProperty("ColorId"), + new Mock(propertyDescriptor, mockEntityType.Object); property.Setup(c => c.Kind).Returns(PropertyKind.Enum); @@ -53,7 +55,7 @@ public void Apply_Calls_HasKey_ForEnumProperty_OnEdmType() .Returns(mockEntityType.Object) .Verifiable(); - mockEntityType.Object.ExplicitProperties.Add(new MockPropertyInfo(), property.Object); + mockEntityType.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), property.Object); // Act new EntityKeyConvention().Apply(mockEntityType.Object, null); diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EdmTypeConfigurationExtensionsTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EdmTypeConfigurationExtensionsTest.cs index 449f51a262..ec24311a01 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EdmTypeConfigurationExtensionsTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EdmTypeConfigurationExtensionsTest.cs @@ -19,17 +19,17 @@ public void EntityType_DerivedProperties_ReturnsAllDerivedProperties() { // Arrange Mock entityA = new Mock(); - entityA.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("A1", entityA.Object)); - entityA.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("A2", entityA.Object)); + entityA.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("A1", entityA.Object)); + entityA.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("A2", entityA.Object)); Mock entityB = new Mock(); - entityB.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("B1", entityB.Object)); - entityB.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("B2", entityB.Object)); + entityB.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("B1", entityB.Object)); + entityB.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("B2", entityB.Object)); entityB.Setup(e => e.BaseType).Returns(entityA.Object); Mock entityC = new Mock(); - entityC.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("C1", entityC.Object)); - entityC.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("C2", entityC.Object)); + entityC.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("C1", entityC.Object)); + entityC.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("C2", entityC.Object)); entityC.Setup(e => e.BaseType).Returns(entityB.Object); // Act & Assert @@ -43,17 +43,17 @@ public void ComplexType_DerivedProperties_ReturnsAllDerivedProperties() { // Arrange Mock complexA = new Mock(); - complexA.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("A1", complexA.Object)); - complexA.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("A2", complexA.Object)); + complexA.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("A1", complexA.Object)); + complexA.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("A2", complexA.Object)); Mock complexB = new Mock(); - complexB.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("B1", complexB.Object)); - complexB.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("B2", complexB.Object)); + complexB.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("B1", complexB.Object)); + complexB.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("B2", complexB.Object)); complexB.Setup(e => e.BaseType).Returns(complexA.Object); Mock complexC = new Mock(); - complexC.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("C1", complexC.Object)); - complexC.Object.ExplicitProperties.Add(new MockPropertyInfo(), MockProperty("C2", complexC.Object)); + complexC.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("C1", complexC.Object)); + complexC.Object.ExplicitProperties.Add(new MockPropertyInfo().AsPropertyDescriptor(), MockProperty("C2", complexC.Object)); complexC.Setup(e => e.BaseType).Returns(complexB.Object); // Act & Assert @@ -437,7 +437,9 @@ private static PropertyConfiguration MockProperty(string name, StructuralTypeCon Mock propertyInfo = new Mock(); propertyInfo.Setup(p => p.Name).Returns(name); - Mock property = new Mock(propertyInfo.Object, declaringType); + Mock propertyDescriptor = new Mock(propertyInfo.Object); + + Mock property = new Mock(propertyDescriptor.Object, declaringType); return property.Object; } } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/PropertyConfigurationTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/PropertyConfigurationTest.cs index 4587e66e80..8a6a3e32b2 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/PropertyConfigurationTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/PropertyConfigurationTest.cs @@ -16,15 +16,18 @@ public class PropertyConfigurationTest private string _name = "name"; private StructuralTypeConfiguration _declaringType; private PropertyInfo _propertyInfo; + private PropertyDescriptor _propertyDescriptor; public PropertyConfigurationTest() { Mock mockPropertyInfo = new Mock(); _propertyInfo = mockPropertyInfo.Object; + Mock mockPropertyDescriptor = new Mock(_propertyInfo); + _propertyDescriptor = mockPropertyDescriptor.Object; Mock mockTypeConfig = new Mock(); _declaringType = mockTypeConfig.Object; Mock mockConfiguration = - new Mock(_propertyInfo, _declaringType) { CallBase = true }; + new Mock(_propertyDescriptor, _declaringType) { CallBase = true }; mockConfiguration.Object.Name = "Name"; _configuration = mockConfiguration.Object; } @@ -44,7 +47,7 @@ public void Property_DeclaringType_Get() [Fact] public void Property_PropertyInfo_Get() { - Assert.Equal(_propertyInfo, _configuration.PropertyInfo); + Assert.Equal(_propertyInfo, _configuration.PropertyInfo.PropertyInfo); } [Fact] diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Common/MockPropertyInfo.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Common/MockPropertyInfo.cs index 88a88899c4..32137f8834 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Common/MockPropertyInfo.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Common/MockPropertyInfo.cs @@ -3,6 +3,7 @@ using System; using System.Reflection; +using Microsoft.AspNet.OData.Builder; using Moq; namespace Microsoft.AspNet.OData.Test.Common @@ -44,5 +45,10 @@ public MockPropertyInfo Abstract() return this; } + + public PropertyDescriptor AsPropertyDescriptor() + { + return new PropertyDescriptor(this); + } } } diff --git a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl index 5ac7bb0c31..78ea9cfb9e 100644 --- a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl +++ b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl @@ -204,9 +204,10 @@ public sealed class Microsoft.AspNet.OData.ODataUriFunctions { } public class Microsoft.AspNet.OData.ClrPropertyInfoAnnotation { + public ClrPropertyInfoAnnotation (PropertyDescriptor propertyDescriptor) public ClrPropertyInfoAnnotation (System.Reflection.PropertyInfo clrPropertyInfo) - System.Reflection.PropertyInfo ClrPropertyInfo { public get; } + PropertyDescriptor ClrPropertyInfo { public get; } } public class Microsoft.AspNet.OData.ClrTypeAnnotation { @@ -1056,7 +1057,7 @@ public abstract class Microsoft.AspNet.OData.Builder.ParameterConfiguration { } public abstract class Microsoft.AspNet.OData.Builder.PropertyConfiguration { - protected PropertyConfiguration (System.Reflection.PropertyInfo property, StructuralTypeConfiguration declaringType) + protected PropertyConfiguration (PropertyDescriptor property, StructuralTypeConfiguration declaringType) bool AddedExplicitly { public get; public set; } bool AutoExpand { public get; public set; } @@ -1072,7 +1073,7 @@ public abstract class Microsoft.AspNet.OData.Builder.PropertyConfiguration { bool NotNavigable { public get; public set; } bool NotSortable { public get; public set; } int Order { public get; public set; } - System.Reflection.PropertyInfo PropertyInfo { public get; } + PropertyDescriptor PropertyInfo { public get; } QueryConfiguration QueryConfiguration { public get; public set; } System.Type RelatedClrType { public abstract get; } bool Unsortable { public get; public set; } @@ -1131,7 +1132,7 @@ public abstract class Microsoft.AspNet.OData.Builder.StructuralTypeConfiguration StructuralTypeConfiguration BaseTypeInternal { protected virtual get; } System.Type ClrType { public virtual get; } System.Reflection.PropertyInfo DynamicPropertyDictionary { public get; } - System.Collections.Generic.IDictionary`2[[System.Reflection.PropertyInfo],[Microsoft.AspNet.OData.Builder.PropertyConfiguration]] ExplicitProperties { protected get; } + System.Collections.Generic.IDictionary`2[[Microsoft.AspNet.OData.Builder.PropertyDescriptor],[Microsoft.AspNet.OData.Builder.PropertyConfiguration]] ExplicitProperties { protected get; } string FullName { public virtual get; } System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Reflection.PropertyInfo]] IgnoredProperties { public get; } System.Nullable`1[[System.Boolean]] IsAbstract { public virtual get; public virtual set; } @@ -1148,13 +1149,16 @@ public abstract class Microsoft.AspNet.OData.Builder.StructuralTypeConfiguration internal virtual void AbstractImpl () public virtual CollectionPropertyConfiguration AddCollectionProperty (System.Reflection.PropertyInfo propertyInfo) public virtual ComplexPropertyConfiguration AddComplexProperty (System.Reflection.PropertyInfo propertyInfo) + public virtual NavigationPropertyConfiguration AddContainedNavigationProperty (PropertyDescriptor navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual NavigationPropertyConfiguration AddContainedNavigationProperty (System.Reflection.PropertyInfo navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual void AddDynamicPropertyDictionary (System.Reflection.PropertyInfo propertyInfo) public virtual EnumPropertyConfiguration AddEnumProperty (System.Reflection.PropertyInfo propertyInfo) + public virtual NavigationPropertyConfiguration AddNavigationProperty (PropertyDescriptor navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual NavigationPropertyConfiguration AddNavigationProperty (System.Reflection.PropertyInfo navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual PrimitivePropertyConfiguration AddProperty (System.Reflection.PropertyInfo propertyInfo) internal virtual void DerivesFromImpl (StructuralTypeConfiguration baseType) internal virtual void DerivesFromNothingImpl () + public virtual void RemoveProperty (PropertyDescriptor propertyDescriptor) public virtual void RemoveProperty (System.Reflection.PropertyInfo propertyInfo) } @@ -1563,6 +1567,7 @@ public class Microsoft.AspNet.OData.Builder.NavigationPropertyBindingConfigurati } public class Microsoft.AspNet.OData.Builder.NavigationPropertyConfiguration : PropertyConfiguration { + public NavigationPropertyConfiguration (PropertyDescriptor propertyDecriptor, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) public NavigationPropertyConfiguration (System.Reflection.PropertyInfo property, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) bool ContainsTarget { public get; } @@ -1578,6 +1583,7 @@ public class Microsoft.AspNet.OData.Builder.NavigationPropertyConfiguration : Pr public NavigationPropertyConfiguration CascadeOnDelete (bool cascade) public NavigationPropertyConfiguration Contained () public NavigationPropertyConfiguration HasConstraint (System.Collections.Generic.KeyValuePair`2[[System.Reflection.PropertyInfo],[System.Reflection.PropertyInfo]] constraint) + public NavigationPropertyConfiguration HasConstraint (PropertyDescriptor dependentPropertyInfo, PropertyDescriptor principalPropertyInfo) public NavigationPropertyConfiguration HasConstraint (System.Reflection.PropertyInfo dependentPropertyInfo, System.Reflection.PropertyInfo principalPropertyInfo) public NavigationPropertyConfiguration NonContained () public NavigationPropertyConfiguration Optional () @@ -1701,6 +1707,25 @@ public class Microsoft.AspNet.OData.Builder.PrimitiveTypeConfiguration : IEdmTyp string Namespace { public virtual get; } } +public class Microsoft.AspNet.OData.Builder.PropertyDescriptor { + public PropertyDescriptor (System.Reflection.MethodInfo methodInfo) + public PropertyDescriptor (System.Reflection.PropertyInfo propertyInfo) + + System.Type DeclaringType { public get; } + System.Reflection.MemberInfo MemberInfo { public get; } + System.Reflection.MethodInfo MethodInfo { public get; } + string Name { public get; } + System.Reflection.PropertyInfo PropertyInfo { public get; } + System.Type PropertyType { public get; } + System.Type ReflectedType { public get; } + + public virtual bool Equals (object obj) + public T GetCustomAttribute (params bool inherit) + public IEnumerable`1 GetCustomAttributes (params bool inherit) + public object[] GetCustomAttributes (System.Type type, bool inherit) + public virtual int GetHashCode () +} + public class Microsoft.AspNet.OData.Builder.QueryConfiguration { public QueryConfiguration () From d0575d949d39962211dc133fdebe0becb39f967e Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Mon, 9 Jul 2018 14:41:25 +0300 Subject: [PATCH 02/11] FilterBinder add support extension method --- .../Query/Expressions/FilterBinder.cs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs index 13eaa1cb77..39831ad11e 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs @@ -11,6 +11,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using Microsoft.AspNet.OData.Adapters; +using Microsoft.AspNet.OData.Builder; using Microsoft.AspNet.OData.Common; using Microsoft.AspNet.OData.Formatter; using Microsoft.AspNet.OData.Interfaces; @@ -633,7 +634,10 @@ public virtual Expression BindSingleComplexNode(SingleComplexNode singleComplexN private Expression CreatePropertyAccessExpression(Expression source, IEdmProperty property, string propertyPath = null) { - string propertyName = EdmLibHelpers.GetClrPropertyName(property, Model); + PropertyDescriptor propertyDescriptor = EdmLibHelpers.GetClrPropertyDescriptor(property, Model); + string propertyName = propertyDescriptor!=null + ? propertyDescriptor.MemberInfo.Name + : EdmLibHelpers.GetClrPropertyName(property, Model); propertyPath = propertyPath ?? propertyName; if (QuerySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type) && source != _lambdaParameters[ODataItParameterName]) @@ -641,7 +645,8 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert var cleanSource = RemoveInnerNullPropagation(source); Expression propertyAccessExpression = null; - propertyAccessExpression = GetFlattenedPropertyExpression(propertyPath) ?? Expression.Property(cleanSource, propertyName); + propertyAccessExpression = GetFlattenedPropertyExpression(propertyPath) + ?? CreatePropertyAccessExpression(source, propertyName, propertyDescriptor); // source.property => source == null ? null : [CastToNullable]RemoveInnerNullPropagation(source).property // Notice that we are checking if source is null already. so we can safely remove any null checks when doing source.Property @@ -655,10 +660,24 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert } else { - return GetFlattenedPropertyExpression(propertyPath) ?? ConvertNonStandardPrimitives(Expression.Property(source, propertyName)); + return GetFlattenedPropertyExpression(propertyPath) + ?? ConvertNonStandardPrimitives(CreatePropertyAccessExpression(source, propertyName, propertyDescriptor)); } } + private Expression CreatePropertyAccessExpression(Expression source, string propertyName, PropertyDescriptor propertyDescriptor) + { + if (propertyDescriptor != null) + { + if (propertyDescriptor.MethodInfo != null) + return Expression.Call(null, propertyDescriptor.MethodInfo, source); + else + return Expression.Property(source, propertyDescriptor.PropertyInfo); + } + else + return Expression.Property(source, propertyName); + } + /// /// Binds a to create a LINQ that /// represents the semantics of the . From f87be6986b1d914cac74f528a9f6db453eb67c9b Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Tue, 10 Jul 2018 09:45:57 +0300 Subject: [PATCH 03/11] fix model building --- .../Builder/EdmModelHelperMethods.cs | 6 +++++ .../Builder/NavigationSourceConfiguration.cs | 25 ++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs b/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs index 30b90bbe59..6c25711292 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs @@ -176,6 +176,7 @@ private static string ConvertBindingPath(EdmTypeMap edmMap, NavigationPropertyBi { Type typeCast = TypeHelper.AsType(bindingInfo); PropertyInfo propertyInfo = bindingInfo as PropertyInfo; + MethodInfo methodInfo = bindingInfo as MethodInfo; if (typeCast != null) { @@ -187,6 +188,11 @@ private static string ConvertBindingPath(EdmTypeMap edmMap, NavigationPropertyBi PropertyDescriptor propDescr = new PropertyDescriptor(propertyInfo); bindings.Add(edmMap.EdmProperties[propDescr].Name); } + else if (methodInfo != null) + { + PropertyDescriptor propDescr = new PropertyDescriptor(methodInfo); + bindings.Add(edmMap.EdmProperties[propDescr].Name); + } } return String.Join("/", bindings); diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationSourceConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationSourceConfiguration.cs index 0d03f916cd..1efe10a45f 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationSourceConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationSourceConfiguration.cs @@ -7,6 +7,7 @@ using System.Diagnostics.Contracts; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Microsoft.AspNet.OData.Common; namespace Microsoft.AspNet.OData.Builder @@ -525,8 +526,8 @@ private void VerifyBindingPath(NavigationPropertyConfiguration navigationConfigu Contract.Assert(navigationConfiguration != null); Contract.Assert(bindingPath != null); - PropertyInfo navigation = bindingPath.Last() as PropertyInfo; - if (navigation == null || navigation != navigationConfiguration.PropertyInfo) + MemberInfo navigation = bindingPath.Last() as MemberInfo; + if (navigation == null || navigation != navigationConfiguration.PropertyInfo.MemberInfo) { throw Error.Argument("navigationConfiguration", SRResources.NavigationPropertyBindingPathIsNotValid, bindingPath.ConvertBindingPath(), navigationConfiguration.Name); @@ -552,13 +553,25 @@ private static Type VerifyBindingSegment(Type current, MemberInfo info) return derivedType.BaseType; } + Type declaringType = null; + Type propertyType = null; PropertyInfo propertyInfo = info as PropertyInfo; - if (propertyInfo == null) + MethodInfo methodInfo = info as MethodInfo; + if(propertyInfo!=null) + { + declaringType = info.DeclaringType; + propertyType = propertyInfo.PropertyType; + } + else if(methodInfo!=null && methodInfo.GetCustomAttribute()!=null) + { + declaringType = methodInfo.GetParameters().First().ParameterType; + propertyType = methodInfo.ReturnType; + } + else { throw Error.NotSupported(SRResources.NavigationPropertyBindingPathNotSupported, info.Name, info.MemberType); } - Type declaringType = propertyInfo.DeclaringType; if (declaringType == null || !(declaringType.IsAssignableFrom(current) || current.IsAssignableFrom(declaringType))) { @@ -567,12 +580,12 @@ private static Type VerifyBindingSegment(Type current, MemberInfo info) } Type elementType; - if (TypeHelper.IsCollection(propertyInfo.PropertyType, out elementType)) + if (TypeHelper.IsCollection(propertyType, out elementType)) { return elementType; } - return propertyInfo.PropertyType; + return propertyType; } } } From 674d25f2f9276e7e8310480cd38153e93d9bc7ea Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Mon, 16 Jul 2018 20:53:42 +0300 Subject: [PATCH 04/11] rename PropertyDescripor and refactor --- .../Builder/EdmModelHelperMethods.cs | 12 ++-- .../Builder/EdmTypeBuilder.cs | 6 +- .../Builder/EdmTypeMap.cs | 4 +- ...pertyDescriptor.cs => MemberDescriptor.cs} | 59 +++++++++---------- .../NavigationPropertyConfiguration.cs | 10 ++-- .../Builder/PropertyConfiguration.cs | 4 +- .../StructuralPropertyConfiguration.cs | 2 +- .../Builder/StructuralTypeConfiguration.cs | 20 +++---- ...turalTypeConfigurationOfTStructuralType.cs | 8 +-- .../ClrPropertyInfoAnnotation.cs | 6 +- .../Formatter/EdmLibHelpers.cs | 4 +- .../Microsoft.AspNet.OData.Shared.projitems | 2 +- .../Query/Expressions/AggregationBinder.cs | 25 +++++++- .../Query/Expressions/FilterBinder.cs | 12 ++-- .../Query/Expressions/SelectExpandBinder.cs | 12 ++-- .../SelectExpandWrapperConverter.cs | 12 ++-- .../AttributeEdmPropertyConventionTests.cs | 2 +- ...ContractAttributeEdmTypeConventionTests.cs | 2 +- ...mberAttributeEdmPropertyConventionTests.cs | 6 +- .../NotMappedAttributeConventionTests.cs | 4 +- .../Conventions/EntityKeyConventionTests.cs | 4 +- .../EdmTypeConfigurationExtensionsTest.cs | 2 +- .../Builder/PropertyConfigurationTest.cs | 4 +- .../Common/MockPropertyInfo.cs | 4 +- .../Microsoft.AspNetCore.OData.PublicApi.bsl | 58 +++++++++--------- 25 files changed, 151 insertions(+), 133 deletions(-) rename src/Microsoft.AspNet.OData.Shared/Builder/{PropertyDescriptor.cs => MemberDescriptor.cs} (66%) diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs b/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs index 6c25711292..7177cc1546 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs @@ -185,12 +185,12 @@ private static string ConvertBindingPath(EdmTypeMap edmMap, NavigationPropertyBi } else if (propertyInfo != null) { - PropertyDescriptor propDescr = new PropertyDescriptor(propertyInfo); + MemberDescriptor propDescr = new MemberDescriptor(propertyInfo); bindings.Add(edmMap.EdmProperties[propDescr].Name); } else if (methodInfo != null) { - PropertyDescriptor propDescr = new PropertyDescriptor(methodInfo); + MemberDescriptor propDescr = new MemberDescriptor(methodInfo); bindings.Add(edmMap.EdmProperties[propDescr].Name); } } @@ -431,7 +431,7 @@ private static Dictionary AddTypes(this EdmModel model, EdmTypeM model.AddClrTypeAnnotations(edmTypes); // add annotation for properties - Dictionary edmProperties = edmTypeMap.EdmProperties; + Dictionary edmProperties = edmTypeMap.EdmProperties; model.AddClrPropertyInfoAnnotations(edmProperties); model.AddPropertyRestrictionsAnnotations(edmTypeMap.EdmPropertiesRestrictions); model.AddPropertiesQuerySettings(edmTypeMap.EdmPropertiesQuerySettings); @@ -495,12 +495,12 @@ private static void AddClrTypeAnnotations(this EdmModel model, Dictionary edmProperties) + private static void AddClrPropertyInfoAnnotations(this EdmModel model, Dictionary edmProperties) { - foreach (KeyValuePair edmPropertyMap in edmProperties) + foreach (KeyValuePair edmPropertyMap in edmProperties) { IEdmProperty edmProperty = edmPropertyMap.Value; - PropertyDescriptor clrProperty = edmPropertyMap.Key; + MemberDescriptor clrProperty = edmPropertyMap.Key; if (clrProperty.MethodInfo != null || edmProperty.Name != clrProperty.Name) { model.SetAnnotationValue(edmProperty, new ClrPropertyInfoAnnotation(clrProperty)); diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/EdmTypeBuilder.cs b/src/Microsoft.AspNet.OData.Shared/Builder/EdmTypeBuilder.cs index 982d65a1ed..091b2d64aa 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/EdmTypeBuilder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/EdmTypeBuilder.cs @@ -22,7 +22,7 @@ internal class EdmTypeBuilder { private readonly List _configurations; private readonly Dictionary _types = new Dictionary(); - private readonly Dictionary _properties = new Dictionary(); + private readonly Dictionary _properties = new Dictionary(); private readonly Dictionary _propertiesRestrictions = new Dictionary(); private readonly Dictionary _propertiesQuerySettings = new Dictionary(); private readonly Dictionary _structuredTypeQuerySettings = new Dictionary(); @@ -433,7 +433,7 @@ private IList GetDeclaringPropertyInfo(IEnumerable GetDeclaringPropertyInfo(IEnumerable edmTypes, - Dictionary edmProperties, + Dictionary edmProperties, Dictionary edmPropertiesRestrictions, Dictionary edmPropertiesQuerySettings, Dictionary edmStructuredTypeQuerySettings, @@ -31,7 +31,7 @@ public EdmTypeMap( public Dictionary EdmTypes { get; private set; } - public Dictionary EdmProperties { get; private set; } + public Dictionary EdmProperties { get; private set; } public Dictionary EdmPropertiesRestrictions { get; private set; } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/PropertyDescriptor.cs b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs similarity index 66% rename from src/Microsoft.AspNet.OData.Shared/Builder/PropertyDescriptor.cs rename to src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs index 2efa5a7016..904e87d127 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/PropertyDescriptor.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs @@ -1,44 +1,45 @@ -using Microsoft.AspNet.OData.Common; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using System.Reflection; -using System.Text; +using Microsoft.AspNet.OData.Common; namespace Microsoft.AspNet.OData.Builder { /// - /// Property descriptor + /// Member descriptor /// - public class PropertyDescriptor + public class MemberDescriptor { - private readonly PropertyInfo _propertyInfo; - private readonly MethodInfo _methodInfo; + private readonly MemberInfo _memberInfo; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Property information - public PropertyDescriptor(PropertyInfo propertyInfo) + public MemberDescriptor(PropertyInfo propertyInfo) { if (propertyInfo == null) { throw Error.ArgumentNull("propertyInfo"); } - this._propertyInfo = propertyInfo; + this._memberInfo = propertyInfo; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// Extension property information - public PropertyDescriptor(MethodInfo methodInfo) + /// Extension method information + public MemberDescriptor(MethodInfo methodInfo) { - if(methodInfo==null) + if(methodInfo == null) { throw Error.ArgumentNull("methodInfo"); } - this._methodInfo = methodInfo; + this._memberInfo = methodInfo; } /// @@ -48,9 +49,7 @@ public MemberInfo MemberInfo { get { - if (_propertyInfo != null) - return _propertyInfo; - return _methodInfo; + return _memberInfo; } } @@ -61,7 +60,7 @@ public PropertyInfo PropertyInfo { get { - return _propertyInfo; + return _memberInfo as PropertyInfo; } } @@ -72,7 +71,7 @@ public MethodInfo MethodInfo { get { - return _methodInfo; + return _memberInfo as MethodInfo; } } @@ -94,9 +93,9 @@ public Type PropertyType { get { - if (_propertyInfo != null) - return _propertyInfo.PropertyType; - return _methodInfo.ReturnType; + if (PropertyInfo != null) + return PropertyInfo.PropertyType; + return MethodInfo.ReturnType; } } @@ -107,9 +106,9 @@ public Type DeclaringType { get { - if (_propertyInfo != null) - return _propertyInfo.DeclaringType; - return _methodInfo.GetParameters()[0].ParameterType; + if (PropertyInfo != null) + return PropertyInfo.DeclaringType; + return MethodInfo.GetParameters()[0].ParameterType; } } @@ -120,14 +119,14 @@ public Type ReflectedType { get { - if (_propertyInfo != null) - return TypeHelper.GetReflectedType(_propertyInfo); - return _methodInfo.GetParameters()[0].ParameterType; + if (PropertyInfo != null) + return TypeHelper.GetReflectedType(PropertyInfo); + return MethodInfo.GetParameters()[0].ParameterType; } } /// - public static implicit operator MemberInfo(PropertyDescriptor propertyDescriptor) + public static implicit operator MemberInfo(MemberDescriptor propertyDescriptor) { return propertyDescriptor.MemberInfo; } @@ -161,7 +160,7 @@ public override int GetHashCode() /// public override bool Equals(object obj) { - PropertyDescriptor propDescr = obj as PropertyDescriptor; + MemberDescriptor propDescr = obj as MemberDescriptor; if (propDescr == null) return false; diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs index 4bea1a8f9f..47e13645d8 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs @@ -40,7 +40,7 @@ public NavigationPropertyConfiguration(PropertyInfo property, EdmMultiplicity mu /// The backing CLR property. /// The . /// The declaring structural type. - public NavigationPropertyConfiguration(PropertyDescriptor propertyDecriptor, EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) + public NavigationPropertyConfiguration(MemberDescriptor propertyDecriptor, EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) : base(propertyDecriptor, declaringType) { if (propertyDecriptor == null) @@ -65,13 +65,13 @@ public NavigationPropertyConfiguration(PropertyDescriptor propertyDecriptor, Edm OnDeleteAction = EdmOnDeleteAction.None; } - private static PropertyDescriptor CreatePropertyDescriptor(PropertyInfo property) + private static MemberDescriptor CreatePropertyDescriptor(PropertyInfo property) { if(property == null) { throw Error.ArgumentNull("property"); } - return new PropertyDescriptor(property); + return new MemberDescriptor(property); } /// @@ -219,8 +219,8 @@ public NavigationPropertyConfiguration HasConstraint(PropertyInfo dependentPrope /// /// The dependent property info for the referential constraint. /// The principal property info for the referential constraint. - public NavigationPropertyConfiguration HasConstraint(PropertyDescriptor dependentPropertyInfo, - PropertyDescriptor principalPropertyInfo) + public NavigationPropertyConfiguration HasConstraint(MemberDescriptor dependentPropertyInfo, + MemberDescriptor principalPropertyInfo) { return HasConstraint(dependentPropertyInfo.PropertyInfo, principalPropertyInfo.PropertyInfo); } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/PropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/PropertyConfiguration.cs index f519e100ce..11cbc67ec6 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/PropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/PropertyConfiguration.cs @@ -20,7 +20,7 @@ public abstract class PropertyConfiguration /// /// The name of the property. /// The declaring EDM type of the property. - protected PropertyConfiguration(PropertyDescriptor property, StructuralTypeConfiguration declaringType) + protected PropertyConfiguration(MemberDescriptor property, StructuralTypeConfiguration declaringType) { if (property == null) { @@ -67,7 +67,7 @@ public string Name /// /// Gets the mapping CLR . /// - public PropertyDescriptor PropertyInfo { get; private set; } + public MemberDescriptor PropertyInfo { get; private set; } /// /// Gets the CLR of the property. diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralPropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralPropertyConfiguration.cs index 1eaf468cfd..142a638023 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralPropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralPropertyConfiguration.cs @@ -17,7 +17,7 @@ public abstract class StructuralPropertyConfiguration : PropertyConfiguration /// The property of the configuration. /// The declaring type of the property. protected StructuralPropertyConfiguration(PropertyInfo property, StructuralTypeConfiguration declaringType) - : base(new PropertyDescriptor(property), declaringType) + : base(new MemberDescriptor(property), declaringType) { OptionalProperty = EdmLibHelpers.IsNullable(property.PropertyType); } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs index cf5efa236e..ac38d6a60e 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs @@ -31,7 +31,7 @@ public abstract class StructuralTypeConfiguration : IEdmTypeConfiguration /// The default constructor is intended for use by unit testing only. protected StructuralTypeConfiguration() { - ExplicitProperties = new Dictionary(); + ExplicitProperties = new Dictionary(); RemovedProperties = new List(); QueryConfiguration = new QueryConfiguration(); } @@ -213,7 +213,7 @@ public virtual IEnumerable NavigationProperties /// /// Gets the collection of explicitly added properties. /// - protected internal IDictionary ExplicitProperties { get; private set; } + protected internal IDictionary ExplicitProperties { get; private set; } /// /// Gets the base type of this structural type. @@ -361,7 +361,7 @@ public virtual EnumPropertyConfiguration AddEnumProperty(PropertyInfo propertyIn return propertyConfiguration; } - internal ComplexPropertyConfiguration AddComplexProperty(PropertyDescriptor propertyDescriptor) + internal ComplexPropertyConfiguration AddComplexProperty(MemberDescriptor propertyDescriptor) { return AddComplexProperty(propertyDescriptor.PropertyInfo); } @@ -408,7 +408,7 @@ public virtual ComplexPropertyConfiguration AddComplexProperty(PropertyInfo prop return propertyConfiguration; } - internal CollectionPropertyConfiguration AddCollectionProperty(PropertyDescriptor propertyDescriptor) + internal CollectionPropertyConfiguration AddCollectionProperty(MemberDescriptor propertyDescriptor) { return AddCollectionProperty(propertyDescriptor.PropertyInfo); } @@ -502,7 +502,7 @@ public virtual void AddDynamicPropertyDictionary(PropertyInfo propertyInfo) /// Removes the given property. /// /// The property being removed. - public virtual void RemoveProperty(PropertyDescriptor propertyDescriptor) + public virtual void RemoveProperty(MemberDescriptor propertyDescriptor) { if (propertyDescriptor == null) { @@ -552,7 +552,7 @@ public virtual void RemoveProperty(PropertyInfo propertyInfo) /// Returns the of the added property. public virtual NavigationPropertyConfiguration AddNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity) { - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(navigationProperty); + MemberDescriptor propertyDescriptor = new MemberDescriptor(navigationProperty); return AddNavigationProperty(propertyDescriptor, multiplicity, containsTarget: false); } @@ -562,7 +562,7 @@ public virtual NavigationPropertyConfiguration AddNavigationProperty(PropertyInf /// The backing CLR property. /// The of the navigation property. /// Returns the of the added property. - public virtual NavigationPropertyConfiguration AddNavigationProperty(PropertyDescriptor navigationProperty, EdmMultiplicity multiplicity) + public virtual NavigationPropertyConfiguration AddNavigationProperty(MemberDescriptor navigationProperty, EdmMultiplicity multiplicity) { return AddNavigationProperty(navigationProperty, multiplicity, containsTarget: false); } @@ -575,7 +575,7 @@ public virtual NavigationPropertyConfiguration AddNavigationProperty(PropertyDes /// Returns the of the added property. public virtual NavigationPropertyConfiguration AddContainedNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity) { - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(navigationProperty); + MemberDescriptor propertyDescriptor = new MemberDescriptor(navigationProperty); return AddNavigationProperty(propertyDescriptor, multiplicity, containsTarget: true); } @@ -585,12 +585,12 @@ public virtual NavigationPropertyConfiguration AddContainedNavigationProperty(Pr /// The backing CLR property. /// The of the navigation property. /// Returns the of the added property. - public virtual NavigationPropertyConfiguration AddContainedNavigationProperty(PropertyDescriptor navigationProperty, EdmMultiplicity multiplicity) + public virtual NavigationPropertyConfiguration AddContainedNavigationProperty(MemberDescriptor navigationProperty, EdmMultiplicity multiplicity) { return AddNavigationProperty(navigationProperty, multiplicity, containsTarget: true); } - private NavigationPropertyConfiguration AddNavigationProperty(PropertyDescriptor navigationProperty, EdmMultiplicity multiplicity, bool containsTarget) + private NavigationPropertyConfiguration AddNavigationProperty(MemberDescriptor navigationProperty, EdmMultiplicity multiplicity, bool containsTarget) { if (navigationProperty == null) { diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfigurationOfTStructuralType.cs b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfigurationOfTStructuralType.cs index 034217be58..657b04d739 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfigurationOfTStructuralType.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfigurationOfTStructuralType.cs @@ -765,8 +765,8 @@ internal NavigationPropertyConfiguration GetOrCreateNavigationProperty(Expressio PropertyInfo pinfo = navigationProperty as PropertyInfo; MethodInfo minfo = navigationProperty as MethodInfo; - PropertyDescriptor propDescr = pinfo!=null ? new PropertyDescriptor(pinfo) - : new PropertyDescriptor(minfo); + MemberDescriptor propDescr = pinfo!=null ? new MemberDescriptor(pinfo) + : new MemberDescriptor(minfo); return _configuration.AddNavigationProperty(propDescr, multiplicity); } @@ -776,8 +776,8 @@ internal NavigationPropertyConfiguration GetOrCreateContainedNavigationProperty( PropertyInfo pinfo = navigationProperty as PropertyInfo; MethodInfo minfo = navigationProperty as MethodInfo; - PropertyDescriptor propDescr = pinfo != null ? new PropertyDescriptor(pinfo) - : new PropertyDescriptor(minfo); + MemberDescriptor propDescr = pinfo != null ? new MemberDescriptor(pinfo) + : new MemberDescriptor(minfo); return _configuration.AddContainedNavigationProperty(propDescr, multiplicity); } diff --git a/src/Microsoft.AspNet.OData.Shared/ClrPropertyInfoAnnotation.cs b/src/Microsoft.AspNet.OData.Shared/ClrPropertyInfoAnnotation.cs index 94fde282bc..e0bd8cc9ae 100644 --- a/src/Microsoft.AspNet.OData.Shared/ClrPropertyInfoAnnotation.cs +++ b/src/Microsoft.AspNet.OData.Shared/ClrPropertyInfoAnnotation.cs @@ -24,14 +24,14 @@ public ClrPropertyInfoAnnotation(PropertyInfo clrPropertyInfo) throw Error.ArgumentNull("clrPropertyInfo"); } - ClrPropertyInfo = new PropertyDescriptor(clrPropertyInfo); + ClrPropertyInfo = new MemberDescriptor(clrPropertyInfo); } /// /// Initializes a new instance of class. /// /// The backing CLR property info for the EDM property. - public ClrPropertyInfoAnnotation(PropertyDescriptor propertyDescriptor) + public ClrPropertyInfoAnnotation(MemberDescriptor propertyDescriptor) { if (propertyDescriptor == null) { @@ -44,6 +44,6 @@ public ClrPropertyInfoAnnotation(PropertyDescriptor propertyDescriptor) /// /// Gets the backing CLR property info for the EDM property. /// - public PropertyDescriptor ClrPropertyInfo { get; private set; } + public MemberDescriptor ClrPropertyInfo { get; private set; } } } diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs index 00d38a0a75..a7a64bd87c 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs @@ -694,7 +694,7 @@ public static string GetClrPropertyName(IEdmProperty edmProperty, IEdmModel edmM ClrPropertyInfoAnnotation annotation = edmModel.GetAnnotationValue(edmProperty); if (annotation != null) { - PropertyDescriptor propDescr = annotation.ClrPropertyInfo; + MemberDescriptor propDescr = annotation.ClrPropertyInfo; if (propDescr != null) { propertyName = propDescr.Name; @@ -704,7 +704,7 @@ public static string GetClrPropertyName(IEdmProperty edmProperty, IEdmModel edmM return propertyName; } - public static PropertyDescriptor GetClrPropertyDescriptor(IEdmProperty edmProperty, IEdmModel edmModel) + public static MemberDescriptor GetClrMemberDescriptor(IEdmProperty edmProperty, IEdmModel edmModel) { if (edmProperty == null) { diff --git a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems index bdf1646702..41d6746971 100644 --- a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems +++ b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems @@ -15,7 +15,7 @@ - + diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs index 39721c15fa..2a3e646561 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using Microsoft.AspNet.OData.Builder; using Microsoft.AspNet.OData.Common; using Microsoft.AspNet.OData.Formatter; using Microsoft.AspNet.OData.Interfaces; @@ -500,14 +501,18 @@ private Expression BindAccessor(QueryNode node, Expression baseElement = null) private Expression CreatePropertyAccessExpression(Expression source, IEdmProperty property, string propertyPath = null) { - string propertyName = EdmLibHelpers.GetClrPropertyName(property, Model); + MemberDescriptor memberDescriptor = EdmLibHelpers.GetClrMemberDescriptor(property, Model); + string propertyName = memberDescriptor != null + ? memberDescriptor.MemberInfo.Name + : EdmLibHelpers.GetClrPropertyName(property, Model); propertyPath = propertyPath ?? propertyName; if (QuerySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type) && source != this._lambdaParameter) { Expression cleanSource = RemoveInnerNullPropagation(source); Expression propertyAccessExpression = null; - propertyAccessExpression = GetFlattenedPropertyExpression(propertyPath) ?? Expression.Property(cleanSource, propertyName); + propertyAccessExpression = GetFlattenedPropertyExpression(propertyPath) + ?? CreatePropertyAccessExpression(source, propertyName, memberDescriptor); // source.property => source == null ? null : [CastToNullable]RemoveInnerNullPropagation(source).property // Notice that we are checking if source is null already. so we can safely remove any null checks when doing source.Property @@ -521,10 +526,24 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert } else { - return GetFlattenedPropertyExpression(propertyPath) ?? ConvertNonStandardPrimitives(Expression.Property(source, propertyName)); + return GetFlattenedPropertyExpression(propertyPath) + ?? ConvertNonStandardPrimitives(CreatePropertyAccessExpression(source, propertyName, memberDescriptor)); } } + private Expression CreatePropertyAccessExpression(Expression source, string propertyName, MemberDescriptor memberDescriptor) + { + if (memberDescriptor != null) + { + if (memberDescriptor.MethodInfo != null) + return Expression.Call(null, memberDescriptor.MethodInfo, source); + else + return Expression.Property(source, memberDescriptor.PropertyInfo); + } + else + return Expression.Property(source, propertyName); + } + private Expression CreateOpenPropertyAccessExpression(SingleValueOpenPropertyAccessNode openNode) { Expression sourceAccessor = BindAccessor(openNode.Source); diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs index 39831ad11e..aa2e137cc6 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs @@ -634,9 +634,9 @@ public virtual Expression BindSingleComplexNode(SingleComplexNode singleComplexN private Expression CreatePropertyAccessExpression(Expression source, IEdmProperty property, string propertyPath = null) { - PropertyDescriptor propertyDescriptor = EdmLibHelpers.GetClrPropertyDescriptor(property, Model); - string propertyName = propertyDescriptor!=null - ? propertyDescriptor.MemberInfo.Name + MemberDescriptor memberDescriptor = EdmLibHelpers.GetClrMemberDescriptor(property, Model); + string propertyName = memberDescriptor!=null + ? memberDescriptor.MemberInfo.Name : EdmLibHelpers.GetClrPropertyName(property, Model); propertyPath = propertyPath ?? propertyName; @@ -646,7 +646,7 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert Expression propertyAccessExpression = null; propertyAccessExpression = GetFlattenedPropertyExpression(propertyPath) - ?? CreatePropertyAccessExpression(source, propertyName, propertyDescriptor); + ?? CreatePropertyAccessExpression(source, propertyName, memberDescriptor); // source.property => source == null ? null : [CastToNullable]RemoveInnerNullPropagation(source).property // Notice that we are checking if source is null already. so we can safely remove any null checks when doing source.Property @@ -661,11 +661,11 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert else { return GetFlattenedPropertyExpression(propertyPath) - ?? ConvertNonStandardPrimitives(CreatePropertyAccessExpression(source, propertyName, propertyDescriptor)); + ?? ConvertNonStandardPrimitives(CreatePropertyAccessExpression(source, propertyName, memberDescriptor)); } } - private Expression CreatePropertyAccessExpression(Expression source, string propertyName, PropertyDescriptor propertyDescriptor) + private Expression CreatePropertyAccessExpression(Expression source, string propertyName, MemberDescriptor propertyDescriptor) { if (propertyDescriptor != null) { diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs index b90499e923..7cc5b4d31a 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs @@ -184,16 +184,16 @@ internal Expression CreatePropertyValueExpressionWithFilter(IEdmEntityType eleme source = Expression.TypeAs(source, castType); } - var propDescr = EdmLibHelpers.GetClrPropertyDescriptor(property, _model); + var memberDescriptor = EdmLibHelpers.GetClrMemberDescriptor(property, _model); string propertyName = null; Expression propertyValue; - if (propDescr != null) + if (memberDescriptor != null) { - propertyName = propDescr.Name; - if (propDescr.PropertyInfo != null) - propertyValue = Expression.Property(source, propDescr.PropertyInfo); + propertyName = memberDescriptor.Name; + if (memberDescriptor.PropertyInfo != null) + propertyValue = Expression.Property(source, memberDescriptor.PropertyInfo); else - propertyValue = Expression.Call(propDescr.MethodInfo, source); + propertyValue = Expression.Call(memberDescriptor.MethodInfo, source); } else { diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandWrapperConverter.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandWrapperConverter.cs index 79c5c0a936..46efbf681b 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandWrapperConverter.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandWrapperConverter.cs @@ -59,8 +59,8 @@ public JsonPropertyNameMapper(IEdmModel model, IEdmStructuredType type) public string MapProperty(string propertyName) { IEdmProperty property = _type.Properties().Single(s => s.Name == propertyName); - PropertyDescriptor info = GetPropertyInfo(property); - JsonPropertyAttribute jsonProperty = GetJsonProperty(info); + MemberDescriptor descriptor = GetMemberDescriptor(property); + JsonPropertyAttribute jsonProperty = GetJsonProperty(descriptor); if (jsonProperty != null && !String.IsNullOrWhiteSpace(jsonProperty.PropertyName)) { return jsonProperty.PropertyName; @@ -71,7 +71,7 @@ public string MapProperty(string propertyName) } } - private PropertyDescriptor GetPropertyInfo(IEdmProperty property) + private MemberDescriptor GetMemberDescriptor(IEdmProperty property) { ClrPropertyInfoAnnotation clrPropertyAnnotation = _model.GetAnnotationValue(property); if (clrPropertyAnnotation != null) @@ -85,12 +85,12 @@ private PropertyDescriptor GetPropertyInfo(IEdmProperty property) PropertyInfo info = clrTypeAnnotation.ClrType.GetProperty(property.Name); Contract.Assert(info != null); - return new PropertyDescriptor(info); + return new MemberDescriptor(info); } - private static JsonPropertyAttribute GetJsonProperty(PropertyDescriptor property) + private static JsonPropertyAttribute GetJsonProperty(MemberDescriptor memberDescriptor) { - return property.GetCustomAttributes(typeof(JsonPropertyAttribute), inherit: false) + return memberDescriptor.GetCustomAttributes(typeof(JsonPropertyAttribute), inherit: false) .OfType().SingleOrDefault(); } } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs index 9c3a9b9063..c62fb56321 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs @@ -60,7 +60,7 @@ public static bool Apply() Mock propertyConfiguration; if(typeof(TProperty) == typeof(PropertyConfiguration)) { - Mock propertyDescriptor = new Mock(property.Object); + Mock propertyDescriptor = new Mock(property.Object); propertyConfiguration = new Mock(propertyDescriptor.Object, structuralType.Object); } else if (typeof(TProperty) == typeof(NavigationPropertyConfiguration)) diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs index 1764f23f18..b99d134283 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs @@ -61,7 +61,7 @@ public void Apply_DoesnotRemove_ExplicitlyAddedProperties() _convention.Apply(entity, builder); // Assert - Assert.Contains(new PropertyDescriptor(propertyInfo), entity.ExplicitProperties.Keys); + Assert.Contains(new MemberDescriptor(propertyInfo), entity.ExplicitProperties.Keys); Assert.DoesNotContain(propertyInfo, entity.RemovedProperties); } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs index 96a809b139..eeb41a6cff 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs @@ -31,7 +31,7 @@ public void Apply_Calls_RemovesProperty() property.Setup(p => p.PropertyType).Returns(typeof(int)); property.Setup(p => p.GetCustomAttributes(It.IsAny())).Returns(new[] { new IgnoreDataMemberAttribute() }); - Mock propertyDescriptor = new Mock(property.Object); + Mock propertyDescriptor = new Mock(property.Object); Mock type = new Mock(); Mock structuralType = new Mock(MockBehavior.Strict); @@ -67,7 +67,7 @@ public void Apply_DoesnotRemoveProperty_TypeIsDataContractAndPropertyHasDataMemb new DataContractAttribute() }); - Mock propertyDescriptor = new Mock(property.Object); + Mock propertyDescriptor = new Mock(property.Object); Mock type = new Mock(); type.Setup(t => t.GetCustomAttributes(It.IsAny(), It.IsAny())).Returns(new[] { new DataContractAttribute() }); @@ -90,7 +90,7 @@ public void Apply_DoesnotRemove_ExplicitlyAddedProperties() // Arrange ODataConventionModelBuilder builder = ODataConventionModelBuilderFactory.Create(); PropertyInfo propertyInfo = typeof(TestEntity).GetProperty("ExplicitlyAddedProperty"); - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyInfo); + MemberDescriptor propertyDescriptor = new MemberDescriptor(propertyInfo); EntityTypeConfiguration entity = builder.AddEntityType(typeof(TestEntity)); PropertyConfiguration property = entity.AddProperty(propertyInfo); diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs index 3fe644cd12..628ac7a121 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs @@ -31,7 +31,7 @@ public void Apply_Calls_RemovesProperty_ForInferredProperties() property.Setup(p => p.PropertyType).Returns(typeof(int)); property.Setup(p => p.GetCustomAttributes(It.IsAny())).Returns(new[] { new NotMappedAttribute() }); - Mock propertyDescriptor = new Mock(property.Object); + Mock propertyDescriptor = new Mock(property.Object); Mock structuralType = new Mock(MockBehavior.Strict); structuralType.Setup(e => e.RemoveProperty(property.Object)).Verifiable(); @@ -53,7 +53,7 @@ public void Apply_DoesnotRemove_ExplicitlyAddedProperties() ODataConventionModelBuilder builder = ODataConventionModelBuilderFactory.Create(); PropertyInfo propertyInfo = typeof(TestEntity).GetProperty("Property"); - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyInfo); + MemberDescriptor propertyDescriptor = new MemberDescriptor(propertyInfo); EntityTypeConfiguration entity = builder.AddEntityType(typeof(TestEntity)); PropertyConfiguration property = entity.AddProperty(propertyInfo); diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/EntityKeyConventionTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/EntityKeyConventionTests.cs index 1d3187932f..0012097afb 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/EntityKeyConventionTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/EntityKeyConventionTests.cs @@ -24,7 +24,7 @@ public void Apply_Calls_HasKey_OnEdmType(string propertyName) { // Arrange Mock mockEntityType = new Mock(); - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(typeof(EntityKeyConventionTests_EntityType).GetProperty(propertyName)); + MemberDescriptor propertyDescriptor = new MemberDescriptor(typeof(EntityKeyConventionTests_EntityType).GetProperty(propertyName)); Mock property = new Mock(propertyDescriptor, mockEntityType.Object); mockEntityType.Setup(e => e.Name).Returns("SampleEntity"); @@ -43,7 +43,7 @@ public void Apply_Calls_HasKey_ForEnumProperty_OnEdmType() { // Arrange Mock mockEntityType = new Mock(); - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(typeof(EntityKeyConventionTests_EntityType).GetProperty("ColorId")); + MemberDescriptor propertyDescriptor = new MemberDescriptor(typeof(EntityKeyConventionTests_EntityType).GetProperty("ColorId")); Mock property = new Mock(propertyDescriptor, mockEntityType.Object); diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EdmTypeConfigurationExtensionsTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EdmTypeConfigurationExtensionsTest.cs index ec24311a01..2b298fcc65 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EdmTypeConfigurationExtensionsTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EdmTypeConfigurationExtensionsTest.cs @@ -437,7 +437,7 @@ private static PropertyConfiguration MockProperty(string name, StructuralTypeCon Mock propertyInfo = new Mock(); propertyInfo.Setup(p => p.Name).Returns(name); - Mock propertyDescriptor = new Mock(propertyInfo.Object); + Mock propertyDescriptor = new Mock(propertyInfo.Object); Mock property = new Mock(propertyDescriptor.Object, declaringType); return property.Object; diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/PropertyConfigurationTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/PropertyConfigurationTest.cs index 8a6a3e32b2..045010aa44 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/PropertyConfigurationTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/PropertyConfigurationTest.cs @@ -16,13 +16,13 @@ public class PropertyConfigurationTest private string _name = "name"; private StructuralTypeConfiguration _declaringType; private PropertyInfo _propertyInfo; - private PropertyDescriptor _propertyDescriptor; + private MemberDescriptor _propertyDescriptor; public PropertyConfigurationTest() { Mock mockPropertyInfo = new Mock(); _propertyInfo = mockPropertyInfo.Object; - Mock mockPropertyDescriptor = new Mock(_propertyInfo); + Mock mockPropertyDescriptor = new Mock(_propertyInfo); _propertyDescriptor = mockPropertyDescriptor.Object; Mock mockTypeConfig = new Mock(); _declaringType = mockTypeConfig.Object; diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Common/MockPropertyInfo.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Common/MockPropertyInfo.cs index 32137f8834..6ae5fd681f 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Common/MockPropertyInfo.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Common/MockPropertyInfo.cs @@ -46,9 +46,9 @@ public MockPropertyInfo Abstract() return this; } - public PropertyDescriptor AsPropertyDescriptor() + public MemberDescriptor AsPropertyDescriptor() { - return new PropertyDescriptor(this); + return new MemberDescriptor(this); } } } diff --git a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl index 7b22a3ffc5..105e1991fe 100644 --- a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl +++ b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl @@ -204,10 +204,10 @@ public sealed class Microsoft.AspNet.OData.ODataUriFunctions { } public class Microsoft.AspNet.OData.ClrPropertyInfoAnnotation { - public ClrPropertyInfoAnnotation (PropertyDescriptor propertyDescriptor) + public ClrPropertyInfoAnnotation (MemberDescriptor propertyDescriptor) public ClrPropertyInfoAnnotation (System.Reflection.PropertyInfo clrPropertyInfo) - PropertyDescriptor ClrPropertyInfo { public get; } + MemberDescriptor ClrPropertyInfo { public get; } } public class Microsoft.AspNet.OData.ClrTypeAnnotation { @@ -1057,7 +1057,7 @@ public abstract class Microsoft.AspNet.OData.Builder.ParameterConfiguration { } public abstract class Microsoft.AspNet.OData.Builder.PropertyConfiguration { - protected PropertyConfiguration (PropertyDescriptor property, StructuralTypeConfiguration declaringType) + protected PropertyConfiguration (MemberDescriptor property, StructuralTypeConfiguration declaringType) bool AddedExplicitly { public get; public set; } bool AutoExpand { public get; public set; } @@ -1073,7 +1073,7 @@ public abstract class Microsoft.AspNet.OData.Builder.PropertyConfiguration { bool NotNavigable { public get; public set; } bool NotSortable { public get; public set; } int Order { public get; public set; } - PropertyDescriptor PropertyInfo { public get; } + MemberDescriptor PropertyInfo { public get; } QueryConfiguration QueryConfiguration { public get; public set; } System.Type RelatedClrType { public abstract get; } bool Unsortable { public get; public set; } @@ -1132,7 +1132,7 @@ public abstract class Microsoft.AspNet.OData.Builder.StructuralTypeConfiguration StructuralTypeConfiguration BaseTypeInternal { protected virtual get; } System.Type ClrType { public virtual get; } System.Reflection.PropertyInfo DynamicPropertyDictionary { public get; } - System.Collections.Generic.IDictionary`2[[Microsoft.AspNet.OData.Builder.PropertyDescriptor],[Microsoft.AspNet.OData.Builder.PropertyConfiguration]] ExplicitProperties { protected get; } + System.Collections.Generic.IDictionary`2[[Microsoft.AspNet.OData.Builder.MemberDescriptor],[Microsoft.AspNet.OData.Builder.PropertyConfiguration]] ExplicitProperties { protected get; } string FullName { public virtual get; } System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Reflection.PropertyInfo]] IgnoredProperties { public get; } System.Nullable`1[[System.Boolean]] IsAbstract { public virtual get; public virtual set; } @@ -1149,16 +1149,16 @@ public abstract class Microsoft.AspNet.OData.Builder.StructuralTypeConfiguration internal virtual void AbstractImpl () public virtual CollectionPropertyConfiguration AddCollectionProperty (System.Reflection.PropertyInfo propertyInfo) public virtual ComplexPropertyConfiguration AddComplexProperty (System.Reflection.PropertyInfo propertyInfo) - public virtual NavigationPropertyConfiguration AddContainedNavigationProperty (PropertyDescriptor navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) + public virtual NavigationPropertyConfiguration AddContainedNavigationProperty (MemberDescriptor navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual NavigationPropertyConfiguration AddContainedNavigationProperty (System.Reflection.PropertyInfo navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual void AddDynamicPropertyDictionary (System.Reflection.PropertyInfo propertyInfo) public virtual EnumPropertyConfiguration AddEnumProperty (System.Reflection.PropertyInfo propertyInfo) - public virtual NavigationPropertyConfiguration AddNavigationProperty (PropertyDescriptor navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) + public virtual NavigationPropertyConfiguration AddNavigationProperty (MemberDescriptor navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual NavigationPropertyConfiguration AddNavigationProperty (System.Reflection.PropertyInfo navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual PrimitivePropertyConfiguration AddProperty (System.Reflection.PropertyInfo propertyInfo) internal virtual void DerivesFromImpl (StructuralTypeConfiguration baseType) internal virtual void DerivesFromNothingImpl () - public virtual void RemoveProperty (PropertyDescriptor propertyDescriptor) + public virtual void RemoveProperty (MemberDescriptor propertyDescriptor) public virtual void RemoveProperty (System.Reflection.PropertyInfo propertyInfo) } @@ -1549,6 +1549,25 @@ public class Microsoft.AspNet.OData.Builder.LowerCamelCaser { public virtual string ToLowerCamelCase (string name) } +public class Microsoft.AspNet.OData.Builder.MemberDescriptor { + public MemberDescriptor (System.Reflection.MethodInfo methodInfo) + public MemberDescriptor (System.Reflection.PropertyInfo propertyInfo) + + System.Type DeclaringType { public get; } + System.Reflection.MemberInfo MemberInfo { public get; } + System.Reflection.MethodInfo MethodInfo { public get; } + string Name { public get; } + System.Reflection.PropertyInfo PropertyInfo { public get; } + System.Type PropertyType { public get; } + System.Type ReflectedType { public get; } + + public virtual bool Equals (object obj) + public T GetCustomAttribute (params bool inherit) + public IEnumerable`1 GetCustomAttributes (params bool inherit) + public object[] GetCustomAttributes (System.Type type, bool inherit) + public virtual int GetHashCode () +} + public class Microsoft.AspNet.OData.Builder.NavigationLinkBuilder { public NavigationLinkBuilder (System.Func`3[[Microsoft.AspNet.OData.ResourceContext],[Microsoft.OData.Edm.IEdmNavigationProperty],[System.Uri]] navigationLinkFactory, bool followsConventions) @@ -1567,7 +1586,7 @@ public class Microsoft.AspNet.OData.Builder.NavigationPropertyBindingConfigurati } public class Microsoft.AspNet.OData.Builder.NavigationPropertyConfiguration : PropertyConfiguration { - public NavigationPropertyConfiguration (PropertyDescriptor propertyDecriptor, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) + public NavigationPropertyConfiguration (MemberDescriptor propertyDecriptor, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) public NavigationPropertyConfiguration (System.Reflection.PropertyInfo property, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) bool ContainsTarget { public get; } @@ -1583,7 +1602,7 @@ public class Microsoft.AspNet.OData.Builder.NavigationPropertyConfiguration : Pr public NavigationPropertyConfiguration CascadeOnDelete (bool cascade) public NavigationPropertyConfiguration Contained () public NavigationPropertyConfiguration HasConstraint (System.Collections.Generic.KeyValuePair`2[[System.Reflection.PropertyInfo],[System.Reflection.PropertyInfo]] constraint) - public NavigationPropertyConfiguration HasConstraint (PropertyDescriptor dependentPropertyInfo, PropertyDescriptor principalPropertyInfo) + public NavigationPropertyConfiguration HasConstraint (MemberDescriptor dependentPropertyInfo, MemberDescriptor principalPropertyInfo) public NavigationPropertyConfiguration HasConstraint (System.Reflection.PropertyInfo dependentPropertyInfo, System.Reflection.PropertyInfo principalPropertyInfo) public NavigationPropertyConfiguration NonContained () public NavigationPropertyConfiguration Optional () @@ -1707,25 +1726,6 @@ public class Microsoft.AspNet.OData.Builder.PrimitiveTypeConfiguration : IEdmTyp string Namespace { public virtual get; } } -public class Microsoft.AspNet.OData.Builder.PropertyDescriptor { - public PropertyDescriptor (System.Reflection.MethodInfo methodInfo) - public PropertyDescriptor (System.Reflection.PropertyInfo propertyInfo) - - System.Type DeclaringType { public get; } - System.Reflection.MemberInfo MemberInfo { public get; } - System.Reflection.MethodInfo MethodInfo { public get; } - string Name { public get; } - System.Reflection.PropertyInfo PropertyInfo { public get; } - System.Type PropertyType { public get; } - System.Type ReflectedType { public get; } - - public virtual bool Equals (object obj) - public T GetCustomAttribute (params bool inherit) - public IEnumerable`1 GetCustomAttributes (params bool inherit) - public object[] GetCustomAttributes (System.Type type, bool inherit) - public virtual int GetHashCode () -} - public class Microsoft.AspNet.OData.Builder.QueryConfiguration { public QueryConfiguration () From 3697874f5ed6a9bb5c434bbd2076e9b0d2f9b88f Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Tue, 17 Jul 2018 09:00:21 +0300 Subject: [PATCH 05/11] Fix documentation comments --- .../Builder/MemberDescriptor.cs | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs index 904e87d127..b67b8cf919 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs @@ -113,7 +113,7 @@ public Type DeclaringType } /// - /// Return the reflected type from a member info. + /// Returns the reflected type from a member info. /// public Type ReflectedType { @@ -125,39 +125,64 @@ public Type ReflectedType } } - /// - public static implicit operator MemberInfo(MemberDescriptor propertyDescriptor) + /// + /// Cast MemberDescriptor to MemberInfo. + /// + /// + public static implicit operator MemberInfo(MemberDescriptor memberDescriptor) { - return propertyDescriptor.MemberInfo; + return memberDescriptor.MemberInfo; } - /// + /// + /// Retrieves an array of the custom attributes applied to an assembly. A parameter specifies the assembly. + /// + /// The type of attribute to search for. Only attributes that are assignable to this type are returned. + /// true to inspect the ancestors of element; otherwise, false. + /// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to attributeType have been applied. public object[] GetCustomAttributes(Type type, bool inherit) { return MemberInfo.GetCustomAttributes(type, inherit); } - /// + /// + /// Retrieves a collection of custom attributes of a specified type that are applied to a specified member. + /// + /// The type of attribute to search for. + /// true to inspect the ancestors of element; otherwise, false. + /// A collection of the custom attributes that are applied to element and that match T, or an empty collection if no such attributes exist. public IEnumerable GetCustomAttributes(bool inherit=false) where T:Attribute { return MemberInfo.GetCustomAttributes(inherit); } - /// + /// + /// Retrieves a custom attribute of a specified type that is applied to a specified member, and optionally inspects the ancestors of that member. + /// + /// The type of attribute to search for. + /// true to inspect the ancestors of element; otherwise, false. + /// A custom attribute that matches T, or null if no such attribute is found. public T GetCustomAttribute(bool inherit = false) where T : Attribute { return MemberInfo.GetCustomAttribute(inherit); } - /// + /// + /// Serves hash function. + /// + /// Hash code. public override int GetHashCode() { return MemberInfo.GetHashCode(); } - /// + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { MemberDescriptor propDescr = obj as MemberDescriptor; From 9fdc18356afa6d4365d925625e20b5b0c5f52a57 Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Tue, 17 Jul 2018 09:30:43 +0300 Subject: [PATCH 06/11] fix CA errors. --- .../Builder/MemberDescriptor.cs | 40 +++++++++++++++++-- .../NavigationPropertyConfiguration.cs | 14 +++---- .../Query/Expressions/AggregationBinder.cs | 4 +- .../Query/Expressions/FilterBinder.cs | 4 +- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs index b67b8cf919..c7fbdae075 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Microsoft.AspNet.OData.Common; @@ -128,12 +129,21 @@ public Type ReflectedType /// /// Cast MemberDescriptor to MemberInfo. /// - /// + /// The object to compare with the current object. public static implicit operator MemberInfo(MemberDescriptor memberDescriptor) { return memberDescriptor.MemberInfo; } + /// + /// Returns a member information that represents the current object. + /// + /// A member information that represents the current object. + public MemberInfo ToMemberInfo() + { + return MemberInfo; + } + /// /// Retrieves an array of the custom attributes applied to an assembly. A parameter specifies the assembly. /// @@ -145,25 +155,47 @@ public object[] GetCustomAttributes(Type type, bool inherit) return MemberInfo.GetCustomAttributes(type, inherit); } + /// + /// Retrieves a collection of custom attributes of a specified type that are applied to a specified member. + /// + /// The type of attribute to search for. + /// A collection of the custom attributes that are applied to element and that match T, or an empty collection if no such attributes exist. + public IEnumerable GetCustomAttributes() + where T : Attribute + { + return GetCustomAttributes(false); + } + /// /// Retrieves a collection of custom attributes of a specified type that are applied to a specified member. /// /// The type of attribute to search for. /// true to inspect the ancestors of element; otherwise, false. /// A collection of the custom attributes that are applied to element and that match T, or an empty collection if no such attributes exist. - public IEnumerable GetCustomAttributes(bool inherit=false) + public IEnumerable GetCustomAttributes(bool inherit) where T:Attribute { return MemberInfo.GetCustomAttributes(inherit); } + /// + /// Retrieves a custom attribute of a specified type that is applied to a specified member, and optionally inspects the ancestors of that member. + /// + /// The type of attribute to search for. + /// A custom attribute that matches T, or null if no such attribute is found. + public T GetCustomAttribute() + where T : Attribute + { + return GetCustomAttribute(false); + } + /// /// Retrieves a custom attribute of a specified type that is applied to a specified member, and optionally inspects the ancestors of that member. /// /// The type of attribute to search for. /// true to inspect the ancestors of element; otherwise, false. /// A custom attribute that matches T, or null if no such attribute is found. - public T GetCustomAttribute(bool inherit = false) + public T GetCustomAttribute(bool inherit) where T : Attribute { return MemberInfo.GetCustomAttribute(inherit); @@ -181,7 +213,7 @@ public override int GetHashCode() /// /// Determines whether the specified object is equal to the current object. /// - /// The object to compare with the current object. + /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs index 47e13645d8..f14103c409 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs @@ -37,26 +37,26 @@ public NavigationPropertyConfiguration(PropertyInfo property, EdmMultiplicity mu /// /// Initializes a new instance of the class. /// - /// The backing CLR property. + /// The backing CLR property. /// The . /// The declaring structural type. - public NavigationPropertyConfiguration(MemberDescriptor propertyDecriptor, EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) - : base(propertyDecriptor, declaringType) + public NavigationPropertyConfiguration(MemberDescriptor memberDescriptor, EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) + : base(memberDescriptor, declaringType) { - if (propertyDecriptor == null) + if (memberDescriptor == null) { - throw Error.ArgumentNull("propertyDescripor"); + throw Error.ArgumentNull("memberDescripor"); } Multiplicity = multiplicity; - _relatedType = propertyDecriptor.PropertyType; + _relatedType = memberDescriptor.PropertyType; if (multiplicity == EdmMultiplicity.Many) { Type elementType; if (!TypeHelper.IsCollection(_relatedType, out elementType)) { - throw Error.Argument("property", SRResources.ManyToManyNavigationPropertyMustReturnCollection, propertyDecriptor.Name, TypeHelper.GetReflectedType(propertyDecriptor).Name); + throw Error.Argument("property", SRResources.ManyToManyNavigationPropertyMustReturnCollection, memberDescriptor.Name, TypeHelper.GetReflectedType(memberDescriptor).Name); } _relatedType = elementType; diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs index 2a3e646561..3866b91064 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs @@ -512,7 +512,7 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert Expression cleanSource = RemoveInnerNullPropagation(source); Expression propertyAccessExpression = null; propertyAccessExpression = GetFlattenedPropertyExpression(propertyPath) - ?? CreatePropertyAccessExpression(source, propertyName, memberDescriptor); + ?? CreatePropertyAccessExpression(cleanSource, propertyName, memberDescriptor); // source.property => source == null ? null : [CastToNullable]RemoveInnerNullPropagation(source).property // Notice that we are checking if source is null already. so we can safely remove any null checks when doing source.Property @@ -531,7 +531,7 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert } } - private Expression CreatePropertyAccessExpression(Expression source, string propertyName, MemberDescriptor memberDescriptor) + private static Expression CreatePropertyAccessExpression(Expression source, string propertyName, MemberDescriptor memberDescriptor) { if (memberDescriptor != null) { diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs index aa2e137cc6..3b94373011 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/FilterBinder.cs @@ -646,7 +646,7 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert Expression propertyAccessExpression = null; propertyAccessExpression = GetFlattenedPropertyExpression(propertyPath) - ?? CreatePropertyAccessExpression(source, propertyName, memberDescriptor); + ?? CreatePropertyAccessExpression(cleanSource, propertyName, memberDescriptor); // source.property => source == null ? null : [CastToNullable]RemoveInnerNullPropagation(source).property // Notice that we are checking if source is null already. so we can safely remove any null checks when doing source.Property @@ -665,7 +665,7 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert } } - private Expression CreatePropertyAccessExpression(Expression source, string propertyName, MemberDescriptor propertyDescriptor) + private static Expression CreatePropertyAccessExpression(Expression source, string propertyName, MemberDescriptor propertyDescriptor) { if (propertyDescriptor != null) { From 57008fff25b12e978c33784cfb62edfca640d68a Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Tue, 17 Jul 2018 10:15:28 +0300 Subject: [PATCH 07/11] update PublicApi for AspNet --- .../Microsoft.AspNet.OData.PublicApi.bsl | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test/PublicApi/Microsoft.AspNet.OData.PublicApi.bsl b/test/UnitTest/Microsoft.AspNet.OData.Test/PublicApi/Microsoft.AspNet.OData.PublicApi.bsl index 47ac7c730e..f914ee10b5 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test/PublicApi/Microsoft.AspNet.OData.PublicApi.bsl +++ b/test/UnitTest/Microsoft.AspNet.OData.Test/PublicApi/Microsoft.AspNet.OData.PublicApi.bsl @@ -197,9 +197,10 @@ public sealed class Microsoft.AspNet.OData.ODataUriFunctions { } public class Microsoft.AspNet.OData.ClrPropertyInfoAnnotation { + public ClrPropertyInfoAnnotation (MemberDescriptor propertyDescriptor) public ClrPropertyInfoAnnotation (System.Reflection.PropertyInfo clrPropertyInfo) - System.Reflection.PropertyInfo ClrPropertyInfo { public get; } + MemberDescriptor ClrPropertyInfo { public get; } } public class Microsoft.AspNet.OData.ClrTypeAnnotation { @@ -1008,7 +1009,7 @@ public abstract class Microsoft.AspNet.OData.Builder.ParameterConfiguration { } public abstract class Microsoft.AspNet.OData.Builder.PropertyConfiguration { - protected PropertyConfiguration (System.Reflection.PropertyInfo property, StructuralTypeConfiguration declaringType) + protected PropertyConfiguration (MemberDescriptor property, StructuralTypeConfiguration declaringType) bool AddedExplicitly { public get; public set; } bool AutoExpand { public get; public set; } @@ -1024,7 +1025,7 @@ public abstract class Microsoft.AspNet.OData.Builder.PropertyConfiguration { bool NotNavigable { public get; public set; } bool NotSortable { public get; public set; } int Order { public get; public set; } - System.Reflection.PropertyInfo PropertyInfo { public get; } + MemberDescriptor PropertyInfo { public get; } QueryConfiguration QueryConfiguration { public get; public set; } System.Type RelatedClrType { public abstract get; } bool Unsortable { public get; public set; } @@ -1083,7 +1084,7 @@ public abstract class Microsoft.AspNet.OData.Builder.StructuralTypeConfiguration StructuralTypeConfiguration BaseTypeInternal { protected virtual get; } System.Type ClrType { public virtual get; } System.Reflection.PropertyInfo DynamicPropertyDictionary { public get; } - System.Collections.Generic.IDictionary`2[[System.Reflection.PropertyInfo],[Microsoft.AspNet.OData.Builder.PropertyConfiguration]] ExplicitProperties { protected get; } + System.Collections.Generic.IDictionary`2[[Microsoft.AspNet.OData.Builder.MemberDescriptor],[Microsoft.AspNet.OData.Builder.PropertyConfiguration]] ExplicitProperties { protected get; } string FullName { public virtual get; } System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Reflection.PropertyInfo]] IgnoredProperties { public get; } System.Nullable`1[[System.Boolean]] IsAbstract { public virtual get; public virtual set; } @@ -1100,13 +1101,16 @@ public abstract class Microsoft.AspNet.OData.Builder.StructuralTypeConfiguration internal virtual void AbstractImpl () public virtual CollectionPropertyConfiguration AddCollectionProperty (System.Reflection.PropertyInfo propertyInfo) public virtual ComplexPropertyConfiguration AddComplexProperty (System.Reflection.PropertyInfo propertyInfo) + public virtual NavigationPropertyConfiguration AddContainedNavigationProperty (MemberDescriptor navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual NavigationPropertyConfiguration AddContainedNavigationProperty (System.Reflection.PropertyInfo navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual void AddDynamicPropertyDictionary (System.Reflection.PropertyInfo propertyInfo) public virtual EnumPropertyConfiguration AddEnumProperty (System.Reflection.PropertyInfo propertyInfo) + public virtual NavigationPropertyConfiguration AddNavigationProperty (MemberDescriptor navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual NavigationPropertyConfiguration AddNavigationProperty (System.Reflection.PropertyInfo navigationProperty, Microsoft.OData.Edm.EdmMultiplicity multiplicity) public virtual PrimitivePropertyConfiguration AddProperty (System.Reflection.PropertyInfo propertyInfo) internal virtual void DerivesFromImpl (StructuralTypeConfiguration baseType) internal virtual void DerivesFromNothingImpl () + public virtual void RemoveProperty (MemberDescriptor propertyDescriptor) public virtual void RemoveProperty (System.Reflection.PropertyInfo propertyInfo) } @@ -1497,6 +1501,28 @@ public class Microsoft.AspNet.OData.Builder.LowerCamelCaser { public virtual string ToLowerCamelCase (string name) } +public class Microsoft.AspNet.OData.Builder.MemberDescriptor { + public MemberDescriptor (System.Reflection.MethodInfo methodInfo) + public MemberDescriptor (System.Reflection.PropertyInfo propertyInfo) + + System.Type DeclaringType { public get; } + System.Reflection.MemberInfo MemberInfo { public get; } + System.Reflection.MethodInfo MethodInfo { public get; } + string Name { public get; } + System.Reflection.PropertyInfo PropertyInfo { public get; } + System.Type PropertyType { public get; } + System.Type ReflectedType { public get; } + + public virtual bool Equals (object obj) + public T GetCustomAttribute () + public T GetCustomAttribute (bool inherit) + public IEnumerable`1 GetCustomAttributes () + public IEnumerable`1 GetCustomAttributes (bool inherit) + public object[] GetCustomAttributes (System.Type type, bool inherit) + public virtual int GetHashCode () + public System.Reflection.MemberInfo ToMemberInfo () +} + public class Microsoft.AspNet.OData.Builder.NavigationLinkBuilder { public NavigationLinkBuilder (System.Func`3[[Microsoft.AspNet.OData.ResourceContext],[Microsoft.OData.Edm.IEdmNavigationProperty],[System.Uri]] navigationLinkFactory, bool followsConventions) @@ -1515,6 +1541,7 @@ public class Microsoft.AspNet.OData.Builder.NavigationPropertyBindingConfigurati } public class Microsoft.AspNet.OData.Builder.NavigationPropertyConfiguration : PropertyConfiguration { + public NavigationPropertyConfiguration (MemberDescriptor memberDescriptor, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) public NavigationPropertyConfiguration (System.Reflection.PropertyInfo property, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) bool ContainsTarget { public get; } @@ -1530,6 +1557,7 @@ public class Microsoft.AspNet.OData.Builder.NavigationPropertyConfiguration : Pr public NavigationPropertyConfiguration CascadeOnDelete (bool cascade) public NavigationPropertyConfiguration Contained () public NavigationPropertyConfiguration HasConstraint (System.Collections.Generic.KeyValuePair`2[[System.Reflection.PropertyInfo],[System.Reflection.PropertyInfo]] constraint) + public NavigationPropertyConfiguration HasConstraint (MemberDescriptor dependentPropertyInfo, MemberDescriptor principalPropertyInfo) public NavigationPropertyConfiguration HasConstraint (System.Reflection.PropertyInfo dependentPropertyInfo, System.Reflection.PropertyInfo principalPropertyInfo) public NavigationPropertyConfiguration NonContained () public NavigationPropertyConfiguration Optional () From d722b4524fdd27f003db917b156b88c2ea4837da Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Fri, 5 Jul 2019 15:45:20 +0300 Subject: [PATCH 08/11] Add test cases --- .../Builder/MemberDescriptor.cs | 2 +- .../NavigationPropertyConfiguration.cs | 10 ++-- .../Builder/PrimitivePropertyConfiguration.cs | 3 +- .../Builder/StructuralTypeConfiguration.cs | 4 +- .../Query/Expressions/SelectExpandBinder.cs | 2 +- .../Builder/EntityTypeTest.cs | 28 +++++++++++ .../Builder/MetadataTest.cs | 17 +++++++ .../NavigationPropertyConfigurationTest.cs | 2 +- .../Builder/TestModels/Company.cs | 8 +++ .../Builder/TestModels/Employee.cs | 8 +++ .../Builder/TestModels/Order.cs | 8 +++ .../Serialization/Models/Customer.cs | 8 +++ .../Expressions/AggregationBinderTests.cs | 50 ++++++++++++++++--- .../Query/Expressions/DataModel.cs | 21 ++++++++ .../Query/Expressions/FilterBinderTests.cs | 31 ++++++++++-- .../Expressions/SelectExpandBinderTest.cs | 45 +++++++++++++++++ .../TestCommon/UsefulBuilders.cs | 18 +++++++ .../Microsoft.AspNetCore.OData.PublicApi.bsl | 9 ++-- 18 files changed, 247 insertions(+), 27 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs index c7fbdae075..baafe7bdf8 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs @@ -129,7 +129,7 @@ public Type ReflectedType /// /// Cast MemberDescriptor to MemberInfo. /// - /// The object to compare with the current object. + /// The object to cast. public static implicit operator MemberInfo(MemberDescriptor memberDescriptor) { return memberDescriptor.MemberInfo; diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs index eb77f7562e..81e52cb4b0 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs @@ -45,7 +45,7 @@ public NavigationPropertyConfiguration(MemberDescriptor memberDescriptor, EdmMul { if (memberDescriptor == null) { - throw Error.ArgumentNull("memberDescripor"); + throw Error.ArgumentNull("memberDescriptor"); } Multiplicity = multiplicity; @@ -56,7 +56,7 @@ public NavigationPropertyConfiguration(MemberDescriptor memberDescriptor, EdmMul Type elementType; if (!TypeHelper.IsCollection(_relatedType, out elementType)) { - throw Error.Argument("property", SRResources.ManyToManyNavigationPropertyMustReturnCollection, memberDescriptor.Name, TypeHelper.GetReflectedType(memberDescriptor).Name); + throw Error.Argument("memberDescriptor", SRResources.ManyToManyNavigationPropertyMustReturnCollection, memberDescriptor.Name, TypeHelper.GetReflectedType(memberDescriptor).Name); } _relatedType = elementType; @@ -67,10 +67,8 @@ public NavigationPropertyConfiguration(MemberDescriptor memberDescriptor, EdmMul private static MemberDescriptor CreatePropertyDescriptor(PropertyInfo property) { - if(property == null) - { - throw Error.ArgumentNull("property"); - } + if (property == null) + return null; return new MemberDescriptor(property); } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/PrimitivePropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/PrimitivePropertyConfiguration.cs index 78eda0d09b..38aaa4372a 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/PrimitivePropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/PrimitivePropertyConfiguration.cs @@ -19,9 +19,8 @@ public class PrimitivePropertyConfiguration : StructuralPropertyConfiguration /// The name of the property. /// The declaring EDM type of the property. public PrimitivePropertyConfiguration(PropertyInfo property, StructuralTypeConfiguration declaringType) - :base(property, declaringType) + : base(property, declaringType) { - } /// diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs index 2b9a99e58d..a6e0c0b74b 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/StructuralTypeConfiguration.cs @@ -578,8 +578,8 @@ public virtual NavigationPropertyConfiguration AddNavigationProperty(MemberDescr /// Returns the of the added property. public virtual NavigationPropertyConfiguration AddContainedNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity) { - MemberDescriptor propertyDescriptor = new MemberDescriptor(navigationProperty); - return AddNavigationProperty(propertyDescriptor, multiplicity, containsTarget: true); + MemberDescriptor memberDescriptor = new MemberDescriptor(navigationProperty); + return AddNavigationProperty(memberDescriptor, multiplicity, containsTarget: true); } /// diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs index 103ac914e6..2934691d4a 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs @@ -225,7 +225,7 @@ internal Expression CreatePropertyValueExpressionWithFilter(IEdmEntityType eleme if (isCollection) { Expression filterSource = - typeof(IEnumerable).IsAssignableFrom(source.Type.GetProperty(propertyName).PropertyType) + typeof(IEnumerable).IsAssignableFrom(propertyValue.Type) ? Expression.Call( ExpressionHelperMethods.QueryableAsQueryable.MakeGenericMethod(clrElementType), nullablePropertyValue) diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EntityTypeTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EntityTypeTest.cs index f827b96e33..20e39a6e52 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EntityTypeTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EntityTypeTest.cs @@ -55,6 +55,34 @@ public void CreateEntityTypeWithRelationship() Assert.Equal(5, customerType.DeclaredProperties.Count()); } + [Fact] + public void CreateEntityTypeWithExtensionRelationship() + { + var builder = new ODataModelBuilder().Add_Customer_EntityType().Add_Order_EntityType().Add_OrderCustomer_Relationship_Extension(); + + var model = builder.GetServiceModel(); + var orderType = model.SchemaElements.OfType().SingleOrDefault(t => t.Name == "Order"); + Assert.NotNull(orderType); + Assert.Equal("Order", orderType.Name); + Assert.Equal(typeof(Order).Namespace, orderType.Namespace); + Assert.Equal("OrderId", orderType.DeclaredKey.Single().Name); + Assert.Equal(5, orderType.DeclaredProperties.Count()); + Assert.Single(orderType.NavigationProperties()); + var deliveryDateProperty = orderType.DeclaredProperties.Single(dp => dp.Name == "DeliveryDate"); + Assert.NotNull(deliveryDateProperty); + Assert.True(deliveryDateProperty.Type.IsNullable); + + Assert.Equal("CustomerExt", orderType.NavigationProperties().First().Name); + Assert.Equal("Customer", orderType.NavigationProperties().First().ToEntityType().Name); + + var customerType = model.SchemaElements.OfType().SingleOrDefault(t => t.Name == "Customer"); + Assert.NotNull(customerType); + Assert.Equal("Customer", customerType.Name); + Assert.Equal(typeof(Customer).Namespace, customerType.Namespace); + Assert.Equal("CustomerId", customerType.DeclaredKey.Single().Name); + Assert.Equal(5, customerType.DeclaredProperties.Count()); + } + [Fact] public void CanCreateEntityWithCompoundKey() { diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/MetadataTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/MetadataTest.cs index ac182e793e..715d8c5158 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/MetadataTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/MetadataTest.cs @@ -174,6 +174,23 @@ public void CanEmitModelWithSingletonHasEntitysetBinding() var csdl = GetCSDL(model); } + [Fact] + public void CanEmitModelWithExtensionMethodBinding() + { + //Arrange + var builder = new ODataModelBuilder() + .Add_Company_EntityType() + .Add_Employee_EntityType() + .Add_CompaniesEmployees_Binding_Extension() + .Add_EmployeeComplany_Relationship_Extension(); + + // Act + var model = builder.GetServiceModel(); + + //Assert + var csdl = GetCSDL(model); + } + public static string GetCSDL(IEdmModel model) { StringWriter writer = new StringWriter(); diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/NavigationPropertyConfigurationTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/NavigationPropertyConfigurationTest.cs index 0a27091930..995c987170 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/NavigationPropertyConfigurationTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/NavigationPropertyConfigurationTest.cs @@ -30,7 +30,7 @@ public void Ctor_ThrowsArgument_IfMultiplicityIsManyAndPropertyIsNotCollection() ExceptionAssert.Throws( () => new NavigationPropertyConfiguration(mockEntity.GetProperty("ID"), EdmMultiplicity.Many, new EntityTypeConfiguration()), - "The property 'ID' on the type 'T' is being configured as a Many-to-Many navigation property. Many to Many navigation properties must be collections.\r\nParameter name: property"); + "The property 'ID' on the type 'T' is being configured as a Many-to-Many navigation property. Many to Many navigation properties must be collections.\r\nParameter name: memberDescriptor"); } [Fact] diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Company.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Company.cs index b6f8d35298..37452e4299 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Company.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Company.cs @@ -19,4 +19,12 @@ public class Company public List Customers { get; set; } public List
Subsidiaries { get; set; } } + + public static class CompanyExtensions + { + public static IList CompanyEmployeesExt(this Company company) + { + return company.ComplanyEmployees; + } + } } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Employee.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Employee.cs index fb8e253dfe..3e562d2386 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Employee.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Employee.cs @@ -39,4 +39,12 @@ public class SalesPerson : Employee public Decimal Bonus { get; set; } public IList Customers { get; set; } } + + public static class EmployeeExtensions + { + public static Company WorkCompanyExt(this Employee employee) + { + return employee.WorkCompany; + } + } } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Order.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Order.cs index ff86a263fa..cdce8b4ee3 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Order.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/TestModels/Order.cs @@ -14,4 +14,12 @@ public class Order public DateTimeOffset OrderDate { get; set; } public DateTimeOffset? DeliveryDate { get; set; } } + + public static class OrderExtensions + { + public static Customer CustomerExt(this Order order) + { + return order.Customer; + } + } } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Serialization/Models/Customer.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Serialization/Models/Customer.cs index 970ee256e2..e32d3b1ee9 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Serialization/Models/Customer.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Serialization/Models/Customer.cs @@ -24,6 +24,14 @@ public Customer() public Address HomeAddress { get; set; } } + public static class CustomerExtensions + { + public static IList OrdersExt(this Customer customer) + { + return customer.Orders; + } + } + public class SpecialCustomer : Customer { public int Level { get; set; } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/AggregationBinderTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/AggregationBinderTests.cs index cd49ee7f70..6cf5e9f079 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/AggregationBinderTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/AggregationBinderTests.cs @@ -52,12 +52,45 @@ public void NavigationGroupBy() [Fact] public void NavigationMultipleGroupBy() - { + { var filters = VerifyQueryDeserialization( "groupby((Category/CategoryName, SupplierAddress/State))", ".GroupBy($it => new GroupByWrapper() {GroupByContainer = new NestedProperty() {Name = SupplierAddress, NestedValue = new GroupByWrapper() {GroupByContainer = new LastInChain() {Name = State, Value = $it.SupplierAddress.State, }, }, Next = new NestedPropertyLastInChain() {Name = Category, NestedValue = new GroupByWrapper() {GroupByContainer = new LastInChain() {Name = CategoryName, Value = $it.Category.CategoryName, }, }, }, }, })" + ".Select($it => new AggregationWrapper() {GroupByContainer = $it.Key.GroupByContainer, })"); } + + [Fact] + public void NavigationExtensionGroupBy() + { + Action modelCustomizer = builder => + { + var entityType = builder.EntityType(); + entityType.HasRequired(p => p.CategoryExt()); + }; + + var filters = VerifyQueryDeserialization( + "groupby((CategoryExt/CategoryName))", + ".GroupBy($it => new GroupByWrapper() {GroupByContainer = new NestedPropertyLastInChain() {Name = CategoryExt, NestedValue = new GroupByWrapper() {GroupByContainer = new LastInChain() {Name = CategoryName, Value = $it.CategoryExt().CategoryName, }, }, }, })" + + ".Select($it => new AggregationWrapper() {GroupByContainer = $it.Key.GroupByContainer, })", + modelCustomizer: modelCustomizer); + } + + [Fact] + public void NavigationExtensionMultipleGroupBy() + { + Action modelCustomizer = builder => + { + var entityType = builder.EntityType(); + entityType.HasRequired(p => p.CategoryExt()); + entityType.HasRequired(p => p.AlternateCategory()); + }; + + var filters = VerifyQueryDeserialization( + "groupby((CategoryExt/CategoryName, AlternateCategory/CategoryName))", + ".GroupBy($it => new GroupByWrapper() {GroupByContainer = new NestedProperty() {Name = AlternateCategory, NestedValue = new GroupByWrapper() {GroupByContainer = new LastInChain() {Name = CategoryName, Value = $it.AlternateCategory().CategoryName, }, }, Next = new NestedPropertyLastInChain() {Name = CategoryExt, NestedValue = new GroupByWrapper() {GroupByContainer = new LastInChain() {Name = CategoryName, Value = $it.CategoryExt().CategoryName, }, }, }, }, })" + + ".Select($it => new AggregationWrapper() {GroupByContainer = $it.Key.GroupByContainer, })", + modelCustomizer: modelCustomizer); + } [Fact] public void SingleDynamicGroupBy() @@ -160,14 +193,14 @@ public void ClassicEFQueryShape() classicEF: true); } - private Expression VerifyQueryDeserialization(string filter, string expectedResult = null, Action settingsCustomizer = null, bool classicEF = false) + private Expression VerifyQueryDeserialization(string filter, string expectedResult = null, Action settingsCustomizer = null, bool classicEF = false, Action modelCustomizer = null) { - return VerifyQueryDeserialization(filter, expectedResult, settingsCustomizer, classicEF); + return VerifyQueryDeserialization(filter, expectedResult, settingsCustomizer, classicEF, modelCustomizer); } - private Expression VerifyQueryDeserialization(string clauseString, string expectedResult = null, Action settingsCustomizer = null, bool classicEF = false) where T : class + private Expression VerifyQueryDeserialization(string clauseString, string expectedResult = null, Action settingsCustomizer = null, bool classicEF = false, Action modelCustomizer = null) where T : class { - IEdmModel model = GetModel(); + IEdmModel model = GetModel(modelCustomizer); ApplyClause clause = CreateApplyNode(clauseString, model, typeof(T)); IWebApiAssembliesResolver assembliesResolver = WebApiAssembliesResolverFactory.Create(); @@ -231,7 +264,7 @@ private ApplyClause CreateApplyNode(string clause, IEdmModel model, Type entityT return parser.ParseApply(); } - private IEdmModel GetModel() where T : class + private IEdmModel GetModel(Action modelCustomizer) where T : class { Type key = typeof(T); IEdmModel value; @@ -246,6 +279,11 @@ private IEdmModel GetModel() where T : class model.EntityType().DerivesFrom(); } + if (modelCustomizer != null) + { + modelCustomizer(model); + } + value = _modelCache[key] = model.GetEdmModel(); } return value; diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/DataModel.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/DataModel.cs index 5db4e5099a..c9bac31ffd 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/DataModel.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/DataModel.cs @@ -60,6 +60,19 @@ public class Product public Address[] NotFilterableAlternateAddresses { get; set; } } + public static class ProductExtensions + { + public static Category CategoryExt(this Product product) + { + return product.Category; + } + + public static Category AlternateCategory(this Product product) + { + return null; + } + } + public class Category { public int CategoryID { get; set; } @@ -73,6 +86,14 @@ public class Category public IQueryable QueryableProducts { get; set; } } + public static class CategoryExtensions + { + public static IEnumerable EnumerableProductsExt(this Category category) + { + return category.EnumerableProducts; + } + } + public class Address { public string City { get; set; } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/FilterBinderTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/FilterBinderTests.cs index a1f872b547..09fe51e7d6 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/FilterBinderTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/FilterBinderTests.cs @@ -523,6 +523,22 @@ public void ComplexPropertyNavigation() new { WithNullPropagation = true, WithoutNullPropagation = true }); } + [Fact] + public void NavigationPropertyExtension() + { + Action modelCustomizer = builder => builder.EntityType().HasRequired(p => p.CategoryExt()); + + var filters = VerifyQueryDeserialization("Category/CategoryName eq 'Electronic'", modelCustomizer: modelCustomizer); + + RunFilters(filters, + new Product { }, + new { WithNullPropagation = false, WithoutNullPropagation = typeof(NullReferenceException) }); + + RunFilters(filters, + new Product { Category = new Category { CategoryName = "Electronic" } }, + new { WithNullPropagation = true, WithoutNullPropagation = true }); + } + #region Any/All [Fact] @@ -2933,14 +2949,14 @@ private bool RunFilter(Expression> filter, T instance) return filter.Compile().Invoke(instance); } - private dynamic VerifyQueryDeserialization(string filter, string expectedResult = null, string expectedResultWithNullPropagation = null, Action settingsCustomizer = null) + private dynamic VerifyQueryDeserialization(string filter, string expectedResult = null, string expectedResultWithNullPropagation = null, Action settingsCustomizer = null, Action modelCustomizer = null) { - return VerifyQueryDeserialization(filter, expectedResult, expectedResultWithNullPropagation, settingsCustomizer); + return VerifyQueryDeserialization(filter, expectedResult, expectedResultWithNullPropagation, settingsCustomizer, modelCustomizer); } - private dynamic VerifyQueryDeserialization(string filter, string expectedResult = null, string expectedResultWithNullPropagation = null, Action settingsCustomizer = null) where T : class + private dynamic VerifyQueryDeserialization(string filter, string expectedResult = null, string expectedResultWithNullPropagation = null, Action settingsCustomizer = null, Action modelCustomizer = null) where T : class { - IEdmModel model = GetModel(); + IEdmModel model = GetModel(modelCustomizer); FilterClause filterNode = CreateFilterNode(filter, model, typeof(T)); IWebApiAssembliesResolver assembliesResolver = WebApiAssembliesResolverFactory.Create(); @@ -2994,7 +3010,7 @@ private void VerifyExpression(Expression filter, string expectedExpression) String.Format("Expected expression '{0}' but the deserializer produced '{1}'", expectedExpression, resultExpression)); } - private IEdmModel GetModel() where T : class + private IEdmModel GetModel(Action modelCustomizer = null) where T : class { Type key = typeof(T); IEdmModel value; @@ -3009,6 +3025,11 @@ private IEdmModel GetModel() where T : class model.EntityType().DerivesFrom(); } + if (modelCustomizer != null) + { + modelCustomizer(model); + } + value = _modelCache[key] = model.GetEdmModel(); } return value; diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/SelectExpandBinderTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/SelectExpandBinderTest.cs index d8e2c79906..9a434218db 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/SelectExpandBinderTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/SelectExpandBinderTest.cs @@ -828,6 +828,51 @@ public void CreatePropertyValueExpressionWithFilter_Collection_Works_HandleNullP Assert.Equal(1, orders.ToList()[0].ID); } + [Fact] + public void CreatePropertyValueExpressionWithFilter_Collection_Works_NavigationExtension() + { + // Arrange + var ordersProperty = _model.Customer.AddUnidirectionalNavigation( + new EdmNavigationPropertyInfo + { + Name = "OrdersExt", + Target = _model.Order, + TargetMultiplicity = EdmMultiplicity.Many + }); + _model.Model.SetAnnotationValue(_model.Order, new ClrTypeAnnotation(typeof(Order))); + var extensionMethod = typeof(CustomerExtensions).GetMethod("OrdersExt", BindingFlags.Static|BindingFlags.Public); + var memberDescriptor = new MemberDescriptor(extensionMethod); + _model.Model.SetAnnotationValue(ordersProperty, new ClrPropertyInfoAnnotation(memberDescriptor)); + _settings.HandleNullPropagation = HandleNullPropagationOption.True; + var customer = + Expression.Constant(new Customer { Orders = new[] { new Order { ID = 1 }, new Order { ID = 2 } } }); + var parser = new ODataQueryOptionParser( + _model.Model, + _model.Order, + _model.Orders, + new Dictionary { { "$filter", "ID eq 1" } }); + var filterCaluse = parser.ParseFilter(); + + // Act + var filterInExpand = _binder.CreatePropertyValueExpressionWithFilter( + _model.Customer, + ordersProperty, + customer, + filterCaluse); + + // Assert + Assert.Equal( + string.Format( + "IIF((value({0}) == null), null, IIF((value({0}).OrdersExt() == null), null, " + + "value({0}).OrdersExt().AsQueryable().Where($it => ($it.ID == value({1}).TypedProperty))))", + customer.Type, + "Microsoft.AspNet.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.Int32]"), + filterInExpand.ToString()); + var orders = Expression.Lambda(filterInExpand).Compile().DynamicInvoke() as IEnumerable; + Assert.Single(orders); + Assert.Equal(1, orders.ToList()[0].ID); + } + [Fact] public void CreatePropertyValueExpressionWithFilter_Collection_Works_HandleNullPropagationOptionIsFalse() { diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/TestCommon/UsefulBuilders.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/TestCommon/UsefulBuilders.cs index 6752266aa0..582cbd98cb 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/TestCommon/UsefulBuilders.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/TestCommon/UsefulBuilders.cs @@ -229,6 +229,12 @@ public static ODataModelBuilder Add_EmployeeComplany_Relationship(this ODataMode return builder; } + public static ODataModelBuilder Add_EmployeeComplany_Relationship_Extension(this ODataModelBuilder builder) + { + builder.EntityType().HasRequired(o => o.WorkCompanyExt()); + return builder; + } + // EntitySet -> EntitySet public static ODataModelBuilder Add_CompaniesEmployees_Binding(this ODataModelBuilder builder) { @@ -236,6 +242,12 @@ public static ODataModelBuilder Add_CompaniesEmployees_Binding(this ODataModelBu return builder; } + public static ODataModelBuilder Add_CompaniesEmployees_Binding_Extension(this ODataModelBuilder builder) + { + builder.EntitySet("Companies").HasManyBinding(c => c.CompanyEmployeesExt(), "Employees"); + return builder; + } + // EntitySet -> Singleton public static ODataModelBuilder Add_CompaniesCEO_Binding(this ODataModelBuilder builder) { @@ -269,6 +281,12 @@ public static ODataModelBuilder Add_OrderCustomer_Relationship(this ODataModelBu return builder; } + public static ODataModelBuilder Add_OrderCustomer_Relationship_Extension(this ODataModelBuilder builder) + { + builder.EntityType().HasRequired(o => o.CustomerExt()); + return builder; + } + public static ODataModelBuilder Add_Customers_EntitySet(this ODataModelBuilder builder) { builder.Add_Customer_EntityType().EntitySet("Customers"); diff --git a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl index d6c22da4c3..72164d034a 100644 --- a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl +++ b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl @@ -1580,10 +1580,13 @@ public class Microsoft.AspNet.OData.Builder.MemberDescriptor { System.Type ReflectedType { public get; } public virtual bool Equals (object obj) - public T GetCustomAttribute (params bool inherit) - public IEnumerable`1 GetCustomAttributes (params bool inherit) + public T GetCustomAttribute () + public T GetCustomAttribute (bool inherit) + public IEnumerable`1 GetCustomAttributes () + public IEnumerable`1 GetCustomAttributes (bool inherit) public object[] GetCustomAttributes (System.Type type, bool inherit) public virtual int GetHashCode () + public System.Reflection.MemberInfo ToMemberInfo () } public class Microsoft.AspNet.OData.Builder.NavigationLinkBuilder { @@ -1604,7 +1607,7 @@ public class Microsoft.AspNet.OData.Builder.NavigationPropertyBindingConfigurati } public class Microsoft.AspNet.OData.Builder.NavigationPropertyConfiguration : PropertyConfiguration { - public NavigationPropertyConfiguration (MemberDescriptor propertyDecriptor, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) + public NavigationPropertyConfiguration (MemberDescriptor memberDescriptor, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) public NavigationPropertyConfiguration (System.Reflection.PropertyInfo property, Microsoft.OData.Edm.EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) bool ContainsTarget { public get; } From 00ff61c9460153bb5a4e16e19a15c670d522167a Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Wed, 17 Jul 2019 09:25:34 +0300 Subject: [PATCH 09/11] tests fixed --- .../Expressions/AggregationBinderTests.cs | 37 +++++-------------- .../Query/Expressions/FilterBinderTests.cs | 20 ++++------ 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/AggregationBinderTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/AggregationBinderTests.cs index 6cf5e9f079..fa690fb53d 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/AggregationBinderTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/AggregationBinderTests.cs @@ -62,34 +62,20 @@ public void NavigationMultipleGroupBy() [Fact] public void NavigationExtensionGroupBy() { - Action modelCustomizer = builder => - { - var entityType = builder.EntityType(); - entityType.HasRequired(p => p.CategoryExt()); - }; - var filters = VerifyQueryDeserialization( "groupby((CategoryExt/CategoryName))", ".GroupBy($it => new GroupByWrapper() {GroupByContainer = new NestedPropertyLastInChain() {Name = CategoryExt, NestedValue = new GroupByWrapper() {GroupByContainer = new LastInChain() {Name = CategoryName, Value = $it.CategoryExt().CategoryName, }, }, }, })" - + ".Select($it => new AggregationWrapper() {GroupByContainer = $it.Key.GroupByContainer, })", - modelCustomizer: modelCustomizer); + + ".Select($it => new AggregationWrapper() {GroupByContainer = $it.Key.GroupByContainer, })"); } [Fact] public void NavigationExtensionMultipleGroupBy() { - Action modelCustomizer = builder => - { - var entityType = builder.EntityType(); - entityType.HasRequired(p => p.CategoryExt()); - entityType.HasRequired(p => p.AlternateCategory()); - }; - + var filters = VerifyQueryDeserialization( "groupby((CategoryExt/CategoryName, AlternateCategory/CategoryName))", ".GroupBy($it => new GroupByWrapper() {GroupByContainer = new NestedProperty() {Name = AlternateCategory, NestedValue = new GroupByWrapper() {GroupByContainer = new LastInChain() {Name = CategoryName, Value = $it.AlternateCategory().CategoryName, }, }, Next = new NestedPropertyLastInChain() {Name = CategoryExt, NestedValue = new GroupByWrapper() {GroupByContainer = new LastInChain() {Name = CategoryName, Value = $it.CategoryExt().CategoryName, }, }, }, }, })" - + ".Select($it => new AggregationWrapper() {GroupByContainer = $it.Key.GroupByContainer, })", - modelCustomizer: modelCustomizer); + + ".Select($it => new AggregationWrapper() {GroupByContainer = $it.Key.GroupByContainer, })"); } [Fact] @@ -193,14 +179,14 @@ public void ClassicEFQueryShape() classicEF: true); } - private Expression VerifyQueryDeserialization(string filter, string expectedResult = null, Action settingsCustomizer = null, bool classicEF = false, Action modelCustomizer = null) + private Expression VerifyQueryDeserialization(string filter, string expectedResult = null, Action settingsCustomizer = null, bool classicEF = false) { - return VerifyQueryDeserialization(filter, expectedResult, settingsCustomizer, classicEF, modelCustomizer); + return VerifyQueryDeserialization(filter, expectedResult, settingsCustomizer, classicEF); } - private Expression VerifyQueryDeserialization(string clauseString, string expectedResult = null, Action settingsCustomizer = null, bool classicEF = false, Action modelCustomizer = null) where T : class + private Expression VerifyQueryDeserialization(string clauseString, string expectedResult = null, Action settingsCustomizer = null, bool classicEF = false) where T : class { - IEdmModel model = GetModel(modelCustomizer); + IEdmModel model = GetModel(); ApplyClause clause = CreateApplyNode(clauseString, model, typeof(T)); IWebApiAssembliesResolver assembliesResolver = WebApiAssembliesResolverFactory.Create(); @@ -264,7 +250,7 @@ private ApplyClause CreateApplyNode(string clause, IEdmModel model, Type entityT return parser.ParseApply(); } - private IEdmModel GetModel(Action modelCustomizer) where T : class + private IEdmModel GetModel() where T : class { Type key = typeof(T); IEdmModel value; @@ -277,11 +263,8 @@ private IEdmModel GetModel(Action modelCustomizer) where T { model.EntityType().DerivesFrom(); model.EntityType().DerivesFrom(); - } - - if (modelCustomizer != null) - { - modelCustomizer(model); + model.EntityType().HasRequired(p => p.CategoryExt()); + model.EntityType().HasRequired(p => p.AlternateCategory()); } value = _modelCache[key] = model.GetEdmModel(); diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/FilterBinderTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/FilterBinderTests.cs index 09fe51e7d6..11e302ee83 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/FilterBinderTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Query/Expressions/FilterBinderTests.cs @@ -526,9 +526,7 @@ public void ComplexPropertyNavigation() [Fact] public void NavigationPropertyExtension() { - Action modelCustomizer = builder => builder.EntityType().HasRequired(p => p.CategoryExt()); - - var filters = VerifyQueryDeserialization("Category/CategoryName eq 'Electronic'", modelCustomizer: modelCustomizer); + var filters = VerifyQueryDeserialization("Category/CategoryName eq 'Electronic'"); RunFilters(filters, new Product { }, @@ -2949,14 +2947,14 @@ private bool RunFilter(Expression> filter, T instance) return filter.Compile().Invoke(instance); } - private dynamic VerifyQueryDeserialization(string filter, string expectedResult = null, string expectedResultWithNullPropagation = null, Action settingsCustomizer = null, Action modelCustomizer = null) + private dynamic VerifyQueryDeserialization(string filter, string expectedResult = null, string expectedResultWithNullPropagation = null, Action settingsCustomizer = null) { - return VerifyQueryDeserialization(filter, expectedResult, expectedResultWithNullPropagation, settingsCustomizer, modelCustomizer); + return VerifyQueryDeserialization(filter, expectedResult, expectedResultWithNullPropagation, settingsCustomizer); } - private dynamic VerifyQueryDeserialization(string filter, string expectedResult = null, string expectedResultWithNullPropagation = null, Action settingsCustomizer = null, Action modelCustomizer = null) where T : class + private dynamic VerifyQueryDeserialization(string filter, string expectedResult = null, string expectedResultWithNullPropagation = null, Action settingsCustomizer = null) where T : class { - IEdmModel model = GetModel(modelCustomizer); + IEdmModel model = GetModel(); FilterClause filterNode = CreateFilterNode(filter, model, typeof(T)); IWebApiAssembliesResolver assembliesResolver = WebApiAssembliesResolverFactory.Create(); @@ -3010,7 +3008,7 @@ private void VerifyExpression(Expression filter, string expectedExpression) String.Format("Expected expression '{0}' but the deserializer produced '{1}'", expectedExpression, resultExpression)); } - private IEdmModel GetModel(Action modelCustomizer = null) where T : class + private IEdmModel GetModel() where T : class { Type key = typeof(T); IEdmModel value; @@ -3023,11 +3021,7 @@ private IEdmModel GetModel(Action modelCustomizer = null) { model.EntityType().DerivesFrom(); model.EntityType().DerivesFrom(); - } - - if (modelCustomizer != null) - { - modelCustomizer(model); + model.EntityType().HasRequired(p=>p.CategoryExt()); } value = _modelCache[key] = model.GetEdmModel(); From d0a39a7a5be18643c06f56a4ca7a99110f4f2065 Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Wed, 17 Jul 2019 09:27:02 +0300 Subject: [PATCH 10/11] fixed code style issues --- .../Builder/MemberDescriptor.cs | 26 +++++++++++++++---- .../Builder/NavigationSourceConfiguration.cs | 4 +-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs index baafe7bdf8..f6cadb9e80 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs @@ -36,10 +36,11 @@ public MemberDescriptor(PropertyInfo propertyInfo) /// Extension method information public MemberDescriptor(MethodInfo methodInfo) { - if(methodInfo == null) + if (methodInfo == null) { throw Error.ArgumentNull("methodInfo"); } + this._memberInfo = methodInfo; } @@ -95,7 +96,10 @@ public Type PropertyType get { if (PropertyInfo != null) + { return PropertyInfo.PropertyType; + } + return MethodInfo.ReturnType; } } @@ -108,7 +112,10 @@ public Type DeclaringType get { if (PropertyInfo != null) + { return PropertyInfo.DeclaringType; + } + return MethodInfo.GetParameters()[0].ParameterType; } } @@ -121,7 +128,10 @@ public Type ReflectedType get { if (PropertyInfo != null) + { return TypeHelper.GetReflectedType(PropertyInfo); + } + return MethodInfo.GetParameters()[0].ParameterType; } } @@ -161,7 +171,7 @@ public object[] GetCustomAttributes(Type type, bool inherit) /// The type of attribute to search for. /// A collection of the custom attributes that are applied to element and that match T, or an empty collection if no such attributes exist. public IEnumerable GetCustomAttributes() - where T : Attribute + where T: Attribute { return GetCustomAttributes(false); } @@ -173,7 +183,7 @@ public IEnumerable GetCustomAttributes() /// true to inspect the ancestors of element; otherwise, false. /// A collection of the custom attributes that are applied to element and that match T, or an empty collection if no such attributes exist. public IEnumerable GetCustomAttributes(bool inherit) - where T:Attribute + where T: Attribute { return MemberInfo.GetCustomAttributes(inherit); } @@ -184,7 +194,7 @@ public IEnumerable GetCustomAttributes(bool inherit) /// The type of attribute to search for. /// A custom attribute that matches T, or null if no such attribute is found. public T GetCustomAttribute() - where T : Attribute + where T: Attribute { return GetCustomAttribute(false); } @@ -196,7 +206,7 @@ public T GetCustomAttribute() /// true to inspect the ancestors of element; otherwise, false. /// A custom attribute that matches T, or null if no such attribute is found. public T GetCustomAttribute(bool inherit) - where T : Attribute + where T: Attribute { return MemberInfo.GetCustomAttribute(inherit); } @@ -219,12 +229,18 @@ public override bool Equals(object obj) { MemberDescriptor propDescr = obj as MemberDescriptor; if (propDescr == null) + { return false; + } if (PropertyInfo != null) + { return PropertyInfo.Equals(propDescr.PropertyInfo); + } else + { return MethodInfo.Equals(propDescr.MethodInfo); + } } } } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationSourceConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationSourceConfiguration.cs index 1efe10a45f..ae3e4fe088 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationSourceConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationSourceConfiguration.cs @@ -557,12 +557,12 @@ private static Type VerifyBindingSegment(Type current, MemberInfo info) Type propertyType = null; PropertyInfo propertyInfo = info as PropertyInfo; MethodInfo methodInfo = info as MethodInfo; - if(propertyInfo!=null) + if (propertyInfo != null) { declaringType = info.DeclaringType; propertyType = propertyInfo.PropertyType; } - else if(methodInfo!=null && methodInfo.GetCustomAttribute()!=null) + else if (methodInfo != null && methodInfo.GetCustomAttribute() != null) { declaringType = methodInfo.GetParameters().First().ParameterType; propertyType = methodInfo.ReturnType; From 93d9f28f517d396930a81b874bccaaad00128e76 Mon Sep 17 00:00:00 2001 From: Gennady Pundikov Date: Thu, 18 Jul 2019 08:30:30 +0300 Subject: [PATCH 11/11] fixed syling issues --- .../Builder/MemberDescriptor.cs | 8 ++++---- .../Builder/NavigationPropertyConfiguration.cs | 5 ++++- .../Query/Expressions/AggregationBinder.cs | 6 ++++++ .../Query/Expressions/SelectExpandBinder.cs | 4 ++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs index f6cadb9e80..f86e3d8c5d 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/MemberDescriptor.cs @@ -171,7 +171,7 @@ public object[] GetCustomAttributes(Type type, bool inherit) /// The type of attribute to search for. /// A collection of the custom attributes that are applied to element and that match T, or an empty collection if no such attributes exist. public IEnumerable GetCustomAttributes() - where T: Attribute + where T : Attribute { return GetCustomAttributes(false); } @@ -183,7 +183,7 @@ public IEnumerable GetCustomAttributes() /// true to inspect the ancestors of element; otherwise, false. /// A collection of the custom attributes that are applied to element and that match T, or an empty collection if no such attributes exist. public IEnumerable GetCustomAttributes(bool inherit) - where T: Attribute + where T : Attribute { return MemberInfo.GetCustomAttributes(inherit); } @@ -194,7 +194,7 @@ public IEnumerable GetCustomAttributes(bool inherit) /// The type of attribute to search for. /// A custom attribute that matches T, or null if no such attribute is found. public T GetCustomAttribute() - where T: Attribute + where T : Attribute { return GetCustomAttribute(false); } @@ -206,7 +206,7 @@ public T GetCustomAttribute() /// true to inspect the ancestors of element; otherwise, false. /// A custom attribute that matches T, or null if no such attribute is found. public T GetCustomAttribute(bool inherit) - where T: Attribute + where T : Attribute { return MemberInfo.GetCustomAttribute(inherit); } diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs index 81e52cb4b0..d3941dc513 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/NavigationPropertyConfiguration.cs @@ -29,7 +29,7 @@ public class NavigationPropertyConfiguration : PropertyConfiguration /// The . /// The declaring structural type. public NavigationPropertyConfiguration(PropertyInfo property, EdmMultiplicity multiplicity, StructuralTypeConfiguration declaringType) - :this(CreatePropertyDescriptor(property), multiplicity, declaringType) + : this(CreatePropertyDescriptor(property), multiplicity, declaringType) { } @@ -68,7 +68,10 @@ public NavigationPropertyConfiguration(MemberDescriptor memberDescriptor, EdmMul private static MemberDescriptor CreatePropertyDescriptor(PropertyInfo property) { if (property == null) + { return null; + } + return new MemberDescriptor(property); } diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs index 0bec046914..4491326664 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/AggregationBinder.cs @@ -580,12 +580,18 @@ private static Expression CreatePropertyAccessExpression(Expression source, stri if (memberDescriptor != null) { if (memberDescriptor.MethodInfo != null) + { return Expression.Call(null, memberDescriptor.MethodInfo, source); + } else + { return Expression.Property(source, memberDescriptor.PropertyInfo); + } } else + { return Expression.Property(source, propertyName); + } } private Expression CreateOpenPropertyAccessExpression(SingleValueOpenPropertyAccessNode openNode) diff --git a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs index 2934691d4a..62403051d1 100644 --- a/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs +++ b/src/Microsoft.AspNet.OData.Shared/Query/Expressions/SelectExpandBinder.cs @@ -191,9 +191,13 @@ internal Expression CreatePropertyValueExpressionWithFilter(IEdmEntityType eleme { propertyName = memberDescriptor.Name; if (memberDescriptor.PropertyInfo != null) + { propertyValue = Expression.Property(source, memberDescriptor.PropertyInfo); + } else + { propertyValue = Expression.Call(memberDescriptor.MethodInfo, source); + } } else {