diff --git a/OData/src/System.Web.OData/OData/Builder/Conventions/Attributes/ComplexTypeAttributeConvention.cs b/OData/src/System.Web.OData/OData/Builder/Conventions/Attributes/ComplexTypeAttributeConvention.cs
index cd444b6afc..2f76c40b5a 100644
--- a/OData/src/System.Web.OData/OData/Builder/Conventions/Attributes/ComplexTypeAttributeConvention.cs
+++ b/OData/src/System.Web.OData/OData/Builder/Conventions/Attributes/ComplexTypeAttributeConvention.cs
@@ -30,6 +30,12 @@ public override void Apply(EntityTypeConfiguration edmTypeConfiguration, ODataCo
{
edmTypeConfiguration.RemoveKey(key);
}
+
+ EnumPropertyConfiguration[] enumKeys = edmTypeConfiguration.EnumKeys.ToArray();
+ foreach (EnumPropertyConfiguration key in enumKeys)
+ {
+ edmTypeConfiguration.RemoveKey(key);
+ }
}
}
}
diff --git a/OData/src/System.Web.OData/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConvention.cs b/OData/src/System.Web.OData/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConvention.cs
index 5d495a357d..45113b9ad7 100644
--- a/OData/src/System.Web.OData/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConvention.cs
+++ b/OData/src/System.Web.OData/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConvention.cs
@@ -10,7 +10,7 @@ namespace System.Web.OData.Builder.Conventions.Attributes
///
/// Configures properties that have the as keys in the .
///
- internal class KeyAttributeEdmPropertyConvention : AttributeEdmPropertyConvention
+ internal class KeyAttributeEdmPropertyConvention : AttributeEdmPropertyConvention
{
public KeyAttributeEdmPropertyConvention()
: base(attribute => attribute.GetType() == typeof(KeyAttribute), allowMultiple: false)
@@ -24,7 +24,7 @@ public KeyAttributeEdmPropertyConvention()
/// The edm type being configured.
/// The found on the property.
/// The ODataConventionModelBuilder used to build the model.
- public override void Apply(PrimitivePropertyConfiguration edmProperty,
+ public override void Apply(StructuralPropertyConfiguration edmProperty,
StructuralTypeConfiguration structuralTypeConfiguration,
Attribute attribute,
ODataConventionModelBuilder model)
@@ -34,10 +34,13 @@ public override void Apply(PrimitivePropertyConfiguration edmProperty,
throw Error.ArgumentNull("edmProperty");
}
- EntityTypeConfiguration entity = structuralTypeConfiguration as EntityTypeConfiguration;
- if (entity != null)
+ if (edmProperty.Kind == PropertyKind.Primitive || edmProperty.Kind == PropertyKind.Enum)
{
- entity.HasKey(edmProperty.PropertyInfo);
+ EntityTypeConfiguration entity = structuralTypeConfiguration as EntityTypeConfiguration;
+ if (entity != null)
+ {
+ entity.HasKey(edmProperty.PropertyInfo);
+ }
}
}
}
diff --git a/OData/src/System.Web.OData/OData/Builder/Conventions/EntityKeyConvention.cs b/OData/src/System.Web.OData/OData/Builder/Conventions/EntityKeyConvention.cs
index fa9c513e4c..5a272857f0 100644
--- a/OData/src/System.Web.OData/OData/Builder/Conventions/EntityKeyConvention.cs
+++ b/OData/src/System.Web.OData/OData/Builder/Conventions/EntityKeyConvention.cs
@@ -27,19 +27,21 @@ public override void Apply(EntityTypeConfiguration entity, ODataConventionModelB
}
// Suppress the EntityKeyConvention if there is any key in EntityTypeConfiguration.
- if (entity.Keys.Any())
+ if (entity.Keys.Any() || entity.EnumKeys.Any())
{
return;
}
- // Try to figure out keys only if there is no base type.
- if (entity.BaseType == null)
+ // Suppress the EntityKeyConvention if base type has any key.
+ if (entity.BaseType != null && entity.BaseType.Keys().Any())
{
- PropertyConfiguration key = GetKeyProperty(entity);
- if (key != null)
- {
- entity.HasKey(key.PropertyInfo);
- }
+ return;
+ }
+
+ PropertyConfiguration key = GetKeyProperty(entity);
+ if (key != null)
+ {
+ entity.HasKey(key.PropertyInfo);
}
}
@@ -48,7 +50,7 @@ private static PropertyConfiguration GetKeyProperty(EntityTypeConfiguration enti
IEnumerable keys =
entityType.Properties
.Where(p => (p.Name.Equals(entityType.Name + "Id", StringComparison.OrdinalIgnoreCase) || p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase))
- && EdmLibHelpers.GetEdmPrimitiveTypeOrNull(p.PropertyInfo.PropertyType) != null);
+ && (EdmLibHelpers.GetEdmPrimitiveTypeOrNull(p.PropertyInfo.PropertyType) != null || TypeHelper.IsEnum(p.PropertyInfo.PropertyType)));
if (keys.Count() == 1)
{
diff --git a/OData/src/System.Web.OData/OData/Builder/EdmTypeBuilder.cs b/OData/src/System.Web.OData/OData/Builder/EdmTypeBuilder.cs
index 01764e9024..fac47b91e2 100644
--- a/OData/src/System.Web.OData/OData/Builder/EdmTypeBuilder.cs
+++ b/OData/src/System.Web.OData/OData/Builder/EdmTypeBuilder.cs
@@ -290,6 +290,10 @@ private void CreateEntityTypeBody(EdmEntityType type, EntityTypeConfiguration co
CreateStructuralTypeBody(type, config);
IEnumerable keys = config.Keys.Select(p => type.DeclaredProperties.OfType().First(dp => dp.Name == p.Name));
type.AddKeys(keys);
+
+ // Add the Enum keys
+ keys = config.EnumKeys.Select(p => type.DeclaredProperties.OfType().First(dp => dp.Name == p.Name));
+ type.AddKeys(keys);
}
private void CreateNavigationProperty(EntityTypeConfiguration config)
diff --git a/OData/src/System.Web.OData/OData/Builder/EdmTypeConfigurationExtensions.cs b/OData/src/System.Web.OData/OData/Builder/EdmTypeConfigurationExtensions.cs
index 224caee05c..6196ce4927 100644
--- a/OData/src/System.Web.OData/OData/Builder/EdmTypeConfigurationExtensions.cs
+++ b/OData/src/System.Web.OData/OData/Builder/EdmTypeConfigurationExtensions.cs
@@ -77,9 +77,9 @@ public static IEnumerable DerivedProperties(
public static IEnumerable Keys(this EntityTypeConfiguration entity)
{
Contract.Assert(entity != null);
- if (entity.Keys.Any())
+ if (entity.Keys.Any() || entity.EnumKeys.Any())
{
- return entity.Keys;
+ return entity.Keys.OfType().Concat(entity.EnumKeys);
}
if (entity.BaseType == null)
diff --git a/OData/src/System.Web.OData/OData/Builder/EntityTypeConfiguration.cs b/OData/src/System.Web.OData/OData/Builder/EntityTypeConfiguration.cs
index 08df190eab..b7e355846f 100644
--- a/OData/src/System.Web.OData/OData/Builder/EntityTypeConfiguration.cs
+++ b/OData/src/System.Web.OData/OData/Builder/EntityTypeConfiguration.cs
@@ -19,6 +19,7 @@ namespace System.Web.OData.Builder
public class EntityTypeConfiguration : StructuralTypeConfiguration
{
private List _keys = new List();
+ private List _enumKeys = new List();
///
/// Initializes a new instance of the class.
@@ -68,6 +69,14 @@ public virtual IEnumerable Keys
}
}
+ ///
+ /// Gets the collection of enum keys for this entity type.
+ ///
+ public virtual IEnumerable EnumKeys
+ {
+ get { return _enumKeys; }
+ }
+
///
/// Gets or sets the base type of this entity type.
///
@@ -105,14 +114,31 @@ public virtual EntityTypeConfiguration HasKey(PropertyInfo keyProperty)
throw Error.InvalidOperation(SRResources.CannotDefineKeysOnDerivedTypes, FullName, BaseType.FullName);
}
- PrimitivePropertyConfiguration propertyConfig = AddProperty(keyProperty);
+ // Add the enum key if the property type is enum
+ if (keyProperty.PropertyType.IsEnum)
+ {
+ ModelBuilder.AddEnumType(keyProperty.PropertyType);
+ EnumPropertyConfiguration enumConfig = AddEnumProperty(keyProperty);
- // keys are always required
- propertyConfig.IsRequired();
+ // keys are always required
+ enumConfig.IsRequired();
- if (!_keys.Contains(propertyConfig))
+ if (!_enumKeys.Contains(enumConfig))
+ {
+ _enumKeys.Add(enumConfig);
+ }
+ }
+ else
{
- _keys.Add(propertyConfig);
+ PrimitivePropertyConfiguration propertyConfig = AddProperty(keyProperty);
+
+ // keys are always required
+ propertyConfig.IsRequired();
+
+ if (!_keys.Contains(propertyConfig))
+ {
+ _keys.Add(propertyConfig);
+ }
}
return this;
@@ -134,6 +160,22 @@ public virtual void RemoveKey(PrimitivePropertyConfiguration keyProperty)
_keys.Remove(keyProperty);
}
+ ///
+ /// Removes the enum property from the entity enum keys collection.
+ ///
+ /// The key to be removed.
+ /// This method just disable the property to be not a key anymore. It does not remove the property all together.
+ /// To remove the property completely, use the method
+ public virtual void RemoveKey(EnumPropertyConfiguration enumKeyProperty)
+ {
+ if (enumKeyProperty == null)
+ {
+ throw Error.ArgumentNull("enumKeyProperty");
+ }
+
+ _enumKeys.Remove(enumKeyProperty);
+ }
+
///
/// Sets the base type of this entity type to null meaning that this entity type
/// does not derive from anything.
@@ -152,7 +194,7 @@ public virtual EntityTypeConfiguration DerivesFromNothing()
/// Returns itself so that multiple calls can be chained.
public virtual EntityTypeConfiguration DerivesFrom(EntityTypeConfiguration baseType)
{
- if (Keys.Any() && baseType.Keys().Any())
+ if ((Keys.Any() || EnumKeys.Any()) && baseType.Keys().Any())
{
throw Error.InvalidOperation(SRResources.CannotDefineKeysOnDerivedTypes, FullName, baseType.FullName);
}
diff --git a/OData/src/System.Web.OData/OData/Builder/ODataConventionModelBuilder.cs b/OData/src/System.Web.OData/OData/Builder/ODataConventionModelBuilder.cs
index 49a47a21d8..601ee69150 100644
--- a/OData/src/System.Web.OData/OData/Builder/ODataConventionModelBuilder.cs
+++ b/OData/src/System.Web.OData/OData/Builder/ODataConventionModelBuilder.cs
@@ -308,6 +308,11 @@ internal void DiscoverInheritanceRelationships()
{
entity.RemoveKey(keyProperty);
}
+
+ foreach (EnumPropertyConfiguration enumKeyProperty in entity.EnumKeys.ToArray())
+ {
+ entity.RemoveKey(enumKeyProperty);
+ }
}
entity.DerivesFrom(baseEntityType);
diff --git a/OData/src/System.Web.OData/OData/Routing/ODataPathSegmentTranslator.cs b/OData/src/System.Web.OData/OData/Routing/ODataPathSegmentTranslator.cs
index 5297e00c3f..6af9f8cc55 100644
--- a/OData/src/System.Web.OData/OData/Routing/ODataPathSegmentTranslator.cs
+++ b/OData/src/System.Web.OData/OData/Routing/ODataPathSegmentTranslator.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Collections.Generic;
-using System.Collections.Specialized;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Http;
@@ -422,6 +421,16 @@ private static string TranslateKeySegmentValue(object value, bool enableUriTempl
}
}
+ ConstantNode constantNode = value as ConstantNode;
+ if (constantNode != null)
+ {
+ ODataEnumValue enumValue = constantNode.Value as ODataEnumValue;
+ if (enumValue != null)
+ {
+ return ODataUriUtils.ConvertToUriLiteral(enumValue, ODataVersion.V4);
+ }
+ }
+
return ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V4);
}
diff --git a/OData/test/System.Web.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs b/OData/test/System.Web.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs
index 99b1d38663..ae51743c7f 100644
--- a/OData/test/System.Web.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs
+++ b/OData/test/System.Web.OData.Test/OData/Builder/Conventions/Attributes/KeyAttributeEdmPropertyConventionTests.cs
@@ -79,6 +79,7 @@ public void Apply_IgnoresKey_ComplexProperty()
Mock complexProperty =
new Mock(property.Object, entityType.Object);
+ complexProperty.Setup(c => c.Kind).Returns(PropertyKind.Complex);
// Act
new KeyAttributeEdmPropertyConvention().Apply(complexProperty.Object, entityType.Object, builder);
@@ -102,6 +103,7 @@ public void Apply_IgnoresKey_NavigationProperty()
Mock navigationProperty =
new Mock(property.Object, EdmMultiplicity.ZeroOrOne, entityType.Object);
+ navigationProperty.Setup(c => c.Kind).Returns(PropertyKind.Navigation);
// Act
new KeyAttributeEdmPropertyConvention().Apply(navigationProperty.Object, entityType.Object, builder);
@@ -109,5 +111,33 @@ public void Apply_IgnoresKey_NavigationProperty()
// Assert
entityType.Verify();
}
+
+ [Fact]
+ public void Apply_AddsEnumKey_EntityTypeConfiguration()
+ {
+ // Arrange
+ ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
+
+ Mock property = new Mock();
+ property.Setup(p => p.Name).Returns("Property");
+ property.Setup(p => p.PropertyType).Returns(typeof(MyEnumType));
+ property.Setup(p => p.GetCustomAttributes(It.IsAny())).Returns(new[] { new KeyAttribute() });
+
+ Mock entityType = new Mock(MockBehavior.Strict);
+ entityType.Setup(e => e.HasKey(property.Object)).Returns(entityType.Object).Verifiable();
+
+ Mock enumProperty =
+ new Mock(property.Object, entityType.Object);
+
+ // Act
+ new KeyAttributeEdmPropertyConvention().Apply(enumProperty.Object, entityType.Object, builder);
+
+ // Assert
+ entityType.Verify();
+ }
+
+ enum MyEnumType
+ {
+ }
}
}
diff --git a/OData/test/System.Web.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs b/OData/test/System.Web.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs
index c58d233b99..a0f2e4add6 100644
--- a/OData/test/System.Web.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs
+++ b/OData/test/System.Web.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations.Schema;
+using System.Web.OData.Builder.TestModels;
using System.Web.OData.TestCommon;
using Microsoft.OData.Edm;
using Microsoft.TestCommon;
@@ -35,6 +36,33 @@ public void Apply_Calls_HasKey_OnEdmType(string propertyName)
mockEntityType.Verify();
}
+ [Fact]
+ public void Apply_Calls_HasKey_ForEnumProperty_OnEdmType()
+ {
+ // Arrange
+ Mock mockEntityType = new Mock();
+ Mock property =
+ new Mock(typeof(EntityKeyConventionTests_EntityType).GetProperty("ColorId"),
+ mockEntityType.Object);
+ property.Setup(c => c.Kind).Returns(PropertyKind.Enum);
+
+ mockEntityType.Setup(e => e.Name).Returns("Color");
+ mockEntityType.Setup(
+ entityType => entityType.HasKey(typeof(EntityKeyConventionTests_EntityType).GetProperty("ColorId")))
+ .Returns(mockEntityType.Object)
+ .Verifiable();
+
+ mockEntityType.Object.ExplicitProperties.Add(new MockPropertyInfo(), property.Object);
+
+ var mockModelBuilder = new Mock(MockBehavior.Strict);
+
+ // Act
+ new EntityKeyConvention().Apply(mockEntityType.Object, mockModelBuilder.Object);
+
+ // Assert
+ mockEntityType.Verify();
+ }
+
[Fact]
public void EntityKeyConvention_FiguresOutTheKeyProperty()
{
@@ -51,6 +79,31 @@ public void EntityKeyConvention_FiguresOutTheKeyProperty()
entity.AssertHasKey(model, "ID", EdmPrimitiveTypeKind.Int64);
}
+ [Fact]
+ public void EntityKeyConvention_FiguresOutTheEnumKeyProperty()
+ {
+ // Arrange
+ MockType baseType =
+ new MockType("BaseType")
+ .Property("ID");
+
+ ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
+ builder.AddEntityType(baseType);
+
+ // Act
+ IEdmModel model = builder.GetEdmModel();
+
+ // Assert
+ IEdmEntityType entity = model.AssertHasEntityType(baseType);
+
+ IEdmStructuralProperty enumProperty = entity.AssertHasProperty(model, "ID", typeof(Color), false);
+ IEdmProperty enumKey = Assert.Single(entity.DeclaredKey);
+ Assert.Same(enumProperty, enumKey);
+
+ Assert.Equal(EdmTypeKind.Enum, enumKey.Type.TypeKind());
+ Assert.Equal("System.Web.OData.Builder.TestModels.Color", enumKey.Type.Definition.FullTypeName());
+ }
+
[Fact]
public void EntityKeyConvention_DoesnotFigureOutKeyPropertyOnDerivedTypes()
{
@@ -101,6 +154,8 @@ class EntityKeyConventionTests_EntityType
public string iD { get; set; }
public string SampleEntityID { get; set; }
+
+ public Color ColorId { get; set; }
}
}
}
diff --git a/OData/test/System.Web.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs b/OData/test/System.Web.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs
index 08b76c9d4d..68f488d814 100644
--- a/OData/test/System.Web.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs
+++ b/OData/test/System.Web.OData.Test/OData/Builder/Conventions/ODataConventionModelBuilderTests.cs
@@ -243,6 +243,39 @@ public void ModelBuilder_ProductsWithKeyAttribute()
version.AssertHasPrimitiveProperty(model, "Minor", EdmPrimitiveTypeKind.Int32, isNullable: false);
}
+ [Fact]
+ public void ModelBuilder_ProductsWithEnumKeyAttribute()
+ {
+ // Arrange
+ ODataConventionModelBuilder modelBuilder = new ODataConventionModelBuilder();
+ modelBuilder.EntityType();
+
+ // Act
+ IEdmModel model = modelBuilder.GetEdmModel();
+
+ // Assert
+ Assert.NotNull(model);
+ Assert.Equal(2, model.SchemaElements.OfType().Count());
+
+ IEdmEntityType product = model.AssertHasEntityType(mappedEntityClrType: typeof(ProductWithEnumKeyAttribute));
+ product.AssertHasPrimitiveProperty(model, "Name", EdmPrimitiveTypeKind.String, isNullable: true);
+ Assert.Equal(2, product.StructuralProperties().Count());
+ Assert.Empty(product.NavigationProperties());
+
+ IEdmStructuralProperty enumKey = Assert.Single(product.DeclaredKey);
+ Assert.Equal("ProductColor", enumKey.Name);
+ Assert.Equal(EdmTypeKind.Enum, enumKey.Type.Definition.TypeKind);
+ Assert.Equal("System.Web.OData.Builder.TestModels.Color", enumKey.Type.Definition.FullTypeName());
+ Assert.False(enumKey.Type.IsNullable);
+
+ IEdmEnumType colorType = Assert.Single(model.SchemaElements.OfType());
+ Assert.Equal("System.Web.OData.Builder.TestModels.Color", enumKey.Type.Definition.FullTypeName());
+ Assert.Equal(3, colorType.Members.Count());
+ Assert.Single(colorType.Members.Where(m => m.Name == "Red"));
+ Assert.Single(colorType.Members.Where(m => m.Name == "Green"));
+ Assert.Single(colorType.Members.Where(m => m.Name == "Blue"));
+ }
+
[Fact]
public void ModelBuilder_ProductsWithFilterSortable()
{
@@ -1671,6 +1704,42 @@ public void DerivedTypes_Can_DefineKeys_InQueryCompositionMode()
derivedEntityType.AssertHasPrimitiveProperty(model, "DerivedTypeId", EdmPrimitiveTypeKind.Int32, isNullable: false);
}
+ [Fact]
+ public void DerivedTypes_Can_DefineEnumKeys_InQueryCompositionMode()
+ {
+ // Arrange
+ MockType baseType = new MockType("BaseType")
+ .Property(typeof(Color), "ID");
+
+ MockType derivedType = new MockType("DerivedType")
+ .Property(typeof(Color), "DerivedTypeId")
+ .BaseType(baseType);
+
+ MockAssembly assembly = new MockAssembly(baseType, derivedType);
+
+ HttpConfiguration configuration = new HttpConfiguration();
+ configuration.Services.Replace(typeof(IAssembliesResolver), new TestAssemblyResolver(assembly));
+ var builder = new ODataConventionModelBuilder(configuration, isQueryCompositionMode: true);
+
+ builder.AddEntitySet("bases", builder.AddEntityType(baseType));
+
+ // Act
+ IEdmModel model = builder.GetEdmModel();
+
+ // Assert
+ model.AssertHasEntitySet("bases", baseType);
+ IEdmEntityType baseEntityType = model.AssertHasEntityType(baseType);
+ IEdmStructuralProperty key = Assert.Single(baseEntityType.DeclaredKey);
+ Assert.Equal(EdmTypeKind.Enum, key.Type.TypeKind());
+ Assert.Equal("System.Web.OData.Builder.TestModels.Color", key.Type.Definition.FullTypeName());
+
+ IEdmEntityType derivedEntityType = model.AssertHasEntityType(derivedType, baseType);
+ Assert.Null(derivedEntityType.DeclaredKey);
+ IEdmProperty derivedId = Assert.Single(derivedEntityType.DeclaredProperties);
+ Assert.Equal(EdmTypeKind.Enum, derivedId.Type.TypeKind());
+ Assert.Equal("System.Web.OData.Builder.TestModels.Color", derivedId.Type.Definition.FullTypeName());
+ }
+
[Fact]
public void ModelBuilder_DerivedComplexTypeHavingKeys_Throws()
{
@@ -2654,6 +2723,14 @@ public class ProductWithKeyAttribute
public CategoryWithKeyAttribute Category { get; set; }
}
+ public class ProductWithEnumKeyAttribute
+ {
+ [Key]
+ public Color ProductColor { get; set; }
+
+ public string Name { get; set; }
+ }
+
public class ProductWithFilterSortable
{
public int ID { get; set; }
diff --git a/OData/test/System.Web.OData.Test/OData/Builder/EntityTypeTest.cs b/OData/test/System.Web.OData.Test/OData/Builder/EntityTypeTest.cs
index 6fa39a1341..4d7663fa65 100644
--- a/OData/test/System.Web.OData.Test/OData/Builder/EntityTypeTest.cs
+++ b/OData/test/System.Web.OData.Test/OData/Builder/EntityTypeTest.cs
@@ -92,6 +92,84 @@ public void CanCreateEntityWithCompoundKey_ForDateAndTimeOfDay()
Assert.NotNull(entityType.DeclaredKey.SingleOrDefault(k => k.Name == "TimeOfDay"));
}
+ [Fact]
+ public void CanCreateEntityWithEnumKey()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ var enumEntityType = builder.EntityType();
+ enumEntityType.HasKey(c => c.Simple);
+ enumEntityType.Property(c => c.Id);
+
+ // Act
+ IEdmModel model = builder.GetServiceModel();
+
+ // Assert
+ IEdmEntityType entityType = model.SchemaElements.OfType()
+ .FirstOrDefault(c => c.Name == "EnumModel");
+ Assert.NotNull(entityType);
+ Assert.Equal(2, entityType.Properties().Count());
+
+ Assert.Equal(1, entityType.DeclaredKey.Count());
+ IEdmStructuralProperty key = entityType.DeclaredKey.First();
+ Assert.Equal(EdmTypeKind.Enum, key.Type.TypeKind());
+ Assert.Equal("Microsoft.TestCommon.Types.SimpleEnum", key.Type.Definition.FullTypeName());
+ }
+
+ [Fact]
+ public void CanCreateEntityWithCompoundEnumKeys()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ var enumEntityType = builder.EntityType();
+ enumEntityType.HasKey(c => new { c.Simple, c.Long });
+
+ // Act
+ IEdmModel model = builder.GetServiceModel();
+
+ // Assert
+ IEdmEntityType entityType = model.SchemaElements.OfType()
+ .FirstOrDefault(c => c.Name == "EnumModel");
+ Assert.NotNull(entityType);
+ Assert.Equal(2, entityType.Properties().Count());
+
+ Assert.Equal(2, entityType.DeclaredKey.Count());
+ IEdmStructuralProperty simpleKey = entityType.DeclaredKey.First(k => k.Name == "Simple");
+ Assert.Equal(EdmTypeKind.Enum, simpleKey.Type.TypeKind());
+ Assert.Equal("Microsoft.TestCommon.Types.SimpleEnum", simpleKey.Type.Definition.FullTypeName());
+
+ IEdmStructuralProperty longKey = entityType.DeclaredKey.First(k => k.Name == "Long");
+ Assert.Equal(EdmTypeKind.Enum, longKey.Type.TypeKind());
+ Assert.Equal("Microsoft.TestCommon.Types.LongEnum", longKey.Type.Definition.FullTypeName());
+ }
+
+ [Fact]
+ public void CanCreateEntityWithPrimitiveAndEnumKey()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ var enumEntityType = builder.EntityType();
+ enumEntityType.HasKey(c => new { c.Simple, c.Id });
+
+ // Act
+ IEdmModel model = builder.GetServiceModel();
+
+ // Assert
+ IEdmEntityType entityType = model.SchemaElements.OfType()
+ .FirstOrDefault(c => c.Name == "EnumModel");
+ Assert.NotNull(entityType);
+ Assert.Equal(2, entityType.Properties().Count());
+
+ Assert.Equal(2, entityType.DeclaredKey.Count());
+ IEdmStructuralProperty enumKey = entityType.DeclaredKey.First(k => k.Name == "Simple");
+ Assert.Equal(EdmTypeKind.Enum, enumKey.Type.TypeKind());
+ Assert.Equal("Microsoft.TestCommon.Types.SimpleEnum", enumKey.Type.Definition.FullTypeName());
+
+ IEdmStructuralProperty primitiveKey = entityType.DeclaredKey.First(k => k.Name == "Id");
+ Assert.Equal(EdmTypeKind.Primitive, primitiveKey.Type.TypeKind());
+ Assert.Equal("Edm.Int32", primitiveKey.Type.Definition.FullTypeName());
+ }
+
[Fact]
public void CanCreateEntityWithCollectionProperties()
{
@@ -522,6 +600,40 @@ public void RemoveKey_Removes_KeyProperty()
Assert.Empty(motorcycle.Keys);
}
+ [Fact]
+ public void RemoveEnumKey_ThrowsArgumentNull()
+ {
+ // Arrange
+ var builder = new ODataModelBuilder();
+ var motorcycle = builder.AddEntityType(typeof(Motorcycle));
+
+ // Act & Assert
+ Assert.ThrowsArgumentNull(
+ () => motorcycle.RemoveKey(enumKeyProperty: null),
+ "enumKeyProperty");
+ }
+
+ [Fact]
+ public void RemoveEnumKey_Removes_EnumKeyProperty()
+ {
+ // Arrange
+ ODataModelBuilder builder = new ODataModelBuilder();
+ var enumEntityType = builder.AddEntityType(typeof(EnumModel));
+ enumEntityType.HasKey(typeof(EnumModel).GetProperty("Simple"));
+
+ EnumPropertyConfiguration enumProperty =
+ enumEntityType.AddEnumProperty(typeof(EnumModel).GetProperty("Simple"));
+
+ Assert.Equal(new[] { enumProperty }, enumEntityType.EnumKeys); // Guard
+
+ // Act
+ enumEntityType.RemoveKey(enumProperty);
+
+ // Assert
+ Assert.Empty(enumEntityType.EnumKeys);
+ }
+
+
[Fact]
public void DynamicDictionaryProperty_Works_ToSetEntityTypeAsOpen()
{
diff --git a/OData/test/System.Web.OData.Test/OData/Builder/EnumTypeTest.cs b/OData/test/System.Web.OData.Test/OData/Builder/EnumTypeTest.cs
index 4213fa3c43..61c65b3e3e 100644
--- a/OData/test/System.Web.OData.Test/OData/Builder/EnumTypeTest.cs
+++ b/OData/test/System.Web.OData.Test/OData/Builder/EnumTypeTest.cs
@@ -150,6 +150,31 @@ public void CreateEnumTypePropertyInEntityType()
Assert.True(EdmCoreModel.Instance.GetInt64(false).Definition == longEnum.Type.AsEnum().EnumDefinition().UnderlyingType);
}
+ [Fact]
+ public void CreateEnumKeyInEntityType()
+ {
+ // Arrange
+ var builder = new ODataModelBuilder().Add_Color_EnumType().Add_LongEnum_EnumType();
+ var entityTypeConfiguration = builder.EntityType();
+ entityTypeConfiguration.HasKey(e => e.RequiredColor);
+
+ // Act
+ var model = builder.GetEdmModel();
+ var entityType = model.SchemaElements.OfType().Single();
+
+ // Assert
+ IEdmProperty requiredColorProperty = Assert.Single(entityType.Properties());
+ Assert.NotNull(requiredColorProperty);
+
+ IEdmStructuralProperty requiredColorKey = Assert.Single(entityType.DeclaredKey);
+ Assert.NotNull(requiredColorKey);
+
+ Assert.Same(requiredColorProperty, requiredColorKey);
+
+ Assert.Equal(EdmTypeKind.Enum, requiredColorKey.Type.TypeKind());
+ Assert.Equal("System.Web.OData.Builder.TestModels.Color", requiredColorKey.Type.Definition.FullTypeName());
+ }
+
[Fact]
public void AddAndRemoveEnumMemberFromEnumType()
{
diff --git a/OData/test/System.Web.OData.Test/OData/Formatter/ODataFormatterTests.cs b/OData/test/System.Web.OData.Test/OData/Formatter/ODataFormatterTests.cs
index 5483ffe3cb..2b5ec5225b 100644
--- a/OData/test/System.Web.OData.Test/OData/Formatter/ODataFormatterTests.cs
+++ b/OData/test/System.Web.OData.Test/OData/Formatter/ODataFormatterTests.cs
@@ -336,6 +336,37 @@ public void CustomSerializerWorks()
}
}
+ [Fact]
+ public void EnumKeySimpleSerializerTest()
+ {
+ // Arrange
+ ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
+ builder.EntitySet("EnumKeyCustomers");
+ builder.EntityType().HasKey(c => c.Color);
+ IEdmModel model = builder.GetEdmModel();
+ var controllers = new[] { typeof(EnumKeyCustomersController) };
+
+ HttpConfiguration configuration = controllers.GetHttpConfiguration();
+ configuration.MapODataServiceRoute("odata", routePrefix: null, model: model);
+ HttpServer host = new HttpServer(configuration);
+ HttpClient client = new HttpClient(host);
+
+ // Act
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get,
+ "http://localhost/EnumKeyCustomers(System.Web.OData.Builder.TestModels.Color'Red')");
+ HttpResponseMessage response = client.SendAsync(request).Result;
+
+ // Assert
+ Assert.True(response.IsSuccessStatusCode);
+ var customer = response.Content.ReadAsAsync().Result;
+ Assert.Equal(9, customer["ID"]);
+ Assert.Equal(Color.Red, Enum.Parse(typeof(Color), customer["Color"].ToString()));
+ var colors = customer["Colors"].Select(c => Enum.Parse(typeof(Color), c.ToString()));
+ Assert.Equal(2, colors.Count());
+ Assert.Contains(Color.Blue, colors);
+ Assert.Contains(Color.Red, colors);
+ }
+
[Fact]
public void EnumTypeRoundTripTest()
{
@@ -536,6 +567,21 @@ public IHttpActionResult GetColor(int key)
}
}
+ public class EnumKeyCustomersController : ODataController
+ {
+ public IHttpActionResult Get([FromODataUri]Color key)
+ {
+ EnumCustomer customer = new EnumCustomer
+ {
+ ID = 9,
+ Color = key,
+ Colors = new List { Color.Blue, Color.Red }
+ };
+
+ return Ok(customer);
+ }
+ }
+
public class CollectionSerializerCustomer
{
public int ID { get; set; }
diff --git a/OData/test/System.Web.OData.Test/OData/MetadataControllerTest.cs b/OData/test/System.Web.OData.Test/OData/MetadataControllerTest.cs
index 6c21cb43a0..90a6dee31d 100644
--- a/OData/test/System.Web.OData.Test/OData/MetadataControllerTest.cs
+++ b/OData/test/System.Web.OData.Test/OData/MetadataControllerTest.cs
@@ -9,11 +9,13 @@
using System.Net.Http.Headers;
using System.Web.Http;
using System.Web.Http.Tracing;
+using System.Web.OData.Builder.TestModels;
using System.Web.OData.Extensions;
using System.Web.OData.Formatter;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Library;
using Microsoft.TestCommon;
+using Microsoft.TestCommon.Types;
using Moq;
namespace System.Web.OData.Builder
@@ -579,6 +581,40 @@ public void DollarMetadata_Works_WithDerivedEntityTypeWithOwnKeys()
Assert.Contains(expectMetadata, payload);
}
+ [Fact]
+ public void DollarMetadata_Works_WithEntityTypeWithEnumKeys()
+ {
+ // Arrange
+ const string expectMetadata =
+ " \r\n" +
+ " \r\n" +
+ " \r\n" +
+ " \r\n" +
+ " \r\n" +
+ " \r\n" +
+ " ";
+
+ ODataModelBuilder builder = new ODataModelBuilder();
+ builder.EntityType().HasKey(e => e.Simple).Namespace = "NS";
+ builder.EnumType().Namespace = "NS";
+ IEdmModel model = builder.GetEdmModel();
+
+ var config = new[] { typeof(MetadataController) }.GetHttpConfiguration();
+ config.MapODataServiceRoute(model);
+ HttpServer server = new HttpServer(config);
+ HttpClient client = new HttpClient(server);
+
+ // Act
+ var response = client.GetAsync("http://localhost/$metadata").Result;
+
+ // Assert
+ Assert.True(response.IsSuccessStatusCode);
+ Assert.Equal("application/xml", response.Content.Headers.ContentType.MediaType);
+
+ string payload = response.Content.ReadAsStringAsync().Result;
+ Assert.Contains(expectMetadata, payload);
+ }
+
private static void AssertHasEntitySet(HttpClient client, string uri, string entitySetName)
{
var response = client.GetAsync(uri).Result;