Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/aspnetwebstack.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorraghuramn <ranadimi@microsoft.com>2012-09-27 22:24:26 +0400
committerraghuramn <ranadimi@microsoft.com>2012-10-10 02:52:43 +0400
commit3f17af2d860cc23f675d8d7653510aa5495c73b8 (patch)
tree299e17d755bf8826c3ba853adf6364f0d0c9e297
parent5b7dd71fb1190fa2772ada7ba5d374b438947e4e (diff)
Adding support for having navigation properties on derived types.
1) Enityset's should be annotated with navigation links for navigation properties present in derived types as well. 2) Entityset's should be bound to other entity set's for navigation properties present in derived types as well.
-rw-r--r--src/System.Web.Http.OData/OData/Builder/CollectionPropertyConfiguration.cs4
-rw-r--r--src/System.Web.Http.OData/OData/Builder/ComplexPropertyConfiguration.cs4
-rw-r--r--src/System.Web.Http.OData/OData/Builder/Conventions/AssociationSetDiscoveryConvention.cs75
-rw-r--r--src/System.Web.Http.OData/OData/Builder/Conventions/NavigationLinksGenerationConvention.cs45
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EdmModelHelperMethods.cs25
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.cs14
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EntitySetConfiguration.cs36
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs124
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs2
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs2
-rw-r--r--src/System.Web.Http.OData/OData/Builder/IEntitySetConfiguration.cs2
-rw-r--r--src/System.Web.Http.OData/OData/Builder/NavigationPropertyConfiguration.cs12
-rw-r--r--src/System.Web.Http.OData/OData/Builder/ODataConventionModelBuilder.cs7
-rw-r--r--src/System.Web.Http.OData/OData/Builder/PrimitivePropertyConfiguration.cs4
-rw-r--r--src/System.Web.Http.OData/OData/Builder/PropertyConfiguration.cs10
-rw-r--r--src/System.Web.Http.OData/OData/Builder/StructuralPropertyConfiguration.cs4
-rw-r--r--src/System.Web.Http.OData/OData/Builder/StructuralTypeConfiguration.cs12
-rw-r--r--src/System.Web.Http.OData/Properties/SRResources.Designer.cs18
-rw-r--r--src/System.Web.Http.OData/Properties/SRResources.resx6
-rw-r--r--src/System.Web.Http.OData/System.Web.Http.OData.csproj1
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/CollectionPropertyConfigurationTest.cs11
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/AssociationSetDiscoveryConventionTest.cs137
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs8
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs3
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs4
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs8
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs2
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/RequiredAttributeEdmPropertyConventionTests.cs2
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs13
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/NavigationLinksGenerationConventionTest.cs54
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs96
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs54
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/EntitySetTest.cs182
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/TestModels/InheritanceModels.cs4
-rw-r--r--test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj2
35 files changed, 898 insertions, 89 deletions
diff --git a/src/System.Web.Http.OData/OData/Builder/CollectionPropertyConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/CollectionPropertyConfiguration.cs
index fea2ead9..dab38184 100644
--- a/src/System.Web.Http.OData/OData/Builder/CollectionPropertyConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/CollectionPropertyConfiguration.cs
@@ -16,8 +16,8 @@ namespace System.Web.Http.OData.Builder
/// <summary>
/// Constructs a CollectionPropertyConfiguration using the <paramref name="property">property</paramref> provided.
/// </summary>
- public CollectionPropertyConfiguration(PropertyInfo property)
- : base(property)
+ public CollectionPropertyConfiguration(PropertyInfo property, IStructuralTypeConfiguration declaringType)
+ : base(property, declaringType)
{
if (!property.PropertyType.IsCollection(out _elementType))
{
diff --git a/src/System.Web.Http.OData/OData/Builder/ComplexPropertyConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/ComplexPropertyConfiguration.cs
index 50b8b658..533c8036 100644
--- a/src/System.Web.Http.OData/OData/Builder/ComplexPropertyConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/ComplexPropertyConfiguration.cs
@@ -6,8 +6,8 @@ namespace System.Web.Http.OData.Builder
{
public class ComplexPropertyConfiguration : StructuralPropertyConfiguration
{
- public ComplexPropertyConfiguration(PropertyInfo property)
- : base(property)
+ public ComplexPropertyConfiguration(PropertyInfo property, IStructuralTypeConfiguration declaringType)
+ : base(property, declaringType)
{
}
diff --git a/src/System.Web.Http.OData/OData/Builder/Conventions/AssociationSetDiscoveryConvention.cs b/src/System.Web.Http.OData/OData/Builder/Conventions/AssociationSetDiscoveryConvention.cs
new file mode 100644
index 00000000..7eb33553
--- /dev/null
+++ b/src/System.Web.Http.OData/OData/Builder/Conventions/AssociationSetDiscoveryConvention.cs
@@ -0,0 +1,75 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Web.Http.OData.Properties;
+
+namespace System.Web.Http.OData.Builder.Conventions
+{
+ /// <summary>
+ /// <see cref="IEntitySetConvention"/> to configure the EDM association sets for the given entity set.
+ /// <remarks>This convention adds an association set for each EDM navigation property defined in this type, its base types and all its derived types.
+ /// The target entity set chosen is the default entity set for the navigation property's target entity type.
+ /// The default entity set for an entity type is the entity set that contains entries of that entity type. If more than one entity sets match, the default entity set is none.
+ /// If no entity sets match the default entity set is the default entity set of the base type.</remarks>
+ /// </summary>
+ public class AssociationSetDiscoveryConvention : IEntitySetConvention
+ {
+ public void Apply(IEntitySetConfiguration configuration, ODataModelBuilder model)
+ {
+ foreach (IEntityTypeConfiguration entity in model.ThisAndBaseAndDerivedTypes(configuration.EntityType))
+ {
+ foreach (NavigationPropertyConfiguration navigationProperty in entity.NavigationProperties)
+ {
+ IEntitySetConfiguration targetEntitySet = GetTargetEntitySet(navigationProperty, model);
+ if (targetEntitySet != null)
+ {
+ configuration.AddBinding(navigationProperty, targetEntitySet);
+ }
+ }
+ }
+ }
+
+ // Get the default target entity set for this navigation property.
+ internal static IEntitySetConfiguration GetTargetEntitySet(NavigationPropertyConfiguration navigationProperty, ODataModelBuilder model)
+ {
+ IEntityTypeConfiguration targetEntityType =
+ model
+ .StructuralTypes
+ .OfType<IEntityTypeConfiguration>()
+ .Where(e => e.ClrType == navigationProperty.RelatedClrType).SingleOrDefault();
+
+ if (targetEntityType == null)
+ {
+ throw Error.InvalidOperation(SRResources.TargetEntityTypeMissing, navigationProperty.Name, navigationProperty.PropertyInfo.ReflectedType.FullName);
+ }
+
+ return GetDefaultEntitySet(targetEntityType, model);
+ }
+
+ private static IEntitySetConfiguration GetDefaultEntitySet(IEntityTypeConfiguration targetEntityType, ODataModelBuilder model)
+ {
+ if (targetEntityType == null)
+ {
+ return null;
+ }
+
+ IEnumerable<IEntitySetConfiguration> matchingEntitySets = model.EntitySets.Where(e => e.EntityType == targetEntityType);
+
+ if (matchingEntitySets.Count() > 1)
+ {
+ // no default entity set if more than one entity set match.
+ return null;
+ }
+ else if (matchingEntitySets.Count() == 1)
+ {
+ return matchingEntitySets.Single();
+ }
+ else
+ {
+ // default entity set is the same as the default entity set for the base type.
+ return GetDefaultEntitySet(targetEntityType.BaseType, model);
+ }
+ }
+ }
+}
diff --git a/src/System.Web.Http.OData/OData/Builder/Conventions/NavigationLinksGenerationConvention.cs b/src/System.Web.Http.OData/OData/Builder/Conventions/NavigationLinksGenerationConvention.cs
index c54e3d7c..30045351 100644
--- a/src/System.Web.Http.OData/OData/Builder/Conventions/NavigationLinksGenerationConvention.cs
+++ b/src/System.Web.Http.OData/OData/Builder/Conventions/NavigationLinksGenerationConvention.cs
@@ -15,30 +15,33 @@ namespace System.Web.Http.OData.Builder.Conventions
throw Error.ArgumentNull("configuration");
}
- foreach (NavigationPropertyConfiguration property in configuration.EntityType.NavigationProperties)
+ foreach (IEntityTypeConfiguration entity in model.ThisAndBaseAndDerivedTypes(configuration.EntityType))
{
- if (configuration.GetNavigationPropertyLink(property.Name) == null)
+ foreach (NavigationPropertyConfiguration property in entity.NavigationProperties)
{
- configuration.HasNavigationPropertyLink(
- property,
- (entityContext, navigationProperty) =>
- {
- string route = PropertyNavigationRouteName ?? ODataRouteNames.PropertyNavigation;
- string link = entityContext.UrlHelper.Link(
- route,
- new
- {
- Controller = configuration.Name,
- ParentId = ConventionsHelpers.GetEntityKeyValue(entityContext, configuration.EntityType),
- NavigationProperty = navigationProperty.Name
- });
-
- if (link == null)
+ if (configuration.GetNavigationPropertyLink(property) == null)
+ {
+ configuration.HasNavigationPropertyLink(
+ property,
+ (entityContext, navigationProperty) =>
{
- throw Error.InvalidOperation(SRResources.NavigationPropertyRouteMissingOrIncorrect, navigationProperty.Name, ODataRouteNames.PropertyNavigation);
- }
- return new Uri(link);
- });
+ string route = PropertyNavigationRouteName ?? ODataRouteNames.PropertyNavigation;
+ string link = entityContext.UrlHelper.Link(
+ route,
+ new
+ {
+ Controller = configuration.Name,
+ ParentId = ConventionsHelpers.GetEntityKeyValue(entityContext, entity),
+ NavigationProperty = navigationProperty.Name
+ });
+
+ if (link == null)
+ {
+ throw Error.InvalidOperation(SRResources.NavigationPropertyRouteMissingOrIncorrect, navigationProperty.Name, ODataRouteNames.PropertyNavigation);
+ }
+ return new Uri(link);
+ });
+ }
}
}
}
diff --git a/src/System.Web.Http.OData/OData/Builder/EdmModelHelperMethods.cs b/src/System.Web.Http.OData/OData/Builder/EdmModelHelperMethods.cs
index 6d4b563c..ab49505b 100644
--- a/src/System.Web.Http.OData/OData/Builder/EdmModelHelperMethods.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EdmModelHelperMethods.cs
@@ -27,7 +27,7 @@ namespace System.Web.Http.OData.Builder
// add types and sets, building an index on the way.
Dictionary<string, IEdmStructuredType> edmTypeMap = model.AddTypes(builder.StructuralTypes);
- Dictionary<string, EdmEntitySet> edmEntitySetMap = model.AddEntitySets(builder.EntitySets, container, edmTypeMap);
+ Dictionary<string, EdmEntitySet> edmEntitySetMap = model.AddEntitySets(builder, container, edmTypeMap);
// add procedures
model.AddProcedures(builder.Procedures, container, edmTypeMap, edmEntitySetMap);
@@ -52,8 +52,10 @@ namespace System.Web.Http.OData.Builder
}
}
- private static Dictionary<string, EdmEntitySet> AddEntitySets(this EdmModel model, IEnumerable<IEntitySetConfiguration> configurations, EdmEntityContainer container, Dictionary<string, IEdmStructuredType> edmTypeMap)
+ private static Dictionary<string, EdmEntitySet> AddEntitySets(this EdmModel model, ODataModelBuilder builder, EdmEntityContainer container, Dictionary<string, IEdmStructuredType> edmTypeMap)
{
+ IEnumerable<IEntitySetConfiguration> configurations = builder.EntitySets;
+
// build the entitysets and their annotations
IEnumerable<Tuple<EdmEntitySet, IEntitySetConfiguration>> entitySets = AddEntitySets(configurations, container, edmTypeMap);
var entitySetAndAnnotations = entitySets.Select(e => new
@@ -78,20 +80,29 @@ namespace System.Web.Http.OData.Builder
model.SetAnnotationValue<EntitySetUrlAnnotation>(entitySet, iter.Annotations.Url);
model.SetAnnotationValue<IEntitySetLinkBuilder>(entitySet, iter.Annotations.LinkBuilder);
- foreach (NavigationPropertyConfiguration navigation in configuration.EntityType.NavigationProperties)
+ AddNavigationBindings(iter.Configuration, iter.EntitySet, iter.Annotations.LinkBuilder, builder, edmTypeMap, edmEntitySetMap);
+ }
+ return edmEntitySetMap;
+ }
+
+ private static void AddNavigationBindings(IEntitySetConfiguration configuration, EdmEntitySet entitySet, EntitySetLinkBuilderAnnotation linkBuilder, ODataModelBuilder builder,
+ Dictionary<string, IEdmStructuredType> edmTypeMap, Dictionary<string, EdmEntitySet> edmEntitySetMap)
+ {
+ foreach (IEntityTypeConfiguration entity in builder.ThisAndBaseAndDerivedTypes(configuration.EntityType))
+ {
+ foreach (NavigationPropertyConfiguration navigation in entity.NavigationProperties)
{
NavigationPropertyBinding binding = configuration.FindBinding(navigation);
if (binding != null)
{
- EdmEntityType edmEntityType = edmTypeMap[configuration.EntityType.FullName] as EdmEntityType;
+ EdmEntityType edmEntityType = edmTypeMap[entity.FullName] as EdmEntityType;
IEdmNavigationProperty edmNavigationProperty = edmEntityType.NavigationProperties().Single(np => np.Name == navigation.Name);
entitySet.AddNavigationTarget(edmNavigationProperty, edmEntitySetMap[binding.EntitySet.Name]);
- iter.Annotations.LinkBuilder.AddNavigationPropertyLinkBuilder(edmNavigationProperty, configuration.GetNavigationPropertyLink(edmNavigationProperty.Name));
+ linkBuilder.AddNavigationPropertyLinkBuilder(edmNavigationProperty, configuration.GetNavigationPropertyLink(navigation));
}
}
}
- return edmEntitySetMap;
}
private static void AddProcedures(this IEdmModel model, IEnumerable<ProcedureConfiguration> configurations, EdmEntityContainer container, Dictionary<string, IEdmStructuredType> edmTypeMap, Dictionary<string, EdmEntitySet> edmEntitySetMap)
@@ -143,8 +154,10 @@ namespace System.Web.Http.OData.Builder
private static Dictionary<string, IEdmStructuredType> AddTypes(this EdmModel model, IEnumerable<IStructuralTypeConfiguration> types)
{
IStructuralTypeConfiguration[] configTypes = types.ToArray();
+
// build types
IEdmStructuredType[] edmTypes = EdmTypeBuilder.GetTypes(configTypes).ToArray();
+
// index types
Dictionary<string, IEdmStructuredType> edmTypeMap = edmTypes.ToDictionary(t => (t as IEdmSchemaType).FullName());
diff --git a/src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.cs b/src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.cs
index ec66766f..da2c2e95 100644
--- a/src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.cs
@@ -84,5 +84,19 @@ namespace System.Web.Http.OData.Builder
}
}
}
+
+ public static bool IsAssignableFrom(this IEntityTypeConfiguration baseEntity, IEntityTypeConfiguration entity)
+ {
+ while (entity != null)
+ {
+ if (baseEntity == entity)
+ {
+ return true;
+ }
+ entity = entity.BaseType;
+ }
+
+ return false;
+ }
}
}
diff --git a/src/System.Web.Http.OData/OData/Builder/EntitySetConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/EntitySetConfiguration.cs
index f9efa74d..375db456 100644
--- a/src/System.Web.Http.OData/OData/Builder/EntitySetConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EntitySetConfiguration.cs
@@ -19,7 +19,7 @@ namespace System.Web.Http.OData.Builder
private Func<EntityInstanceContext, Uri> _editLinkFactory;
private Func<EntityInstanceContext, Uri> _readLinkFactory;
private Func<EntityInstanceContext, string> _idLinkFactory;
- private readonly IDictionary<string, Func<EntityInstanceContext, IEdmNavigationProperty, Uri>> _navigationPropertyLinkBuilders;
+ private readonly IDictionary<NavigationPropertyConfiguration, Func<EntityInstanceContext, IEdmNavigationProperty, Uri>> _navigationPropertyLinkBuilders;
internal EntitySetConfiguration(ODataModelBuilder modelBuilder, Type entityType, string name)
: this(modelBuilder, new EntityTypeConfiguration(modelBuilder, entityType), name)
@@ -36,7 +36,7 @@ namespace System.Web.Http.OData.Builder
_editLinkFactory = null;
_readLinkFactory = null;
- _navigationPropertyLinkBuilders = new Dictionary<string, Func<EntityInstanceContext, IEdmNavigationProperty, Uri>>();
+ _navigationPropertyLinkBuilders = new Dictionary<NavigationPropertyConfiguration, Func<EntityInstanceContext, IEdmNavigationProperty, Uri>>();
}
public IEnumerable<NavigationPropertyBinding> Bindings
@@ -97,7 +97,13 @@ namespace System.Web.Http.OData.Builder
throw Error.ArgumentNull("navigationLinkFactory");
}
- _navigationPropertyLinkBuilders.Add(navigationProperty.Name, navigationLinkFactory);
+ IEntityTypeConfiguration declaringEntityType = navigationProperty.DeclaringEntityType;
+ if (!(declaringEntityType.IsAssignableFrom(EntityType) || EntityType.IsAssignableFrom(declaringEntityType)))
+ {
+ throw Error.InvalidOperation(SRResources.NavigationPropertyNotInHierarchy, declaringEntityType.FullName, EntityType.FullName, Name);
+ }
+
+ _navigationPropertyLinkBuilders.Add(navigationProperty, navigationLinkFactory);
return this;
}
@@ -123,6 +129,22 @@ namespace System.Web.Http.OData.Builder
public NavigationPropertyBinding AddBinding(NavigationPropertyConfiguration navigationConfiguration, IEntitySetConfiguration targetEntitySet)
{
+ if (navigationConfiguration == null)
+ {
+ throw Error.ArgumentNull("navigationConfiguration");
+ }
+
+ if (targetEntitySet == null)
+ {
+ throw Error.ArgumentNull("targetEntitySet");
+ }
+
+ IEntityTypeConfiguration declaringEntityType = navigationConfiguration.DeclaringEntityType;
+ if (!(declaringEntityType.IsAssignableFrom(EntityType) || EntityType.IsAssignableFrom(declaringEntityType)))
+ {
+ throw Error.InvalidOperation(SRResources.NavigationPropertyNotInHierarchy, declaringEntityType.FullName, EntityType.FullName, Name);
+ }
+
NavigationPropertyBinding navigationPropertyBinding = null;
if (_entitySetBindings.ContainsKey(navigationConfiguration))
{
@@ -211,15 +233,15 @@ namespace System.Web.Http.OData.Builder
return _idLinkFactory;
}
- public Func<EntityInstanceContext, IEdmNavigationProperty, Uri> GetNavigationPropertyLink(string navigationPropertyName)
+ public Func<EntityInstanceContext, IEdmNavigationProperty, Uri> GetNavigationPropertyLink(NavigationPropertyConfiguration navigationProperty)
{
- if (String.IsNullOrEmpty(navigationPropertyName))
+ if (navigationProperty == null)
{
- throw Error.ArgumentNullOrEmpty("navigationProperty");
+ throw Error.ArgumentNull("navigationProperty");
}
Func<EntityInstanceContext, IEdmNavigationProperty, Uri> navigationPropertyLinkBuilder;
- _navigationPropertyLinkBuilders.TryGetValue(navigationPropertyName, out navigationPropertyLinkBuilder);
+ _navigationPropertyLinkBuilders.TryGetValue(navigationProperty, out navigationPropertyLinkBuilder);
return navigationPropertyLinkBuilder;
}
diff --git a/src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs b/src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs
index 4eb8745d..8826468c 100644
--- a/src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs
@@ -44,8 +44,25 @@ namespace System.Web.Http.OData.Builder
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
+ public NavigationPropertyBinding HasManyBinding<TTargetType, TDerivedEntityType>(
+ Expression<Func<TDerivedEntityType, IEnumerable<TTargetType>>> navigationExpression, string entitySetName)
+ where TTargetType : class
+ where TDerivedEntityType : class, TEntityType
+ {
+ if (navigationExpression == null)
+ {
+ throw Error.ArgumentNull("navigationExpression");
+ }
+
+ EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
+ _modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
+
+ return _configuration.AddBinding(derivedEntityType.HasMany(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBinding HasManyBinding<TTargetType>(
- Expression<Func<TEntityType, ICollection<TTargetType>>> navigationExpression, string entitySetName)
+ Expression<Func<TEntityType, IEnumerable<TTargetType>>> navigationExpression, string entitySetName)
where TTargetType : class
{
if (navigationExpression == null)
@@ -58,7 +75,7 @@ namespace System.Web.Http.OData.Builder
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBinding HasManyBinding<TTargetType>(
- Expression<Func<TEntityType, ICollection<TTargetType>>> navigationExpression,
+ Expression<Func<TEntityType, IEnumerable<TTargetType>>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet) where TTargetType : class
{
if (navigationExpression == null)
@@ -75,6 +92,29 @@ namespace System.Web.Http.OData.Builder
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
+ public NavigationPropertyBinding HasManyBinding<TTargetType, TDerivedEntityType>(
+ Expression<Func<TDerivedEntityType, IEnumerable<TTargetType>>> navigationExpression,
+ EntitySetConfiguration<TTargetType> targetSet)
+ where TTargetType : class
+ where TDerivedEntityType : class, TEntityType
+ {
+ if (navigationExpression == null)
+ {
+ throw Error.ArgumentNull("navigationExpression");
+ }
+
+ if (targetSet == null)
+ {
+ throw Error.ArgumentNull("targetSet");
+ }
+
+ EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
+ _modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
+
+ return _configuration.AddBinding(derivedEntityType.HasMany(navigationExpression), targetSet._configuration);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBinding HasRequiredBinding<TTargetType>(
Expression<Func<TEntityType, TTargetType>> navigationExpression, string entitySetName)
where TTargetType : class
@@ -88,6 +128,23 @@ namespace System.Web.Http.OData.Builder
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
+ public NavigationPropertyBinding HasRequiredBinding<TTargetType, TDerivedEntityType>(
+ Expression<Func<TDerivedEntityType, TTargetType>> navigationExpression, string entitySetName)
+ where TTargetType : class
+ where TDerivedEntityType : class, TEntityType
+ {
+ if (navigationExpression == null)
+ {
+ throw Error.ArgumentNull("navigationExpression");
+ }
+
+ EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
+ _modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
+
+ return _configuration.AddBinding(derivedEntityType.HasRequired(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBinding HasRequiredBinding<TTargetType>(
Expression<Func<TEntityType, TTargetType>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet) where TTargetType : class
@@ -106,6 +163,29 @@ namespace System.Web.Http.OData.Builder
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
+ public NavigationPropertyBinding HasRequiredBinding<TTargetType, TDerivedEntityType>(
+ Expression<Func<TDerivedEntityType, TTargetType>> navigationExpression,
+ EntitySetConfiguration<TTargetType> targetSet)
+ where TTargetType : class
+ where TDerivedEntityType : class, TEntityType
+ {
+ if (navigationExpression == null)
+ {
+ throw Error.ArgumentNull("navigationExpression");
+ }
+
+ if (targetSet == null)
+ {
+ throw Error.ArgumentNull("targetSet");
+ }
+
+ EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
+ _modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
+
+ return _configuration.AddBinding(derivedEntityType.HasRequired(navigationExpression), targetSet._configuration);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBinding HasOptionalBinding<TTargetType>(
Expression<Func<TEntityType, TTargetType>> navigationExpression, string entitySetName)
where TTargetType : class
@@ -119,6 +199,23 @@ namespace System.Web.Http.OData.Builder
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
+ public NavigationPropertyBinding HasOptionalBinding<TTargetType, TDerivedEntityType>(
+ Expression<Func<TDerivedEntityType, TTargetType>> navigationExpression, string entitySetName)
+ where TTargetType : class
+ where TDerivedEntityType : class, TEntityType
+ {
+ if (navigationExpression == null)
+ {
+ throw Error.ArgumentNull("navigationExpression");
+ }
+
+ EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
+ _modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
+
+ return _configuration.AddBinding(derivedEntityType.HasOptional(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBinding HasOptionalBinding<TTargetType>(
Expression<Func<TEntityType, TTargetType>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet) where TTargetType : class
@@ -136,6 +233,29 @@ namespace System.Web.Http.OData.Builder
return _configuration.AddBinding(EntityType.HasOptional(navigationExpression), targetSet._configuration);
}
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
+ public NavigationPropertyBinding HasOptionalBinding<TTargetType, TDerivedEntityType>(
+ Expression<Func<TDerivedEntityType, TTargetType>> navigationExpression,
+ EntitySetConfiguration<TTargetType> targetSet)
+ where TTargetType : class
+ where TDerivedEntityType : class, TEntityType
+ {
+ if (navigationExpression == null)
+ {
+ throw Error.ArgumentNull("navigationExpression");
+ }
+
+ if (targetSet == null)
+ {
+ throw Error.ArgumentNull("targetSet");
+ }
+
+ EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
+ _modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
+
+ return _configuration.AddBinding(derivedEntityType.HasOptional(navigationExpression), targetSet._configuration);
+ }
+
/// <summary>
/// Adds a self link to the feed.
/// </summary>
diff --git a/src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs
index 37019225..b48fd507 100644
--- a/src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs
@@ -250,7 +250,7 @@ namespace System.Web.Http.OData.Builder
}
else
{
- navigationPropertyConfig = new NavigationPropertyConfiguration(navigationProperty, multiplicity);
+ navigationPropertyConfig = new NavigationPropertyConfiguration(navigationProperty, multiplicity, this);
ExplicitProperties[navigationProperty] = navigationPropertyConfig;
// make sure the related type is configured
ModelBuilder.AddEntity(navigationPropertyConfig.RelatedClrType);
diff --git a/src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs b/src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs
index 4512a0b4..d4e26c6f 100644
--- a/src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs
@@ -127,7 +127,7 @@ namespace System.Web.Http.OData.Builder
/// <returns>A configuration object that can be used to further configure the relationship.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Explicit Expression generic type is more clear")]
- public NavigationPropertyConfiguration HasMany<TTargetEntity>(Expression<Func<TEntityType, ICollection<TTargetEntity>>> navigationPropertyExpression) where TTargetEntity : class
+ public NavigationPropertyConfiguration HasMany<TTargetEntity>(Expression<Func<TEntityType, IEnumerable<TTargetEntity>>> navigationPropertyExpression) where TTargetEntity : class
{
return GetOrCreateNavigationProperty(navigationPropertyExpression, EdmMultiplicity.Many);
}
diff --git a/src/System.Web.Http.OData/OData/Builder/IEntitySetConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/IEntitySetConfiguration.cs
index fe42a450..198e9f2b 100644
--- a/src/System.Web.Http.OData/OData/Builder/IEntitySetConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/IEntitySetConfiguration.cs
@@ -43,7 +43,7 @@ namespace System.Web.Http.OData.Builder
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Consistent with EF Has/Get pattern")]
Func<EntityInstanceContext, string> GetIdLink();
- Func<EntityInstanceContext, IEdmNavigationProperty, Uri> GetNavigationPropertyLink(string navigationPropertyName);
+ Func<EntityInstanceContext, IEdmNavigationProperty, Uri> GetNavigationPropertyLink(NavigationPropertyConfiguration navigationProperty);
/// <summary>
/// Adds a self link to the feed.
diff --git a/src/System.Web.Http.OData/OData/Builder/NavigationPropertyConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/NavigationPropertyConfiguration.cs
index 5015cd1a..6835a9db 100644
--- a/src/System.Web.Http.OData/OData/Builder/NavigationPropertyConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/NavigationPropertyConfiguration.cs
@@ -11,8 +11,8 @@ namespace System.Web.Http.OData.Builder
{
private readonly Type _relatedType = null;
- public NavigationPropertyConfiguration(PropertyInfo property, EdmMultiplicity multiplicity)
- : base(property)
+ public NavigationPropertyConfiguration(PropertyInfo property, EdmMultiplicity multiplicity, IEntityTypeConfiguration declaringType)
+ : base(property, declaringType)
{
if (property == null)
{
@@ -34,6 +34,14 @@ namespace System.Web.Http.OData.Builder
}
}
+ public IEntityTypeConfiguration DeclaringEntityType
+ {
+ get
+ {
+ return DeclaringType as IEntityTypeConfiguration;
+ }
+ }
+
public EdmMultiplicity Multiplicity { get; private set; }
public override Type RelatedClrType
diff --git a/src/System.Web.Http.OData/OData/Builder/ODataConventionModelBuilder.cs b/src/System.Web.Http.OData/OData/Builder/ODataConventionModelBuilder.cs
index 89b31a3c..900e4d32 100644
--- a/src/System.Web.Http.OData/OData/Builder/ODataConventionModelBuilder.cs
+++ b/src/System.Web.Http.OData/OData/Builder/ODataConventionModelBuilder.cs
@@ -34,12 +34,7 @@ namespace System.Web.Http.OData.Builder
// IEntitySetConvention's
new SelfLinksGenerationConvention(),
new NavigationLinksGenerationConvention(),
-
- // IEdmPropertyConvention's
- new NotMappedAttributeConvention(),
- new RequiredAttributeEdmPropertyConvention(),
- new KeyAttributeEdmPropertyConvention(),
- new IgnoreDataMemberAttributeEdmPropertyConvention(),
+ new AssociationSetDiscoveryConvention(),
// IEdmFunctionImportConventions's
new ActionLinkGenerationConvention(),
diff --git a/src/System.Web.Http.OData/OData/Builder/PrimitivePropertyConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/PrimitivePropertyConfiguration.cs
index 8fd4b6c7..9d8668c1 100644
--- a/src/System.Web.Http.OData/OData/Builder/PrimitivePropertyConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/PrimitivePropertyConfiguration.cs
@@ -10,8 +10,8 @@ namespace System.Web.Http.OData.Builder
/// </summary>
public class PrimitivePropertyConfiguration : StructuralPropertyConfiguration
{
- public PrimitivePropertyConfiguration(PropertyInfo property)
- : base(property)
+ public PrimitivePropertyConfiguration(PropertyInfo property, IStructuralTypeConfiguration declaringType)
+ : base(property, declaringType)
{
}
diff --git a/src/System.Web.Http.OData/OData/Builder/PropertyConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/PropertyConfiguration.cs
index 27411a67..2bfb15b1 100644
--- a/src/System.Web.Http.OData/OData/Builder/PropertyConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/PropertyConfiguration.cs
@@ -9,14 +9,20 @@ namespace System.Web.Http.OData.Builder
/// </summary>
public abstract class PropertyConfiguration
{
- protected PropertyConfiguration(PropertyInfo property)
+ protected PropertyConfiguration(PropertyInfo property, IStructuralTypeConfiguration declaringType)
{
if (property == null)
{
throw Error.ArgumentNull("property");
}
+ if (declaringType == null)
+ {
+ throw Error.ArgumentNull("declaringType");
+ }
+
PropertyInfo = property;
+ DeclaringType = declaringType;
}
/// <summary>
@@ -27,6 +33,8 @@ namespace System.Web.Http.OData.Builder
get { return PropertyInfo.Name; }
}
+ public IStructuralTypeConfiguration DeclaringType { get; private set; }
+
/// <summary>
/// Gets the mapping CLR <see cref="PropertyInfo"/>.
/// </summary>
diff --git a/src/System.Web.Http.OData/OData/Builder/StructuralPropertyConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/StructuralPropertyConfiguration.cs
index 53975876..9c6bce09 100644
--- a/src/System.Web.Http.OData/OData/Builder/StructuralPropertyConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/StructuralPropertyConfiguration.cs
@@ -9,8 +9,8 @@ namespace System.Web.Http.OData.Builder
/// </summary>
public abstract class StructuralPropertyConfiguration : PropertyConfiguration
{
- protected StructuralPropertyConfiguration(PropertyInfo property)
- : base(property)
+ protected StructuralPropertyConfiguration(PropertyInfo property, IStructuralTypeConfiguration declaringType)
+ : base(property, declaringType)
{
OptionalProperty = IsNullable(property.PropertyType);
}
diff --git a/src/System.Web.Http.OData/OData/Builder/StructuralTypeConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/StructuralTypeConfiguration.cs
index 76884571..7691b074 100644
--- a/src/System.Web.Http.OData/OData/Builder/StructuralTypeConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/StructuralTypeConfiguration.cs
@@ -88,7 +88,7 @@ namespace System.Web.Http.OData.Builder
}
else
{
- propertyConfiguration = new PrimitivePropertyConfiguration(propertyInfo);
+ propertyConfiguration = new PrimitivePropertyConfiguration(propertyInfo, this);
ExplicitProperties[propertyInfo] = propertyConfiguration;
}
@@ -130,7 +130,7 @@ namespace System.Web.Http.OData.Builder
}
else
{
- propertyConfiguration = new ComplexPropertyConfiguration(propertyInfo);
+ propertyConfiguration = new ComplexPropertyConfiguration(propertyInfo, this);
ExplicitProperties[propertyInfo] = propertyConfiguration;
// Make sure the complex type is in the model.
@@ -150,7 +150,7 @@ namespace System.Web.Http.OData.Builder
if (!propertyInfo.DeclaringType.IsAssignableFrom(ClrType))
{
throw Error.Argument("propertyInfo", SRResources.PropertyDoesNotBelongToType);
- }
+ }
// Remove from the ignored properties
if (IgnoredProperties.Contains(propertyInfo))
@@ -169,13 +169,13 @@ namespace System.Web.Http.OData.Builder
}
else
{
- propertyConfiguration = new CollectionPropertyConfiguration(propertyInfo);
+ propertyConfiguration = new CollectionPropertyConfiguration(propertyInfo, this);
ExplicitProperties[propertyInfo] = propertyConfiguration;
-
+
// If the ElementType is the same as this type this is recursive complex type nesting
if (propertyConfiguration.ElementType == ClrType)
{
- throw Error.Argument("propertyInfo",
+ throw Error.Argument("propertyInfo",
SRResources.RecursiveComplexTypesNotAllowed,
ClrType.Name,
propertyConfiguration.Name);
diff --git a/src/System.Web.Http.OData/Properties/SRResources.Designer.cs b/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
index 9239f8fe..d4e79465 100644
--- a/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
+++ b/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
@@ -655,6 +655,15 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to The declaring entity type &apos;{0}&apos; of the given navigation property is not a part of the entity type &apos;{1}&apos; hierarchy of the entity set &apos;{2}&apos;..
+ /// </summary>
+ internal static string NavigationPropertyNotInHierarchy {
+ get {
+ return ResourceManager.GetString("NavigationPropertyNotInHierarchy", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Link generation for the &apos;{0}&apos; NavigationProperty failed. Check that you have the &apos;{1}&apos; route correctly registered..
/// </summary>
internal static string NavigationPropertyRouteMissingOrIncorrect {
@@ -898,6 +907,15 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to Could not find the target entity type for the navigation property &apos;{0}&apos; on entity type &apos;{1}&apos;..
+ /// </summary>
+ internal static string TargetEntityTypeMissing {
+ get {
+ return ResourceManager.GetString("TargetEntityTypeMissing", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The type &apos;{0}&apos; cannot be configured as a ComplexType. It was previously configured as an EntityType..
/// </summary>
internal static string TypeCannotBeComplexWasEntity {
diff --git a/src/System.Web.Http.OData/Properties/SRResources.resx b/src/System.Web.Http.OData/Properties/SRResources.resx
index 280bff86..98d1bfbd 100644
--- a/src/System.Web.Http.OData/Properties/SRResources.resx
+++ b/src/System.Web.Http.OData/Properties/SRResources.resx
@@ -432,4 +432,10 @@
<data name="HasActionLinkRequiresBindToEntity" xml:space="preserve">
<value>To register an action link factory, actions must be bindable to a single entity. Action '{0}' does not meet this requirement.</value>
</data>
+ <data name="NavigationPropertyNotInHierarchy" xml:space="preserve">
+ <value>The declaring entity type '{0}' of the given navigation property is not a part of the entity type '{1}' hierarchy of the entity set '{2}'.</value>
+ </data>
+ <data name="TargetEntityTypeMissing" xml:space="preserve">
+ <value>Could not find the target entity type for the navigation property '{0}' on entity type '{1}'.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/System.Web.Http.OData/System.Web.Http.OData.csproj b/src/System.Web.Http.OData/System.Web.Http.OData.csproj
index 18063b81..127acb20 100644
--- a/src/System.Web.Http.OData/System.Web.Http.OData.csproj
+++ b/src/System.Web.Http.OData/System.Web.Http.OData.csproj
@@ -108,6 +108,7 @@
<Compile Include="OData\Builder\BindableProcedureFinder.cs" />
<Compile Include="OData\Builder\Conventions\ActionLinkGenerationConvention.cs" />
<Compile Include="OData\Builder\Conventions\IProcedureConvention.cs" />
+ <Compile Include="OData\Builder\Conventions\AssociationSetDiscoveryConvention.cs" />
<Compile Include="OData\ODataActionParameters.cs" />
<Compile Include="OData\Builder\ActionConfiguration.cs" />
<Compile Include="OData\Builder\BindingParameterConfiguration.cs" />
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/CollectionPropertyConfigurationTest.cs b/test/System.Web.Http.OData.Test/OData/Builder/CollectionPropertyConfigurationTest.cs
index 56165dfc..6896d4d1 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/CollectionPropertyConfigurationTest.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/CollectionPropertyConfigurationTest.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.TestCommon;
+using Moq;
namespace System.Web.Http.OData.Builder
{
@@ -44,12 +45,14 @@ namespace System.Web.Http.OData.Builder
[PropertyData("GetValidPropertiesAndElementTypes")]
public void HasCorrectKindPropertyInfoAndName(PropertyInfo property, Type elementType)
{
- CollectionPropertyConfiguration configuration = new CollectionPropertyConfiguration(property);
+ Mock<IStructuralTypeConfiguration> structuralType = new Mock<IStructuralTypeConfiguration>();
+ CollectionPropertyConfiguration configuration = new CollectionPropertyConfiguration(property, structuralType.Object);
Assert.Equal(PropertyKind.Collection, configuration.Kind);
Assert.Equal(elementType, configuration.ElementType);
Assert.Equal(elementType, configuration.RelatedClrType);
Assert.Equal(property, configuration.PropertyInfo);
Assert.Equal(property.Name, configuration.Name);
+ Assert.Equal(structuralType.Object, configuration.DeclaringType);
}
[Fact]
@@ -58,7 +61,8 @@ namespace System.Web.Http.OData.Builder
ArgumentException exception = Assert.Throws<ArgumentException>(() =>
{
PropertyInfo nonCollectionProperty = typeof(LotsOfCollectionProperties).GetProperty("NonCollectionProperty");
- CollectionPropertyConfiguration configuration = new CollectionPropertyConfiguration(nonCollectionProperty);
+ Mock<IStructuralTypeConfiguration> structuralType = new Mock<IStructuralTypeConfiguration>();
+ CollectionPropertyConfiguration configuration = new CollectionPropertyConfiguration(nonCollectionProperty, structuralType.Object);
});
}
@@ -66,7 +70,8 @@ namespace System.Web.Http.OData.Builder
[PropertyData("GetValidPropertiesAndElementTypes")]
public void CanCorrectlyDetectCollectionProperties(PropertyInfo property, Type elementType)
{
- CollectionPropertyConfiguration configuration = new CollectionPropertyConfiguration(property);
+ Mock<IStructuralTypeConfiguration> structuralType = new Mock<IStructuralTypeConfiguration>();
+ CollectionPropertyConfiguration configuration = new CollectionPropertyConfiguration(property, structuralType.Object);
Assert.Same(property, configuration.PropertyInfo);
Assert.Same(elementType, configuration.ElementType);
Assert.Same(property.Name, configuration.Name);
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/AssociationSetDiscoveryConventionTest.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/AssociationSetDiscoveryConventionTest.cs
new file mode 100644
index 00000000..94c2bcc2
--- /dev/null
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/AssociationSetDiscoveryConventionTest.cs
@@ -0,0 +1,137 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System.Reflection;
+using System.Web.Http.OData.Builder.TestModels;
+using Microsoft.Data.Edm;
+using Microsoft.TestCommon;
+using Moq;
+
+namespace System.Web.Http.OData.Builder.Conventions
+{
+ public class AssociationSetDiscoveryConventionTest
+ {
+ private AssociationSetDiscoveryConvention _convention = new AssociationSetDiscoveryConvention();
+
+ [Fact]
+ public void AssociationSetDiscoveryConvention_AddsBindingForBaseAndDerivedNavigationProperties()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+
+ IEntityTypeConfiguration vehicle = builder.AddEntity(typeof(Vehicle));
+
+ IEntityTypeConfiguration car = builder.AddEntity(typeof(Car)).DerivesFrom(vehicle);
+ NavigationPropertyConfiguration carNavigationProperty = car.AddNavigationProperty(typeof(Car).GetProperty("Manufacturer"), EdmMultiplicity.ZeroOrOne);
+
+ IEntityTypeConfiguration motorcycle = builder.AddEntity(typeof(Motorcycle)).DerivesFrom(vehicle);
+ NavigationPropertyConfiguration motorcycleNavigationProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.ZeroOrOne);
+
+ IEntityTypeConfiguration manufacturer = builder.AddEntity(typeof(Manufacturer));
+ IEntityTypeConfiguration motorcycleManufacturer = builder.AddEntity(typeof(MotorcycleManufacturer)).DerivesFrom(manufacturer);
+ IEntityTypeConfiguration carManufacturer = builder.AddEntity(typeof(CarManufacturer)).DerivesFrom(manufacturer);
+
+ IEntitySetConfiguration manufacturers = builder.AddEntitySet("manufacturers", manufacturer);
+
+
+ Mock<IEntitySetConfiguration> entitySet = new Mock<IEntitySetConfiguration>(MockBehavior.Strict);
+ entitySet.Setup(v => v.EntityType).Returns(vehicle);
+ entitySet.Setup(v => v.AddBinding(motorcycleNavigationProperty, manufacturers)).Returns<NavigationPropertyConfiguration>(null);
+ entitySet.Setup(v => v.AddBinding(carNavigationProperty, manufacturers)).Returns<NavigationPropertyConfiguration>(null);
+
+ // Act
+ _convention.Apply(entitySet.Object, builder);
+
+ // Assert
+ entitySet.VerifyAll();
+ }
+
+ [Fact]
+ public void GetTargetEntitySet_Throws_IfTargetEntityTypeIsMissing()
+ {
+ // Arrange
+ Mock<PropertyInfo> property = new Mock<PropertyInfo>();
+ property.Setup(p => p.PropertyType).Returns(typeof(Vehicle));
+ property.Setup(p => p.ReflectedType).Returns(typeof(AssociationSetDiscoveryConventionTest));
+ property.Setup(p => p.Name).Returns("SamplePropertyName");
+
+ Mock<IEntityTypeConfiguration> entityTypeConfiguration = new Mock<IEntityTypeConfiguration>();
+ NavigationPropertyConfiguration config = new NavigationPropertyConfiguration(property.Object, EdmMultiplicity.ZeroOrOne, entityTypeConfiguration.Object);
+
+ Mock<ODataModelBuilder> modelBuilder = new Mock<ODataModelBuilder>();
+
+ // Act & Assert
+ Assert.Throws<InvalidOperationException>(
+ () => AssociationSetDiscoveryConvention.GetTargetEntitySet(config, modelBuilder.Object),
+ "Could not find the target entity type for the navigation property 'SamplePropertyName' on entity type 'System.Web.Http.OData.Builder.Conventions.AssociationSetDiscoveryConventionTest'.");
+ }
+
+ [Fact]
+ public void GetTargetEntitySet_Returns_Null_IfNoMatchingTargetEntitySet()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ IEntityTypeConfiguration motorcycle = builder.AddEntity(typeof(Motorcycle));
+ NavigationPropertyConfiguration navigationProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.ZeroOrOne);
+
+ // Act
+ IEntitySetConfiguration targetEntitySet = AssociationSetDiscoveryConvention.GetTargetEntitySet(navigationProperty, builder);
+
+ // Assert
+ Assert.Null(targetEntitySet);
+ }
+
+ [Fact]
+ public void GetTargetEntitySet_Returns_TargetEntitySet()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ IEntityTypeConfiguration motorcycle = builder.AddEntity(typeof(Motorcycle));
+ IEntityTypeConfiguration manufacturer = builder.AddEntity(typeof(MotorcycleManufacturer));
+ NavigationPropertyConfiguration navigationProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.ZeroOrOne);
+ IEntitySetConfiguration manufacturers = builder.AddEntitySet("manufacturers", manufacturer);
+
+ // Act
+ IEntitySetConfiguration targetEntitySet = AssociationSetDiscoveryConvention.GetTargetEntitySet(navigationProperty, builder);
+
+ // Assert
+ Assert.Equal(manufacturers, targetEntitySet);
+ }
+
+ [Fact]
+ public void GetTargetEntitySet_Returns_Null_IfMultipleMatchingTargetEntitySet()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ IEntityTypeConfiguration motorcycle = builder.AddEntity(typeof(Motorcycle));
+ IEntityTypeConfiguration manufacturer = builder.AddEntity(typeof(MotorcycleManufacturer));
+ NavigationPropertyConfiguration navigationProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.ZeroOrOne);
+ IEntitySetConfiguration manufacturers1 = builder.AddEntitySet("manufacturers1", manufacturer);
+ IEntitySetConfiguration manufacturers2 = builder.AddEntitySet("manufacturers2", manufacturer);
+
+ // Act
+ IEntitySetConfiguration targetEntitySet = AssociationSetDiscoveryConvention.GetTargetEntitySet(navigationProperty, builder);
+
+ // Assert
+ Assert.Null(targetEntitySet);
+ }
+
+ [Fact]
+ public void GetTargetEntitySet_Returns_BaseTypeEntitySet_IfNoMatchingEntitysetForCurrentType()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ IEntityTypeConfiguration motorcycle = builder.AddEntity(typeof(Motorcycle));
+ IEntityTypeConfiguration manufacturer = builder.AddEntity(typeof(Manufacturer));
+ IEntityTypeConfiguration motorcycleManufacturer = builder.AddEntity(typeof(MotorcycleManufacturer)).DerivesFrom(manufacturer);
+
+ NavigationPropertyConfiguration navigationProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.ZeroOrOne);
+ IEntitySetConfiguration manufacturers = builder.AddEntitySet("manufacturers", manufacturer);
+
+ // Act
+ IEntitySetConfiguration targetEntitySet = AssociationSetDiscoveryConvention.GetTargetEntitySet(navigationProperty, builder);
+
+ // Assert
+ Assert.Equal(manufacturers, targetEntitySet);
+ }
+ }
+}
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs
index 9cc4f607..eed7de5d 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/AttributeEdmPropertyConventionTests.cs
@@ -1,7 +1,5 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
using Microsoft.Data.Edm;
@@ -43,7 +41,7 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
Func<Attribute, bool> matchAllFilter = a => true;
// build the type
- Mock<IStructuralTypeConfiguration> structuralType = new Mock<IStructuralTypeConfiguration>(MockBehavior.Strict);
+ Mock<IEntityTypeConfiguration> structuralType = new Mock<IEntityTypeConfiguration>(MockBehavior.Strict);
// build the property
Mock<PropertyInfo> property = new Mock<PropertyInfo>();
@@ -55,11 +53,11 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
Mock<TProperty> propertyConfiguration;
if (typeof(TProperty) == typeof(NavigationPropertyConfiguration))
{
- propertyConfiguration = new Mock<TProperty>(property.Object, EdmMultiplicity.ZeroOrOne);
+ propertyConfiguration = new Mock<TProperty>(property.Object, EdmMultiplicity.ZeroOrOne, structuralType.Object);
}
else
{
- propertyConfiguration = new Mock<TProperty>(property.Object);
+ propertyConfiguration = new Mock<TProperty>(property.Object, structuralType.Object);
}
// build the convention
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs
index 23a2dfc8..47d6ad01 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/DataContractAttributeEdmTypeConventionTests.cs
@@ -46,10 +46,11 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
private static PropertyConfiguration CreateMockProperty(params Attribute[] attributes)
{
+ IStructuralTypeConfiguration structuralType = new Mock<IStructuralTypeConfiguration>().Object;
Mock<PropertyInfo> propertyInfo = new Mock<PropertyInfo>();
propertyInfo.Setup(p => p.PropertyType).Returns(typeof(int));
propertyInfo.Setup(p => p.GetCustomAttributes(It.IsAny<Type>(), It.IsAny<bool>())).Returns(attributes);
- return new PrimitivePropertyConfiguration(propertyInfo.Object);
+ return new PrimitivePropertyConfiguration(propertyInfo.Object, structuralType);
}
}
}
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs
index db00c856..61dfe34d 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/IgnoreDataMemberAttributeEdmPropertyConventionTests.cs
@@ -26,8 +26,8 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
Mock<Type> type = new Mock<Type>();
- Mock<PropertyConfiguration> primitiveProperty = new Mock<PropertyConfiguration>(property.Object);
Mock<IStructuralTypeConfiguration> structuralType = new Mock<IStructuralTypeConfiguration>(MockBehavior.Strict);
+ Mock<PropertyConfiguration> primitiveProperty = new Mock<PropertyConfiguration>(property.Object, structuralType.Object);
structuralType.Setup(e => e.RemoveProperty(property.Object)).Verifiable();
structuralType.Setup(s => s.ClrType).Returns(type.Object);
@@ -51,8 +51,8 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
Mock<Type> type = new Mock<Type>();
type.Setup(t => t.GetCustomAttributes(It.IsAny<Type>(), It.IsAny<bool>())).Returns(new[] { new DataContractAttribute() });
- Mock<PropertyConfiguration> primitiveProperty = new Mock<PropertyConfiguration>(property.Object);
Mock<IStructuralTypeConfiguration> structuralType = new Mock<IStructuralTypeConfiguration>(MockBehavior.Strict);
+ Mock<PropertyConfiguration> primitiveProperty = new Mock<PropertyConfiguration>(property.Object, structuralType.Object);
structuralType.Setup(s => s.ClrType).Returns(type.Object);
// Act
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs
index 8d242aae..cd8929e4 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs
@@ -25,8 +25,8 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
property.Setup(p => p.PropertyType).Returns(typeof(int));
property.Setup(p => p.GetCustomAttributes(It.IsAny<bool>())).Returns(new[] { new KeyAttribute() });
- Mock<PrimitivePropertyConfiguration> primitiveProperty = new Mock<PrimitivePropertyConfiguration>(property.Object);
Mock<IEntityTypeConfiguration> entityType = new Mock<IEntityTypeConfiguration>(MockBehavior.Strict);
+ Mock<PrimitivePropertyConfiguration> primitiveProperty = new Mock<PrimitivePropertyConfiguration>(property.Object, entityType.Object);
entityType.Setup(e => e.HasKey(property.Object)).Returns(entityType.Object).Verifiable();
// Act
@@ -45,8 +45,8 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
property.Setup(p => p.PropertyType).Returns(typeof(int));
property.Setup(p => p.GetCustomAttributes(It.IsAny<bool>())).Returns(new[] { new KeyAttribute() });
- Mock<PrimitivePropertyConfiguration> primitiveProperty = new Mock<PrimitivePropertyConfiguration>(property.Object);
Mock<IComplexTypeConfiguration> complexType = new Mock<IComplexTypeConfiguration>(MockBehavior.Strict);
+ Mock<PrimitivePropertyConfiguration> primitiveProperty = new Mock<PrimitivePropertyConfiguration>(property.Object, complexType.Object);
// Act
new KeyAttributeEdmPropertyConvention().Apply(primitiveProperty.Object, complexType.Object);
@@ -64,8 +64,8 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
property.Setup(p => p.PropertyType).Returns(typeof(int));
property.Setup(p => p.GetCustomAttributes(It.IsAny<bool>())).Returns(new[] { new KeyAttribute() });
- Mock<ComplexPropertyConfiguration> complexProperty = new Mock<ComplexPropertyConfiguration>(property.Object);
Mock<IEntityTypeConfiguration> entityType = new Mock<IEntityTypeConfiguration>(MockBehavior.Strict);
+ Mock<ComplexPropertyConfiguration> complexProperty = new Mock<ComplexPropertyConfiguration>(property.Object, entityType.Object);
// Act
new KeyAttributeEdmPropertyConvention().Apply(complexProperty.Object, entityType.Object);
@@ -83,8 +83,8 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
property.Setup(p => p.PropertyType).Returns(typeof(int));
property.Setup(p => p.GetCustomAttributes(It.IsAny<bool>())).Returns(new[] { new KeyAttribute() });
- Mock<NavigationPropertyConfiguration> navigationProperty = new Mock<NavigationPropertyConfiguration>(property.Object, EdmMultiplicity.ZeroOrOne);
Mock<IEntityTypeConfiguration> entityType = new Mock<IEntityTypeConfiguration>(MockBehavior.Strict);
+ Mock<NavigationPropertyConfiguration> navigationProperty = new Mock<NavigationPropertyConfiguration>(property.Object, EdmMultiplicity.ZeroOrOne, entityType.Object);
// Act
new KeyAttributeEdmPropertyConvention().Apply(navigationProperty.Object, entityType.Object);
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs
index 09f3d089..9ba37424 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/NotMappedAttributeConventionTests.cs
@@ -24,8 +24,8 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
property.Setup(p => p.PropertyType).Returns(typeof(int));
property.Setup(p => p.GetCustomAttributes(It.IsAny<bool>())).Returns(new[] { new NotMappedAttribute() });
- Mock<PropertyConfiguration> primitiveProperty = new Mock<PropertyConfiguration>(property.Object);
Mock<IStructuralTypeConfiguration> structuralType = new Mock<IStructuralTypeConfiguration>(MockBehavior.Strict);
+ Mock<PropertyConfiguration> primitiveProperty = new Mock<PropertyConfiguration>(property.Object, structuralType.Object);
structuralType.Setup(e => e.RemoveProperty(property.Object)).Verifiable();
// Act
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/RequiredAttributeEdmPropertyConventionTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/RequiredAttributeEdmPropertyConventionTests.cs
index 16d9fa6d..f8e40f22 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/RequiredAttributeEdmPropertyConventionTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/Attributes/RequiredAttributeEdmPropertyConventionTests.cs
@@ -24,8 +24,8 @@ namespace System.Web.Http.OData.Builder.Conventions.Attributes
property.Setup(p => p.PropertyType).Returns(typeof(string));
property.Setup(p => p.GetCustomAttributes(It.IsAny<bool>())).Returns(new[] { new RequiredAttribute() });
- Mock<StructuralPropertyConfiguration> structuralProperty = new Mock<StructuralPropertyConfiguration>(property.Object);
Mock<IStructuralTypeConfiguration> structuralType = new Mock<IStructuralTypeConfiguration>();
+ Mock<StructuralPropertyConfiguration> structuralProperty = new Mock<StructuralPropertyConfiguration>(property.Object, structuralType.Object);
// Act
new RequiredAttributeEdmPropertyConvention().Apply(structuralProperty.Object, structuralType.Object);
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs
index f4d3691f..5cfead43 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs
@@ -100,8 +100,9 @@ namespace System.Web.Http.OData.Builder.Conventions
public void GetEntityKeyValue_SingleKey()
{
// Arrange
+ IStructuralTypeConfiguration structuralType = new Mock<IStructuralTypeConfiguration>().Object;
var entityInstance = new { Key = "key" };
- PrimitivePropertyConfiguration[] keys = { new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key")) };
+ PrimitivePropertyConfiguration[] keys = { new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key"), structuralType) };
Mock<IEntityTypeConfiguration> entityType = new Mock<IEntityTypeConfiguration>();
entityType
@@ -120,8 +121,9 @@ namespace System.Web.Http.OData.Builder.Conventions
public void GetEntityKeyValue_SingleKey_DifferentDataTypes(object value, object expectedValue)
{
// Arrange
+ IStructuralTypeConfiguration structuralType = new Mock<IStructuralTypeConfiguration>().Object;
var entityInstance = new { Key = value };
- PrimitivePropertyConfiguration[] keys = { new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key")) };
+ PrimitivePropertyConfiguration[] keys = { new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key"), structuralType) };
Mock<IEntityTypeConfiguration> entityType = new Mock<IEntityTypeConfiguration>();
entityType
@@ -139,12 +141,13 @@ namespace System.Web.Http.OData.Builder.Conventions
public void GetEntityKeyValue_MultipleKeys()
{
// Arrange
+ IStructuralTypeConfiguration structuralType = new Mock<IStructuralTypeConfiguration>().Object;
var entityInstance = new { Key1 = "key1", Key2 = 2, Key3 = true };
PrimitivePropertyConfiguration[] keys =
{
- new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key1")),
- new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key2")),
- new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key3")),
+ new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key1"), structuralType),
+ new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key2"), structuralType),
+ new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key3"), structuralType),
};
Mock<IEntityTypeConfiguration> entityType = new Mock<IEntityTypeConfiguration>();
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/NavigationLinksGenerationConventionTest.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/NavigationLinksGenerationConventionTest.cs
new file mode 100644
index 00000000..f8631f52
--- /dev/null
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/NavigationLinksGenerationConventionTest.cs
@@ -0,0 +1,54 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System.Web.Http.OData.Builder.TestModels;
+using Microsoft.Data.Edm;
+using Microsoft.TestCommon;
+using Moq;
+
+namespace System.Web.Http.OData.Builder.Conventions
+{
+ public class NavigationLinksGenerationConventionTest
+ {
+ private NavigationLinksGenerationConvention _convention = new NavigationLinksGenerationConvention();
+
+ [Fact]
+ public void Apply_AddsNavigationLinkFor_AllBaseDeclaredAndDerivedProperties()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+
+ IEntityTypeConfiguration vehicle = builder.AddEntity(typeof(Vehicle));
+
+ IEntityTypeConfiguration car = builder.AddEntity(typeof(Car)).DerivesFrom(vehicle);
+ NavigationPropertyConfiguration carNavigationProperty = car.AddNavigationProperty(typeof(Car).GetProperty("Manufacturer"), EdmMultiplicity.ZeroOrOne);
+
+ IEntityTypeConfiguration motorcycle = builder.AddEntity(typeof(Motorcycle)).DerivesFrom(vehicle);
+ NavigationPropertyConfiguration motorcycleNavigationProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.ZeroOrOne);
+
+ IEntityTypeConfiguration manufacturer = builder.AddEntity(typeof(Manufacturer));
+ IEntityTypeConfiguration motorcycleManufacturer = builder.AddEntity(typeof(MotorcycleManufacturer)).DerivesFrom(manufacturer);
+ IEntityTypeConfiguration carManufacturer = builder.AddEntity(typeof(CarManufacturer)).DerivesFrom(manufacturer);
+
+ IEntitySetConfiguration manufacturers = builder.AddEntitySet("manufacturers", manufacturer);
+
+
+ Mock<IEntitySetConfiguration> entitySet = new Mock<IEntitySetConfiguration>(MockBehavior.Strict);
+ entitySet.Setup(v => v.EntityType).Returns(vehicle);
+ entitySet.Setup(v => v.GetNavigationPropertyLink(motorcycleNavigationProperty)).Returns<NavigationPropertyConfiguration>(null);
+ entitySet.Setup(v => v.GetNavigationPropertyLink(carNavigationProperty)).Returns<NavigationPropertyConfiguration>(null);
+
+ entitySet
+ .Setup(v => v.HasNavigationPropertyLink(motorcycleNavigationProperty, It.IsAny<Func<EntityInstanceContext, IEdmNavigationProperty, Uri>>()))
+ .Returns<IEntitySetConfiguration>(null);
+ entitySet
+ .Setup(v => v.HasNavigationPropertyLink(carNavigationProperty, It.IsAny<Func<EntityInstanceContext, IEdmNavigationProperty, Uri>>()))
+ .Returns<IEntitySetConfiguration>(null);
+
+ // Act
+ _convention.Apply(entitySet.Object, builder);
+
+ // Assert
+ entitySet.VerifyAll();
+ }
+ }
+}
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs
index 64acec76..2c3ab6a8 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs
@@ -549,6 +549,102 @@ namespace System.Web.Http.OData.Builder.Conventions
}
[Fact]
+ public void ModelBuilder_Figures_Bindings_For_DerivedNavigationProperties()
+ {
+ ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
+ builder.EntitySet<Vehicle>("vehicles");
+ builder.EntitySet<Manufacturer>("manufacturers");
+
+ IEdmModel model = builder.GetEdmModel();
+
+ model.AssertHasEntitySet("vehicles", typeof(Vehicle));
+ IEdmEntitySet vehicles = model.EntityContainers().Single().FindEntitySet("vehicles");
+
+ IEdmEntityType car = model.AssertHasEntityType(typeof(Car));
+ IEdmEntityType motorcycle = model.AssertHasEntityType(typeof(Motorcycle));
+ IEdmEntityType sportbike = model.AssertHasEntityType(typeof(SportBike));
+
+ Assert.Equal(2, vehicles.NavigationTargets.Count());
+ vehicles.AssertHasNavigationTarget(
+ car.AssertHasNavigationProperty(model, "Manufacturer", typeof(CarManufacturer), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne),
+ "manufacturers");
+ vehicles.AssertHasNavigationTarget(
+ motorcycle.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne),
+ "manufacturers");
+ vehicles.AssertHasNavigationTarget(
+ sportbike.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne),
+ "manufacturers");
+ }
+
+ [Fact]
+ public void ModelBuilder_BindsToTheClosestEntitySet_ForNavigationProperties()
+ {
+ ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
+ builder.EntitySet<Vehicle>("vehicles");
+ builder.EntitySet<CarManufacturer>("car_manufacturers");
+ builder.EntitySet<MotorcycleManufacturer>("motorcycle_manufacturers");
+
+ IEdmModel model = builder.GetEdmModel();
+
+ model.AssertHasEntitySet("vehicles", typeof(Vehicle));
+ IEdmEntitySet vehicles = model.EntityContainers().Single().FindEntitySet("vehicles");
+
+ IEdmEntityType car = model.AssertHasEntityType(typeof(Car));
+ IEdmEntityType motorcycle = model.AssertHasEntityType(typeof(Motorcycle));
+ IEdmEntityType sportbike = model.AssertHasEntityType(typeof(SportBike));
+
+ Assert.Equal(2, vehicles.NavigationTargets.Count());
+ vehicles.AssertHasNavigationTarget(
+ car.AssertHasNavigationProperty(model, "Manufacturer", typeof(CarManufacturer), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne),
+ "car_manufacturers");
+ vehicles.AssertHasNavigationTarget(
+ motorcycle.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne),
+ "motorcycle_manufacturers");
+ vehicles.AssertHasNavigationTarget(
+ sportbike.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne),
+ "motorcycle_manufacturers");
+ }
+
+ [Fact]
+ public void ModelBuilder_BindsToAllEntitySets()
+ {
+ ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
+
+ builder.EntitySet<Vehicle>("vehicles");
+ builder.EntitySet<Car>("cars");
+ builder.EntitySet<Motorcycle>("motorcycles");
+ builder.EntitySet<SportBike>("sportbikes");
+ builder.EntitySet<CarManufacturer>("car_manufacturers");
+ builder.EntitySet<MotorcycleManufacturer>("motorcycle_manufacturers");
+
+ IEdmModel model = builder.GetEdmModel();
+
+ // one for motorcycle manufacturer and one for car manufacturer
+ IEdmEntitySet vehicles = model.EntityContainers().Single().FindEntitySet("vehicles");
+ Assert.Equal(2, vehicles.NavigationTargets.Count());
+
+ // one for car manufacturer
+ IEdmEntitySet cars = model.EntityContainers().Single().FindEntitySet("cars");
+ Assert.Equal(1, cars.NavigationTargets.Count());
+
+ // one for motorcycle manufacturer
+ IEdmEntitySet motorcycles = model.EntityContainers().Single().FindEntitySet("motorcycles");
+ Assert.Equal(1, motorcycles.NavigationTargets.Count());
+
+ // one for motorcycle manufacturer
+ IEdmEntitySet sportbikes = model.EntityContainers().Single().FindEntitySet("sportbikes");
+ Assert.Equal(1, sportbikes.NavigationTargets.Count());
+
+ // no navigations
+ IEdmEntitySet carManufacturers = model.EntityContainers().Single().FindEntitySet("car_manufacturers");
+ Assert.Equal(0, carManufacturers.NavigationTargets.Count());
+
+ // no navigations
+ IEdmEntitySet motorcycleManufacturers = model.EntityContainers().Single().FindEntitySet("motorcycle_manufacturers");
+ Assert.Equal(0, motorcycleManufacturers.NavigationTargets.Count());
+ }
+
+ [Fact]
public void ModelBuilder_DerivedTypeDeclaringKeyThrows()
{
MockType baseType =
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs b/test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs
index 482d146b..548ccb81 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs
@@ -13,14 +13,14 @@ namespace System.Web.Http.OData.Builder
public void DerivedProperties_ReturnsAllDerivedProperties()
{
Mock<IEntityTypeConfiguration> entityA = new Mock<IEntityTypeConfiguration>();
- entityA.Setup(e => e.Properties).Returns(new[] { MockProperty("A1"), MockProperty("A2") });
+ entityA.Setup(e => e.Properties).Returns(new[] { MockProperty("A1", entityA.Object), MockProperty("A2", entityA.Object) });
Mock<IEntityTypeConfiguration> entityB = new Mock<IEntityTypeConfiguration>();
- entityB.Setup(e => e.Properties).Returns(new[] { MockProperty("B1"), MockProperty("B2") });
+ entityB.Setup(e => e.Properties).Returns(new[] { MockProperty("B1", entityB.Object), MockProperty("B2", entityB.Object) });
entityB.Setup(e => e.BaseType).Returns(entityA.Object);
Mock<IEntityTypeConfiguration> entityC = new Mock<IEntityTypeConfiguration>();
- entityC.Setup(e => e.Properties).Returns(new[] { MockProperty("C1"), MockProperty("C2") });
+ entityC.Setup(e => e.Properties).Returns(new[] { MockProperty("C1", entityC.Object), MockProperty("C2", entityC.Object) });
entityC.Setup(e => e.BaseType).Returns(entityB.Object);
Assert.Equal(
@@ -102,6 +102,50 @@ namespace System.Web.Http.OData.Builder
new[] { sportbike, motorcycle, car }.Select(e => e.Name).OrderBy(name => name));
}
+ [Fact]
+ public void IsAssignableFrom_ReturnsTrueForDerivedType()
+ {
+ ODataModelBuilder builder = GetMockVehicleModel();
+ IEntityTypeConfiguration vehicle = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "vehicle").Single();
+ IEntityTypeConfiguration car = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "car").Single();
+ IEntityTypeConfiguration motorcycle = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "motorcycle").Single();
+ IEntityTypeConfiguration sportbike = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "sportbike").Single();
+
+ Assert.True(vehicle.IsAssignableFrom(vehicle));
+ Assert.True(vehicle.IsAssignableFrom(car));
+ Assert.True(vehicle.IsAssignableFrom(motorcycle));
+ Assert.True(vehicle.IsAssignableFrom(sportbike));
+ }
+
+ [Fact]
+ public void IsAssignableFrom_ReturnsFalseForBaseType()
+ {
+ ODataModelBuilder builder = GetMockVehicleModel();
+ IEntityTypeConfiguration vehicle = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "vehicle").Single();
+ IEntityTypeConfiguration car = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "car").Single();
+ IEntityTypeConfiguration motorcycle = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "motorcycle").Single();
+ IEntityTypeConfiguration sportbike = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "sportbike").Single();
+
+ Assert.False(car.IsAssignableFrom(vehicle));
+ Assert.False(motorcycle.IsAssignableFrom(vehicle));
+ Assert.False(sportbike.IsAssignableFrom(vehicle));
+ }
+
+ [Fact]
+ public void IsAssignableFrom_ReturnsFalseForUnRelatedTypes()
+ {
+ ODataModelBuilder builder = GetMockVehicleModel();
+ IEntityTypeConfiguration vehicle = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "vehicle").Single();
+ IEntityTypeConfiguration car = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "car").Single();
+ IEntityTypeConfiguration motorcycle = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "motorcycle").Single();
+ IEntityTypeConfiguration sportbike = builder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.Name == "sportbike").Single();
+
+ Assert.False(motorcycle.IsAssignableFrom(car));
+ Assert.False(car.IsAssignableFrom(motorcycle));
+ Assert.False(sportbike.IsAssignableFrom(car));
+ Assert.False(car.IsAssignableFrom(sportbike));
+ }
+
private static ODataModelBuilder GetMockVehicleModel()
{
Mock<IEntityTypeConfiguration> vehicle = new Mock<IEntityTypeConfiguration>();
@@ -126,12 +170,12 @@ namespace System.Web.Http.OData.Builder
return modelBuilder.Object;
}
- private static PropertyConfiguration MockProperty(string name)
+ private static PropertyConfiguration MockProperty(string name, IStructuralTypeConfiguration declaringType)
{
Mock<PropertyInfo> propertyInfo = new Mock<PropertyInfo>();
propertyInfo.Setup(p => p.Name).Returns(name);
- Mock<PropertyConfiguration> property = new Mock<PropertyConfiguration>(propertyInfo.Object);
+ Mock<PropertyConfiguration> property = new Mock<PropertyConfiguration>(propertyInfo.Object, declaringType);
return property.Object;
}
}
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/EntitySetTest.cs b/test/System.Web.Http.OData.Test/OData/Builder/EntitySetTest.cs
index 824dc102..339ca544 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/EntitySetTest.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/EntitySetTest.cs
@@ -45,5 +45,187 @@ namespace System.Web.Http.OData.Builder
Assert.NotNull(customerOrders);
Assert.Equal("Orders", customerOrders.TargetEntitySet.Name);
}
+
+ [Fact]
+ public void CanAddBinding_For_DerivedNavigationProperty()
+ {
+ ODataModelBuilder builder = new ODataModelBuilder();
+
+ var vehicle = builder.AddEntity(typeof(Vehicle));
+ var motorcycle = builder.AddEntity(typeof(Motorcycle)).DerivesFrom(vehicle);
+ var manufacturer = builder.AddEntity(typeof(MotorcycleManufacturer));
+ var manufacturers = builder.AddEntitySet("manufacturers", manufacturer);
+ var navProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.One);
+
+ var vehicles = builder.AddEntitySet("vehicles", vehicle);
+ vehicles.AddBinding(navProperty, manufacturers);
+
+ IEdmModel model = builder.GetEdmModel();
+ var motorcycleEdmType = model.AssertHasEntityType(typeof(Motorcycle));
+ var edmNavProperty = motorcycleEdmType.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: false, multiplicity: EdmMultiplicity.ZeroOrOne);
+
+ Assert.Equal(
+ "manufacturers",
+ model.EntityContainers().Single().FindEntitySet("vehicles").FindNavigationTarget(edmNavProperty).Name);
+ }
+
+ [Fact]
+ public void CanAddNavigationLink_For_DerivedNavigationProperty()
+ {
+ ODataModelBuilder builder = new ODataModelBuilder();
+
+ var vehicle = builder.AddEntity(typeof(Vehicle));
+ var motorcycle = builder.AddEntity(typeof(Motorcycle)).DerivesFrom(vehicle);
+ var manufacturer = builder.AddEntity(typeof(MotorcycleManufacturer));
+ var manufacturers = builder.AddEntitySet("manufacturers", manufacturer);
+ var navProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.One);
+
+ var vehicles = builder.AddEntitySet("vehicles", vehicle);
+ var binding = vehicles.AddBinding(navProperty, manufacturers);
+ vehicles.HasNavigationPropertyLink(navProperty, (ctxt, property) => new Uri("http://works/"));
+
+ IEdmModel model = builder.GetEdmModel();
+ var motorcycleEdmType = model.AssertHasEntityType(typeof(Motorcycle));
+ var edmNavProperty = motorcycleEdmType.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: false, multiplicity: EdmMultiplicity.ZeroOrOne);
+ var vehiclesEdmSet = model.EntityContainers().Single().FindEntitySet("vehicles");
+
+ Assert.NotNull(model.GetEntitySetLinkBuilder(vehiclesEdmSet));
+ Assert.Equal(
+ "http://works/",
+ model.GetEntitySetLinkBuilder(vehiclesEdmSet).BuildNavigationLink(new EntityInstanceContext(), edmNavProperty).AbsoluteUri);
+ }
+
+ [Fact]
+ public void AddBinding_For_NavigationPropertyInHierarchy_Throws()
+ {
+ ODataModelBuilder builder = new ODataModelBuilder();
+
+ var vehicle = builder.AddEntity(typeof(Vehicle));
+ var motorcycle = builder.AddEntity(typeof(Motorcycle));
+ var manufacturer = builder.AddEntity(typeof(MotorcycleManufacturer));
+ var manufacturers = builder.AddEntitySet("manufacturers", manufacturer);
+
+ var navProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.One);
+
+ var vehicles = builder.AddEntitySet("vehicles", vehicle);
+
+ Assert.Throws<InvalidOperationException>(
+ () => vehicles.AddBinding(navProperty, manufacturers),
+ "The declaring entity type 'System.Web.Http.OData.Builder.TestModels.Motorcycle' of the given navigation property is not a part of the entity type 'System.Web.Http.OData.Builder.TestModels.Vehicle' hierarchy of the entity set 'vehicles'.");
+ }
+
+ [Fact]
+ public void AddNavigationLink_For_NavigationPropertyInHierarchy_Throws()
+ {
+ ODataModelBuilder builder = new ODataModelBuilder();
+
+ var vehicle = builder.AddEntity(typeof(Vehicle));
+ var motorcycle = builder.AddEntity(typeof(Motorcycle));
+ var manufacturer = builder.AddEntity(typeof(MotorcycleManufacturer));
+ var manufacturers = builder.AddEntitySet("manufacturers", manufacturer);
+
+ var navProperty = motorcycle.AddNavigationProperty(typeof(Motorcycle).GetProperty("Manufacturer"), EdmMultiplicity.One);
+
+ var vehicles = builder.AddEntitySet("vehicles", vehicle);
+
+ Assert.Throws<InvalidOperationException>(
+ () => vehicles.HasNavigationPropertyLink(navProperty, (ctxt, property) => new Uri("http://works/")),
+ "The declaring entity type 'System.Web.Http.OData.Builder.TestModels.Motorcycle' of the given navigation property is not a part of the entity type 'System.Web.Http.OData.Builder.TestModels.Vehicle' hierarchy of the entity set 'vehicles'.");
+ }
+
+ [Fact]
+ public void CanConfigureOptionalBinding_For_NavigationPropertiesInDerivedType()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ builder
+ .EntitySet<Vehicle>("vehicles")
+ .HasOptionalBinding((Motorcycle m) => m.Manufacturer, "manufacturers");
+
+ // Act
+ IEdmModel model = builder.GetEdmModel();
+
+ // Assert
+ var vehicles = model.EntityContainers().Single().FindEntitySet("vehicles");
+ Assert.NotNull(vehicles);
+
+ var motorcycle = model.AssertHasEntityType(typeof(Motorcycle));
+ var motorcycleManufacturerProperty = motorcycle.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne);
+
+ var motorcycleManufacturerPropertyTargetSet = vehicles.FindNavigationTarget(motorcycleManufacturerProperty);
+ Assert.NotNull(motorcycleManufacturerPropertyTargetSet);
+ Assert.Equal("manufacturers", motorcycleManufacturerPropertyTargetSet.Name);
+ }
+
+ [Fact]
+ public void CanConfigureRequiredBinding_For_NavigationPropertiesInDerivedType()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ builder
+ .EntitySet<Vehicle>("vehicles")
+ .HasRequiredBinding((Motorcycle m) => m.Manufacturer, "manufacturers");
+
+ // Act
+ IEdmModel model = builder.GetEdmModel();
+
+ // Assert
+ var vehicles = model.EntityContainers().Single().FindEntitySet("vehicles");
+ Assert.NotNull(vehicles);
+
+ var motorcycle = model.AssertHasEntityType(typeof(Motorcycle));
+ var motorcycleManufacturerProperty = motorcycle.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: false, multiplicity: EdmMultiplicity.ZeroOrOne);
+
+ var motorcycleManufacturerPropertyTargetSet = vehicles.FindNavigationTarget(motorcycleManufacturerProperty);
+ Assert.NotNull(motorcycleManufacturerPropertyTargetSet);
+ Assert.Equal("manufacturers", motorcycleManufacturerPropertyTargetSet.Name);
+ }
+
+ [Fact]
+ public void CanConfigureManyBinding_For_NavigationPropertiesInDerivedType()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ builder
+ .EntitySet<Vehicle>("vehicles")
+ .HasManyBinding((Motorcycle m) => m.Manufacturers, "manufacturers");
+
+ // Act
+ IEdmModel model = builder.GetEdmModel();
+
+ // Assert
+ var vehicles = model.EntityContainers().Single().FindEntitySet("vehicles");
+ Assert.NotNull(vehicles);
+
+ var motorcycle = model.AssertHasEntityType(typeof(Motorcycle));
+ var motorcycleManufacturerProperty = motorcycle.AssertHasNavigationProperty(model, "Manufacturers", typeof(MotorcycleManufacturer), isNullable: false, multiplicity: EdmMultiplicity.Many);
+
+ var motorcycleManufacturerPropertyTargetSet = vehicles.FindNavigationTarget(motorcycleManufacturerProperty);
+ Assert.NotNull(motorcycleManufacturerPropertyTargetSet);
+ Assert.Equal("manufacturers", motorcycleManufacturerPropertyTargetSet.Name);
+ }
+
+ [Fact]
+ public void CanConfigureLinks_For_NavigationPropertiesInDerivedType()
+ {
+ ODataModelBuilder builder = new ODataModelBuilder();
+
+ var vehiclesSet = builder.EntitySet<Vehicle>("vehicles");
+
+ vehiclesSet.HasNavigationPropertyLink(
+ vehiclesSet.HasOptionalBinding((Motorcycle m) => m.Manufacturer, "manufacturers").NavigationProperty,
+ (ctxt, property) => new Uri(String.Format("http://localhost/vehicles/{0}/{1}/{2}", ctxt.EntityInstance.Model, ctxt.EntityInstance.Name, property.Name)));
+
+ IEdmModel model = builder.GetEdmModel();
+ var vehicles = model.EntityContainers().Single().FindEntitySet("vehicles");
+ var motorcycle = model.AssertHasEntityType(typeof(Motorcycle));
+ var motorcycleManufacturerProperty = motorcycle.AssertHasNavigationProperty(model, "Manufacturer", typeof(MotorcycleManufacturer), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne);
+
+ Uri link = model.GetEntitySetLinkBuilder(vehicles).BuildNavigationLink(
+ new EntityInstanceContext { EntityInstance = new Motorcycle { Name = "Motorcycle1", Model = 2009 }, EdmModel = model, EntitySet = vehicles, EntityType = motorcycle },
+ motorcycleManufacturerProperty);
+
+ Assert.Equal("http://localhost/vehicles/2009/Motorcycle1/Manufacturer", link.AbsoluteUri);
+ }
}
}
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/TestModels/InheritanceModels.cs b/test/System.Web.Http.OData.Test/OData/Builder/TestModels/InheritanceModels.cs
index 1e479d78..c86c9c77 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/TestModels/InheritanceModels.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/TestModels/InheritanceModels.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -43,6 +44,9 @@ namespace System.Web.Http.OData.Builder.TestModels
public int ID { get; set; }
public MotorcycleManufacturer Manufacturer { get; set; }
+
+ [NotMapped]
+ public IEnumerable<MotorcycleManufacturer> Manufacturers { get; set; }
}
public class SportBike : Motorcycle
diff --git a/test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj b/test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj
index d2a1a89e..a2d74690 100644
--- a/test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj
+++ b/test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj
@@ -105,6 +105,8 @@
</Compile>
<Compile Include="OData\Builder\ActionConfigurationTest.cs" />
<Compile Include="OData\Builder\BindableProcedureFinderAnnotationTest.cs" />
+ <Compile Include="OData\Builder\Conventions\AssociationSetDiscoveryConventionTest.cs" />
+ <Compile Include="OData\Builder\Conventions\NavigationLinksGenerationConventionTest.cs" />
<Compile Include="OData\Builder\ParameterConfigurationTest.cs" />
<Compile Include="OData\Builder\CollectionPropertyConfigurationTest.cs" />
<Compile Include="OData\Builder\TestModels\EnumModel.cs" />