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-12 21:07:04 +0400
committerraghuramn <ranadimi@microsoft.com>2012-09-18 04:26:54 +0400
commitf4c252a30e6853df22590db11895bcd9c2b458dc (patch)
tree2aa2c3fe9431c98381a33bba95b2d6aeacbc9364
parent81177687964a50ef0aba96f9c253bc62e884da61 (diff)
Adding inheritance support to ODataModelBuilder.
Users can now define abstract entity types and entity types that derive from another entity type. OData doesn't support complex type inheritance. This commit only adds support in the modelbuilder. Support for inheritance in the ODataConventionModelBuilder, ODataMediaTypeFormatter and Query support is still pending.
-rw-r--r--src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs5
-rw-r--r--src/System.Web.Http.OData/OData/Builder/Conventions/EntityKeyConvention.cs3
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EdmTypeBuilder.cs53
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.cs54
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs7
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs170
-rw-r--r--src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs68
-rw-r--r--src/System.Web.Http.OData/OData/Builder/IEntityTypeConfiguration.cs35
-rw-r--r--src/System.Web.Http.OData/OData/Builder/ODataModelBuilder.cs2
-rw-r--r--src/System.Web.Http.OData/Properties/SRResources.Designer.cs38
-rw-r--r--src/System.Web.Http.OData/Properties/SRResources.resx12
-rw-r--r--src/System.Web.Http.OData/System.Web.Http.OData.csproj1
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs5
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/EdmModelAsserts.cs (renamed from test/System.Web.Http.OData.Test/OData/Builder/Conventions/EdmModelAsserts.cs)11
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs40
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/EntityTypeTest.cs238
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/TestModels/InheritanceModels.cs43
-rw-r--r--test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj4
18 files changed, 753 insertions, 36 deletions
diff --git a/src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs b/src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs
index cbe6d8b3..408eb807 100644
--- a/src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs
+++ b/src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs
@@ -15,11 +15,6 @@ namespace System.Web.Http.OData.Builder.Conventions
{
private static HashSet<Type> _ignoredCollectionTypes = new HashSet<Type>(new Type[] { typeof(string) });
- public static bool IsEntityType(Type type)
- {
- return GetKeyProperty(type) != null;
- }
-
public static PropertyInfo GetKeyProperty(Type entityType, bool throwOnError = false)
{
IEnumerable<PropertyInfo> keys = entityType.GetProperties()
diff --git a/src/System.Web.Http.OData/OData/Builder/Conventions/EntityKeyConvention.cs b/src/System.Web.Http.OData/OData/Builder/Conventions/EntityKeyConvention.cs
index 2152f823..63ccf00e 100644
--- a/src/System.Web.Http.OData/OData/Builder/Conventions/EntityKeyConvention.cs
+++ b/src/System.Web.Http.OData/OData/Builder/Conventions/EntityKeyConvention.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.Linq;
using System.Reflection;
namespace System.Web.Http.OData.Builder.Conventions
@@ -14,7 +15,7 @@ namespace System.Web.Http.OData.Builder.Conventions
}
PropertyInfo key = ConventionsHelpers.GetKeyProperty(entity.ClrType);
- if (key != null)
+ if (key != null && !entity.IgnoredProperties.Contains(key))
{
entity.HasKey(key);
}
diff --git a/src/System.Web.Http.OData/OData/Builder/EdmTypeBuilder.cs b/src/System.Web.Http.OData/OData/Builder/EdmTypeBuilder.cs
index b3fee97b..563be29a 100644
--- a/src/System.Web.Http.OData/OData/Builder/EdmTypeBuilder.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EdmTypeBuilder.cs
@@ -1,6 +1,7 @@
// 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.Diagnostics.Contracts;
using System.Linq;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Properties;
@@ -26,11 +27,13 @@ namespace System.Web.Http.OData.Builder
{
// Reset
_types.Clear();
+
// Create headers to allow CreateEdmTypeBody to blindly references other things.
foreach (IStructuralTypeConfiguration config in _configurations)
{
- _types.Add(config.ClrType, CreateEdmTypeHeader(config));
+ CreateEdmTypeHeader(config);
}
+
foreach (IStructuralTypeConfiguration config in _configurations)
{
CreateEdmTypeBody(config);
@@ -42,6 +45,34 @@ namespace System.Web.Http.OData.Builder
}
}
+ private void CreateEdmTypeHeader(IStructuralTypeConfiguration config)
+ {
+ if (!_types.ContainsKey(config.ClrType))
+ {
+ if (config.Kind == EdmTypeKind.Complex)
+ {
+ _types.Add(config.ClrType, new EdmComplexType(config.Namespace, config.Name));
+ }
+ else
+ {
+ IEntityTypeConfiguration entity = config as IEntityTypeConfiguration;
+ Contract.Assert(entity != null);
+
+ IEdmEntityType baseType = null;
+
+ if (entity.BaseType != null)
+ {
+ CreateEdmTypeHeader(entity.BaseType);
+ baseType = _types[entity.BaseType.ClrType] as IEdmEntityType;
+
+ Contract.Assert(baseType != null);
+ }
+
+ _types.Add(config.ClrType, new EdmEntityType(config.Namespace, config.Name, baseType, entity.IsAbstract, isOpen: false));
+ }
+ }
+ }
+
private void CreateEdmTypeBody(IStructuralTypeConfiguration config)
{
IEdmType edmType = _types[config.ClrType];
@@ -62,7 +93,7 @@ namespace System.Web.Http.OData.Builder
private void CreateStructuralTypeBody(EdmStructuredType type, IStructuralTypeConfiguration config)
{
foreach (PropertyConfiguration property in config.Properties)
- {
+ {
switch (property.Kind)
{
case PropertyKind.Primitive:
@@ -88,11 +119,11 @@ namespace System.Web.Http.OData.Builder
if (_types.ContainsKey(collectionProperty.ElementType))
{
IEdmComplexType elementType = _types[collectionProperty.ElementType] as IEdmComplexType;
- elementTypeReference = new EdmComplexTypeReference(elementType, false);
+ elementTypeReference = new EdmComplexTypeReference(elementType, false);
}
- else
+ else
{
- elementTypeReference = EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(collectionProperty.ElementType);
+ elementTypeReference = EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(collectionProperty.ElementType);
}
type.AddStructuralProperty(
collectionProperty.PropertyInfo.Name,
@@ -161,17 +192,5 @@ namespace System.Web.Http.OData.Builder
return primitiveType.PrimitiveKind;
}
-
- private static IEdmStructuredType CreateEdmTypeHeader(IStructuralTypeConfiguration config)
- {
- if (config.Kind == EdmTypeKind.Complex)
- {
- return new EdmComplexType(config.Namespace, config.Name);
- }
- else
- {
- return new EdmEntityType(config.Namespace, config.Name);
- }
- }
}
}
diff --git a/src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.cs b/src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.cs
new file mode 100644
index 00000000..08773d60
--- /dev/null
+++ b/src/System.Web.Http.OData/OData/Builder/EdmTypeConfigurationExtensions.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.Collections.Generic;
+using System.Linq;
+
+namespace System.Web.Http.OData.Builder
+{
+ internal static class EdmTypeConfigurationExtensions
+ {
+ public static IEnumerable<PropertyConfiguration> DerivedProperties(this IEntityTypeConfiguration entity)
+ {
+ if (entity == null)
+ {
+ throw Error.ArgumentNull("entity");
+ }
+
+ IEntityTypeConfiguration baseType = entity.BaseType;
+
+ while (baseType != null)
+ {
+ foreach (PropertyConfiguration property in baseType.Properties)
+ {
+ yield return property;
+ }
+
+ baseType = baseType.BaseType;
+ }
+ }
+
+ public static IEnumerable<IEntityTypeConfiguration> DerivedTypes(this ODataModelBuilder modelBuilder, IEntityTypeConfiguration entity)
+ {
+ if (modelBuilder == null)
+ {
+ throw Error.ArgumentNull("modelBuilder");
+ }
+
+ if (entity == null)
+ {
+ throw Error.ArgumentNull("entity");
+ }
+
+ IEnumerable<IEntityTypeConfiguration> derivedEntities = modelBuilder.StructuralTypes.OfType<IEntityTypeConfiguration>().Where(e => e.BaseType == entity);
+
+ foreach (IEntityTypeConfiguration derivedEntity in derivedEntities)
+ {
+ yield return derivedEntity;
+ foreach (IEntityTypeConfiguration derivedDerivedEntity in modelBuilder.DerivedTypes(derivedEntity))
+ {
+ yield return derivedDerivedEntity;
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs b/src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs
index db2f8a7e..4eb8745d 100644
--- a/src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EntitySetConfigurationOfTEntityType.cs
@@ -16,9 +16,6 @@ namespace System.Web.Http.OData.Builder
public EntitySetConfiguration(ODataModelBuilder modelBuilder, string name)
: this(modelBuilder, new EntitySetConfiguration(modelBuilder, typeof(TEntityType), name))
{
- _configuration = new EntitySetConfiguration(modelBuilder, typeof(TEntityType), name);
- _entityType = new EntityTypeConfiguration<TEntityType>(_configuration.EntityType); // TBD: fix this
- _modelBuilder = modelBuilder;
}
public EntitySetConfiguration(ODataModelBuilder modelBuilder, IEntitySetConfiguration configuration)
@@ -35,7 +32,7 @@ namespace System.Web.Http.OData.Builder
_configuration = configuration;
_modelBuilder = modelBuilder;
- _entityType = new EntityTypeConfiguration<TEntityType>(_configuration.EntityType);
+ _entityType = new EntityTypeConfiguration<TEntityType>(modelBuilder, _configuration.EntityType);
}
public EntityTypeConfiguration<TEntityType> EntityType
@@ -166,7 +163,7 @@ namespace System.Web.Http.OData.Builder
_configuration.HasFeedSelfLink(feedSelfLinkFactory);
}
-
+
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public void HasEditLink(Func<EntityInstanceContext<TEntityType>, string> editLinkFactory)
{
diff --git a/src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs
index e37fed62..75b3abcc 100644
--- a/src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EntityTypeConfiguration.cs
@@ -8,28 +8,46 @@ using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
- // TODO: support inheritance
// TODO: add support for FK properties
// CUT: support for bi-directional properties
+
+ /// <summary>
+ /// Represents an <see cref="IEdmEntityType"/> that can be built using <see cref="ODataModelBuilder"/>.
+ /// </summary>
public class EntityTypeConfiguration : StructuralTypeConfiguration, IEntityTypeConfiguration
{
private List<PrimitivePropertyConfiguration> _keys = new List<PrimitivePropertyConfiguration>();
+ private IEntityTypeConfiguration _baseType;
+ /// <summary>
+ /// Initializes a new instance of <see cref="EntityTypeConfiguration"/>.
+ /// </summary>
+ /// <param name="modelBuilder">The <see cref="ODataModelBuilder"/> being used.</param>
+ /// <param name="clrType">The backing CLR type for this entity type.</param>
public EntityTypeConfiguration(ODataModelBuilder modelBuilder, Type clrType)
: base(modelBuilder, clrType)
{
}
+ /// <summary>
+ /// Gets the <see cref="EdmTypeKind"/> of this <see cref="IEdmTypeConfiguration"/>
+ /// </summary>
public override EdmTypeKind Kind
{
get { return EdmTypeKind.Entity; }
}
+ /// <summary>
+ /// Gets the collection of <see cref="NavigationPropertyConfiguration"/> of this entity type.
+ /// </summary>
public IEnumerable<NavigationPropertyConfiguration> NavigationProperties
{
get { return ExplicitProperties.Values.OfType<NavigationPropertyConfiguration>(); }
}
+ /// <summary>
+ /// Gets the collection of keys for this entity type.
+ /// </summary>
public IEnumerable<PrimitivePropertyConfiguration> Keys
{
get
@@ -38,13 +56,49 @@ namespace System.Web.Http.OData.Builder
}
}
+ /// <summary>
+ /// Gets or sets a value indicating whether this type is abstract.
+ /// </summary>
+ public bool IsAbstract { get; set; }
+
+ /// <summary>
+ /// Gets or sets the base type of this entity type.
+ /// </summary>
public IEntityTypeConfiguration BaseType
{
- get { return null; }
+ get
+ {
+ return _baseType;
+ }
+
+ set
+ {
+ DerivesFrom(value);
+ }
+ }
+
+ /// <summary>
+ /// Marks this entity type as abstract.
+ /// </summary>
+ /// <returns>Returns itself so that multiple calls can be chained.</returns>
+ public IEntityTypeConfiguration Abstract()
+ {
+ IsAbstract = true;
+ return this;
}
+ /// <summary>
+ /// Configures the key property(s) for this entity type.
+ /// </summary>
+ /// <param name="keyProperty">The property to be added to the key properties of this entity type.</param>
+ /// <returns>Returns itself so that multiple calls can be chained.</returns>
public IEntityTypeConfiguration HasKey(PropertyInfo keyProperty)
{
+ if (BaseType != null)
+ {
+ throw Error.InvalidOperation(SRResources.CannotDefineKeysOnDerivedTypes, FullName, BaseType.FullName);
+ }
+
PrimitivePropertyConfiguration propertyConfig = AddProperty(keyProperty);
// keys are always required
@@ -58,6 +112,88 @@ namespace System.Web.Http.OData.Builder
return this;
}
+ /// <summary>
+ /// Sets the base type of this entity type.
+ /// </summary>
+ /// <param name="baseType">The base entity type.</param>
+ /// <returns>Returns itself so that multiple calls can be chained.</returns>
+ public IEntityTypeConfiguration DerivesFrom(IEntityTypeConfiguration baseType)
+ {
+ if (baseType == null)
+ {
+ throw Error.ArgumentNull("baseType");
+ }
+
+ if (!baseType.ClrType.IsAssignableFrom(ClrType) || baseType.ClrType == ClrType)
+ {
+ throw Error.InvalidOperation(SRResources.TypeDoesNotInheritFromBaseType, ClrType.FullName, baseType.ClrType.FullName);
+ }
+
+ if (Keys.Any())
+ {
+ throw Error.InvalidOperation(SRResources.CannotDefineKeysOnDerivedTypes, FullName, baseType.FullName);
+ }
+
+ _baseType = baseType;
+
+ foreach (PropertyConfiguration property in Properties)
+ {
+ ValidatePropertyNotAlreadyDefinedInBaseTypes(property.PropertyInfo);
+ }
+
+ foreach (PropertyConfiguration property in this.DerivedProperties())
+ {
+ ValidatePropertyNotAlreadyDefinedInDerivedTypes(property.PropertyInfo);
+ }
+
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a new EDM primitive property to this entity type.
+ /// </summary>
+ /// <param name="propertyInfo">The backing CLR property.</param>
+ /// <returns>Returns the <see cref="PrimitivePropertyConfiguration"/> of the added property.</returns>
+ public override PrimitivePropertyConfiguration AddProperty(PropertyInfo propertyInfo)
+ {
+ ValidatePropertyNotAlreadyDefinedInBaseTypes(propertyInfo);
+ ValidatePropertyNotAlreadyDefinedInDerivedTypes(propertyInfo);
+
+ return base.AddProperty(propertyInfo);
+ }
+
+ /// <summary>
+ /// Adds a new EDM complex property to this entity type.
+ /// </summary>
+ /// <param name="propertyInfo">The backing CLR property.</param>
+ /// <returns>Returns the <see cref="ComplexPropertyConfiguration"/> of the added property.</returns>
+ public override ComplexPropertyConfiguration AddComplexProperty(PropertyInfo propertyInfo)
+ {
+ ValidatePropertyNotAlreadyDefinedInBaseTypes(propertyInfo);
+ ValidatePropertyNotAlreadyDefinedInDerivedTypes(propertyInfo);
+
+ return base.AddComplexProperty(propertyInfo);
+ }
+
+ /// <summary>
+ /// Adds a new EDM collection property to this entity type.
+ /// </summary>
+ /// <param name="propertyInfo">The backing CLR property.</param>
+ /// <returns>Returns the <see cref="CollectionPropertyConfiguration"/> of the added property.</returns>
+ public override CollectionPropertyConfiguration AddCollectionProperty(PropertyInfo propertyInfo)
+ {
+ ValidatePropertyNotAlreadyDefinedInBaseTypes(propertyInfo);
+ ValidatePropertyNotAlreadyDefinedInDerivedTypes(propertyInfo);
+
+ return base.AddCollectionProperty(propertyInfo);
+ }
+
+ /// <summary>
+ /// Adds a new EDM navigation property to this entity type.
+ /// </summary>
+ /// <param name="navigationProperty">The backing CLR property.</param>
+ /// <param name="multiplicity">The <see cref="EdmMultiplicity"/> of the navigation property.</param>
+ /// <returns>Returns the <see cref="NavigationPropertyConfiguration"/> of the added property.</returns>
public NavigationPropertyConfiguration AddNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity)
{
if (navigationProperty == null)
@@ -70,6 +206,9 @@ namespace System.Web.Http.OData.Builder
throw Error.InvalidOperation(SRResources.PropertyDoesNotBelongToType, navigationProperty.Name, ClrType.FullName);
}
+ ValidatePropertyNotAlreadyDefinedInBaseTypes(navigationProperty);
+ ValidatePropertyNotAlreadyDefinedInDerivedTypes(navigationProperty);
+
PropertyConfiguration propertyConfig;
NavigationPropertyConfiguration navigationPropertyConfig;
@@ -96,5 +235,32 @@ namespace System.Web.Http.OData.Builder
}
return navigationPropertyConfig;
}
+
+ public override void RemoveProperty(PropertyInfo propertyInfo)
+ {
+ base.RemoveProperty(propertyInfo);
+ _keys.RemoveAll(p => p.PropertyInfo == propertyInfo);
+ }
+
+ private void ValidatePropertyNotAlreadyDefinedInBaseTypes(PropertyInfo propertyInfo)
+ {
+ PropertyConfiguration baseProperty = this.DerivedProperties().Where(p => p.Name == propertyInfo.Name).FirstOrDefault();
+ if (baseProperty != null)
+ {
+ throw Error.InvalidOperation(SRResources.CannotRedefineBaseTypeProperty, propertyInfo.Name, baseProperty.PropertyInfo.ReflectedType.FullName);
+ }
+ }
+
+ private void ValidatePropertyNotAlreadyDefinedInDerivedTypes(PropertyInfo propertyInfo)
+ {
+ foreach (IEntityTypeConfiguration derivedEntity in ModelBuilder.DerivedTypes(this))
+ {
+ PropertyConfiguration propertyInDerivedType = derivedEntity.Properties.Where(p => p.Name == propertyInfo.Name).FirstOrDefault();
+ if (propertyInDerivedType != null)
+ {
+ throw Error.InvalidOperation(SRResources.PropertyAlreadyDefinedInDerivedType, propertyInfo.Name, FullName, derivedEntity.FullName);
+ }
+ }
+ }
}
}
diff --git a/src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs b/src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs
index d5eb4357..09552955 100644
--- a/src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs
+++ b/src/System.Web.Http.OData/OData/Builder/EntityTypeConfigurationOfTEntityType.cs
@@ -2,29 +2,46 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
+ /// <summary>
+ /// Represents an <see cref="IEdmEntityType"/> that can be built using <see cref="ODataModelBuilder"/>.
+ /// </summary>
+ /// <typeparam name="TEntityType">The backing CLR type for this <see cref="IEdmEntityType"/>.</typeparam>
public class EntityTypeConfiguration<TEntityType> : StructuralTypeConfiguration<TEntityType> where TEntityType : class
{
private IEntityTypeConfiguration _configuration;
private EntityCollectionConfiguration<TEntityType> _collection;
+ private ODataModelBuilder _modelBuilder;
+ /// <summary>
+ /// Initializes a new instance of <see cref="EntityTypeConfiguration"/>.
+ /// </summary>
+ /// <param name="modelBuilder">The <see cref="ODataModelBuilder"/> being used.</param>
public EntityTypeConfiguration(ODataModelBuilder modelBuilder)
- : this(new EntityTypeConfiguration(modelBuilder, typeof(TEntityType)))
+ : this(modelBuilder, new EntityTypeConfiguration(modelBuilder, typeof(TEntityType)))
{
}
- public EntityTypeConfiguration(IEntityTypeConfiguration configuration)
+ internal EntityTypeConfiguration(ODataModelBuilder modelBuilder, IEntityTypeConfiguration configuration)
: base(configuration)
{
+ Contract.Assert(modelBuilder != null);
+ Contract.Assert(configuration != null);
+
+ _modelBuilder = modelBuilder;
_configuration = configuration;
_collection = new EntityCollectionConfiguration<TEntityType>(configuration);
}
+ /// <summary>
+ /// Gets the collection of <see cref="NavigationPropertyConfiguration"/> of this entity type.
+ /// </summary>
public IEnumerable<NavigationPropertyConfiguration> NavigationProperties
{
get { return _configuration.NavigationProperties; }
@@ -39,6 +56,35 @@ namespace System.Web.Http.OData.Builder
get { return _collection; }
}
+ /// <summary>
+ /// Marks this entity type as abstract.
+ /// </summary>
+ /// <returns>Returns itself so that multiple calls can be chained.</returns>
+ public EntityTypeConfiguration<TEntityType> Abstract()
+ {
+ _configuration.IsAbstract = true;
+ return this;
+ }
+
+ /// <summary>
+ /// Sets the base type of this entity type.
+ /// </summary>
+ /// <typeparam name="TBaseType">The base entity type.</typeparam>
+ /// <returns>Returns itself so that multiple calls can be chained.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "typeof(TBaseType) is used and getting it as a generic argument is cleaner")]
+ public EntityTypeConfiguration<TEntityType> DerivesFrom<TBaseType>() where TBaseType : class
+ {
+ EntityTypeConfiguration<TBaseType> baseEntityType = _modelBuilder.Entity<TBaseType>();
+ _configuration.DerivesFrom(baseEntityType._configuration);
+ return this;
+ }
+
+ /// <summary>
+ /// Configures the key property(s) for this entity type.
+ /// </summary>
+ /// <typeparam name="TKey">The type of key.</typeparam>
+ /// <param name="keyDefinitionExpression">A lambda expression representing the property to be used as the primary key. For example, in C# t => t.Id and in Visual Basic .Net Function(t) t.Id.</param>
+ /// <returns>Returns itself so that multiple calls can be chained.</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 EntityTypeConfiguration<TEntityType> HasKey<TKey>(Expression<Func<TEntityType, TKey>> keyDefinitionExpression)
@@ -51,6 +97,12 @@ namespace System.Web.Http.OData.Builder
return this;
}
+ /// <summary>
+ /// Configures a many relationship from this entity type.
+ /// </summary>
+ /// <typeparam name="TTargetEntity">The type of the entity at the other end of the relationship.</typeparam>
+ /// <param name="navigationPropertyExpression">A lambda expression representing the navigation property for the relationship. For example, in C# t => t.MyProperty and in Visual Basic .Net Function(t) t.MyProperty.</param>
+ /// <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
@@ -58,6 +110,12 @@ namespace System.Web.Http.OData.Builder
return GetOrCreateNavigationProperty(navigationPropertyExpression, EdmMultiplicity.Many);
}
+ /// <summary>
+ /// Configures an optional relationship from this entity type.
+ /// </summary>
+ /// <typeparam name="TTargetEntity">The type of the entity at the other end of the relationship.</typeparam>
+ /// <param name="navigationPropertyExpression">A lambda expression representing the navigation property for the relationship. For example, in C# t => t.MyProperty and in Visual Basic .Net Function(t) t.MyProperty.</param>
+ /// <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 HasOptional<TTargetEntity>(Expression<Func<TEntityType, TTargetEntity>> navigationPropertyExpression) where TTargetEntity : class
@@ -65,6 +123,12 @@ namespace System.Web.Http.OData.Builder
return GetOrCreateNavigationProperty(navigationPropertyExpression, EdmMultiplicity.ZeroOrOne);
}
+ /// <summary>
+ /// Configures a required relationship from this entity type.
+ /// </summary>
+ /// <typeparam name="TTargetEntity">The type of the entity at the other end of the relationship.</typeparam>
+ /// <param name="navigationPropertyExpression">A lambda expression representing the navigation property for the relationship. For example, in C# t => t.MyProperty and in Visual Basic .Net Function(t) t.MyProperty.</param>
+ /// <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 HasRequired<TTargetEntity>(Expression<Func<TEntityType, TTargetEntity>> navigationPropertyExpression) where TTargetEntity : class
diff --git a/src/System.Web.Http.OData/OData/Builder/IEntityTypeConfiguration.cs b/src/System.Web.Http.OData/OData/Builder/IEntityTypeConfiguration.cs
index c27a19be..ece3d8c5 100644
--- a/src/System.Web.Http.OData/OData/Builder/IEntityTypeConfiguration.cs
+++ b/src/System.Web.Http.OData/OData/Builder/IEntityTypeConfiguration.cs
@@ -6,16 +6,51 @@ using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
+ /// <summary>
+ /// Represents an <see cref="IEdmEntityType"/> that can be built using <see cref="ODataModelBuilder"/>.
+ /// </summary>
public interface IEntityTypeConfiguration : IStructuralTypeConfiguration
{
+ /// <summary>
+ /// Gets or sets a value indicating whether this type is abstract.
+ /// </summary>
+ bool IsAbstract { get; set; }
+
+ /// <summary>
+ /// Gets the base type of this entity type.
+ /// </summary>
IEntityTypeConfiguration BaseType { get; }
+ /// <summary>
+ /// Gets the key properties of this entity type.
+ /// </summary>
IEnumerable<PrimitivePropertyConfiguration> Keys { get; }
+ /// <summary>
+ /// Gets the navigation properties of this entity type.
+ /// </summary>
IEnumerable<NavigationPropertyConfiguration> NavigationProperties { get; }
+ /// <summary>
+ /// Configures the key property(s) for this entity type.
+ /// </summary>
+ /// <param name="keyProperty">The property to be added to the key properties of this entity type.</param>
+ /// <returns>Returns itself so that multiple calls can be chained.</returns>
IEntityTypeConfiguration HasKey(PropertyInfo keyProperty);
+ /// <summary>
+ /// Adds a new navigation property to this entity type.
+ /// </summary>
+ /// <param name="navigationProperty">The property to be added as a navigation property.</param>
+ /// <param name="multiplicity">The <see cref="EdmMultiplicity"/> of the navigation property.</param>
+ /// <returns>The <see cref="NavigationPropertyConfiguration"/> of the added property.</returns>
NavigationPropertyConfiguration AddNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity);
+
+ /// <summary>
+ /// Sets the base type of this entity type.
+ /// </summary>
+ /// <param name="baseType">The base entity type</param>
+ /// <returns>Returns itself so that multiple calls can be chained.</returns>
+ IEntityTypeConfiguration DerivesFrom(IEntityTypeConfiguration baseType);
}
}
diff --git a/src/System.Web.Http.OData/OData/Builder/ODataModelBuilder.cs b/src/System.Web.Http.OData/OData/Builder/ODataModelBuilder.cs
index 2343b5dd..b61d50aa 100644
--- a/src/System.Web.Http.OData/OData/Builder/ODataModelBuilder.cs
+++ b/src/System.Web.Http.OData/OData/Builder/ODataModelBuilder.cs
@@ -68,7 +68,7 @@ namespace System.Web.Http.OData.Builder
/// <returns>The configuration object for the specified entity type.</returns>
public EntityTypeConfiguration<TEntityType> Entity<TEntityType>() where TEntityType : class
{
- return new EntityTypeConfiguration<TEntityType>(AddEntity(typeof(TEntityType)));
+ return new EntityTypeConfiguration<TEntityType>(this, AddEntity(typeof(TEntityType)));
}
/// <summary>
diff --git a/src/System.Web.Http.OData/Properties/SRResources.Designer.cs b/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
index c3e6e3d9..812bf288 100644
--- a/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
+++ b/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.269
+// Runtime Version:4.0.30319.18003
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -124,6 +124,15 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to Cannot define keys on type &apos;{0}&apos; deriving from &apos;{1}&apos;. Only the root type in the entity inheritance hierarchy can contain keys..
+ /// </summary>
+ internal static string CannotDefineKeysOnDerivedTypes {
+ get {
+ return ResourceManager.GetString("CannotDefineKeysOnDerivedTypes", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Failed to convert &apos;{0}&apos; to an integer..
/// </summary>
internal static string CanNotParseInteger {
@@ -151,6 +160,15 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to Cannot redefine property &apos;{0}&apos; already defined on the base type &apos;{1}&apos;..
+ /// </summary>
+ internal static string CannotRedefineBaseTypeProperty {
+ get {
+ return ResourceManager.GetString("CannotRedefineBaseTypeProperty", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Cannot serialize a null &apos;{0}&apos;..
/// </summary>
internal static string CannotSerializerNull {
@@ -664,6 +682,15 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to Cannot define property &apos;{0}&apos; in the base entity type &apos;{1}&apos; as the derived type &apos;{2}&apos; already defines it..
+ /// </summary>
+ internal static string PropertyAlreadyDefinedInDerivedType {
+ get {
+ return ResourceManager.GetString("PropertyAlreadyDefinedInDerivedType", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The property &apos;{0}&apos; does not belong to the type &apos;{1}&apos;..
/// </summary>
internal static string PropertyDoesNotBelongToType {
@@ -808,6 +835,15 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to &apos;{0}&apos; does not inherit from &apos;{1}&apos;..
+ /// </summary>
+ internal static string TypeDoesNotInheritFromBaseType {
+ get {
+ return ResourceManager.GetString("TypeDoesNotInheritFromBaseType", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to {0} is not a collection of type {1}. The ODataFeedSerializer can serialize only entity collections..
/// </summary>
internal static string TypeMustBeEntityCollection {
diff --git a/src/System.Web.Http.OData/Properties/SRResources.resx b/src/System.Web.Http.OData/Properties/SRResources.resx
index cd3086a7..9b6441ea 100644
--- a/src/System.Web.Http.OData/Properties/SRResources.resx
+++ b/src/System.Web.Http.OData/Properties/SRResources.resx
@@ -384,4 +384,16 @@
<data name="UnsupportedEdmTypeKind" xml:space="preserve">
<value>Found unsupported EdmTypeKind '{0}' in list of available types.</value>
</data>
+ <data name="CannotDefineKeysOnDerivedTypes" xml:space="preserve">
+ <value>Cannot define keys on type '{0}' deriving from '{1}'. Only the root type in the entity inheritance hierarchy can contain keys.</value>
+ </data>
+ <data name="CannotRedefineBaseTypeProperty" xml:space="preserve">
+ <value>Cannot redefine property '{0}' already defined on the base type '{1}'.</value>
+ </data>
+ <data name="TypeDoesNotInheritFromBaseType" xml:space="preserve">
+ <value>'{0}' does not inherit from '{1}'.</value>
+ </data>
+ <data name="PropertyAlreadyDefinedInDerivedType" xml:space="preserve">
+ <value>Cannot define property '{0}' in the base entity type '{1}' as the derived type '{2}' already defines it.</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 e5af5c84..ed13e6ef 100644
--- a/src/System.Web.Http.OData/System.Web.Http.OData.csproj
+++ b/src/System.Web.Http.OData/System.Web.Http.OData.csproj
@@ -129,6 +129,7 @@
<Compile Include="OData\Builder\Conventions\SelfLinksGenerationConvention.cs" />
<Compile Include="OData\Builder\EdmTypeBuilder.cs" />
<Compile Include="OData\Builder\EntityCollectionConfigurationOfTEntityType.cs" />
+ <Compile Include="OData\Builder\EdmTypeConfigurationExtensions.cs" />
<Compile Include="OData\Builder\EntitySetConfiguration.cs" />
<Compile Include="OData\Builder\EntitySetConfigurationOfTEntityType.cs" />
<Compile Include="OData\Builder\EntityTypeConfiguration.cs" />
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs
index d3615ffc..a16ac788 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/EntityKeyConventionTests.cs
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+using System.Linq;
+using System.Reflection;
using Microsoft.TestCommon;
using Moq;
@@ -15,6 +17,9 @@ namespace System.Web.Http.OData.Builder.Conventions
mockEntityType
.Setup(edmType => edmType.ClrType)
.Returns(typeof(EntityKeyConventionTests_EntityType));
+ mockEntityType
+ .Setup(edmType => edmType.IgnoredProperties)
+ .Returns(Enumerable.Empty<PropertyInfo>());
mockEntityType.Setup(entityType => entityType.HasKey(typeof(EntityKeyConventionTests_EntityType).GetProperty("ID"))).Returns(mockEntityType.Object);
var mockModelBuilder = new Mock<ODataModelBuilder>(MockBehavior.Strict);
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/EdmModelAsserts.cs b/test/System.Web.Http.OData.Test/OData/Builder/EdmModelAsserts.cs
index fd83e698..cb2ebad9 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/EdmModelAsserts.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/EdmModelAsserts.cs
@@ -7,7 +7,7 @@ using Microsoft.Data.Edm;
using Microsoft.Data.Edm.Library;
using Microsoft.TestCommon;
-namespace System.Web.Http.OData.Builder.Conventions
+namespace System.Web.Http.OData.Builder
{
public static class EdmModelAsserts
{
@@ -27,6 +27,15 @@ namespace System.Web.Http.OData.Builder.Conventions
return entityType;
}
+ public static IEdmEntityType AssertHasEntityType(this IEdmModel model, Type mappedEntityClrType, Type mappedEntityBaseType)
+ {
+ IEdmEntityType entity = AssertHasEntityType(model, mappedEntityClrType);
+ IEdmEntityType baseEntity = AssertHasEntityType(model, mappedEntityBaseType);
+
+ Assert.Equal(baseEntity, entity.BaseEntityType());
+ return entity;
+ }
+
public static IEdmEntityType AssertHasEntityType(this IEdmModel model, Type mappedEntityClrType)
{
string entityTypeName = mappedEntityClrType.FullName;
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs b/test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs
new file mode 100644
index 00000000..031036a9
--- /dev/null
+++ b/test/System.Web.Http.OData.Test/OData/Builder/EdmTypeConfigurationExtensionsTest.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Reflection;
+using Microsoft.TestCommon;
+using Moq;
+
+namespace System.Web.Http.OData.Builder
+{
+ public class EdmTypeConfigurationExtensionsTest
+ {
+ [Fact]
+ public void DerivedProperties_ReturnsAllDerivedProperties()
+ {
+ Mock<IEntityTypeConfiguration> entityA = new Mock<IEntityTypeConfiguration>();
+ entityA.Setup(e => e.Properties).Returns(new[] { MockProperty("A1"), MockProperty("A2") });
+
+ Mock<IEntityTypeConfiguration> entityB = new Mock<IEntityTypeConfiguration>();
+ entityB.Setup(e => e.Properties).Returns(new[] { MockProperty("B1"), MockProperty("B2") });
+ 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.BaseType).Returns(entityB.Object);
+
+ Assert.Equal(
+ new[] { "A1", "A2", "B1", "B2" },
+ entityC.Object.DerivedProperties().Select(p => p.Name).OrderBy(s => s));
+ }
+
+ private static PropertyConfiguration MockProperty(string name)
+ {
+ Mock<PropertyInfo> propertyInfo = new Mock<PropertyInfo>();
+ propertyInfo.Setup(p => p.Name).Returns(name);
+
+ Mock<PropertyConfiguration> property = new Mock<PropertyConfiguration>(propertyInfo.Object);
+ return property.Object;
+ }
+ }
+}
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/EntityTypeTest.cs b/test/System.Web.Http.OData.Test/OData/Builder/EntityTypeTest.cs
index 8dc8da4e..b5f6b9b8 100644
--- a/test/System.Web.Http.OData.Test/OData/Builder/EntityTypeTest.cs
+++ b/test/System.Web.Http.OData.Test/OData/Builder/EntityTypeTest.cs
@@ -98,5 +98,243 @@ namespace System.Web.Http.OData.Builder
var edmCustomerType = model.FindType(typeof(Customer).FullName) as IEdmEntityType;
var edmAddressType = model.FindType(typeof(Address).FullName) as IEdmComplexType;
}
+
+ [Fact]
+ public void CanCreateAbstractEntityType()
+ {
+ var builder = new ODataModelBuilder();
+ builder
+ .Entity<Vehicle>()
+ .Abstract();
+
+ var model = builder.GetEdmModel();
+ var edmCustomerType = model.FindType(typeof(Vehicle).FullName) as IEdmEntityType;
+ Assert.True(edmCustomerType != null);
+ Assert.True(edmCustomerType.IsAbstract);
+ }
+
+ [Fact]
+ public void CanCreateDerivedtypes()
+ {
+ var builder = new ODataModelBuilder();
+
+ builder
+ .Entity<Vehicle>()
+ .Abstract()
+ .HasKey(v => v.Model)
+ .HasKey(v => v.Name)
+ .Property(v => v.WheelCount);
+
+ builder
+ .Entity<Motorcycle>()
+ .DerivesFrom<Vehicle>()
+ .Property(m => m.CanDoAWheelie);
+
+ builder
+ .Entity<Car>()
+ .DerivesFrom<Vehicle>()
+ .Property(c => c.SeatingCapacity);
+
+ var model = builder.GetEdmModel();
+
+ var edmVehicle = model.AssertHasEntityType(typeof(Vehicle));
+ Assert.Null(edmVehicle.BaseEntityType());
+ Assert.Equal(2, edmVehicle.Key().Count());
+ edmVehicle.AssertHasKey(model, "Model", EdmPrimitiveTypeKind.Int32);
+ edmVehicle.AssertHasKey(model, "Name", EdmPrimitiveTypeKind.String);
+ Assert.Equal(3, edmVehicle.Properties().Count());
+ edmVehicle.AssertHasPrimitiveProperty(model, "WheelCount", EdmPrimitiveTypeKind.Int32, isNullable: false);
+
+ var edmMotorcycle = model.AssertHasEntityType(typeof(Motorcycle));
+ Assert.Equal(edmVehicle, edmMotorcycle.BaseEntityType());
+ Assert.Equal(2, edmMotorcycle.Key().Count());
+ edmMotorcycle.AssertHasKey(model, "Model", EdmPrimitiveTypeKind.Int32);
+ edmMotorcycle.AssertHasKey(model, "Name", EdmPrimitiveTypeKind.String);
+ Assert.Equal(4, edmMotorcycle.Properties().Count());
+ edmMotorcycle.AssertHasPrimitiveProperty(model, "WheelCount", EdmPrimitiveTypeKind.Int32, isNullable: false);
+ edmMotorcycle.AssertHasPrimitiveProperty(model, "CanDoAWheelie", EdmPrimitiveTypeKind.Boolean, isNullable: false);
+
+ var edmCar = model.AssertHasEntityType(typeof(Car));
+ Assert.Equal(edmVehicle, edmMotorcycle.BaseEntityType());
+ Assert.Equal(2, edmCar.Key().Count());
+ edmCar.AssertHasKey(model, "Model", EdmPrimitiveTypeKind.Int32);
+ edmCar.AssertHasKey(model, "Name", EdmPrimitiveTypeKind.String);
+ Assert.Equal(4, edmCar.Properties().Count());
+ edmCar.AssertHasPrimitiveProperty(model, "WheelCount", EdmPrimitiveTypeKind.Int32, isNullable: false);
+ edmCar.AssertHasPrimitiveProperty(model, "SeatingCapacity", EdmPrimitiveTypeKind.Int32, isNullable: false);
+ }
+
+ [Fact]
+ public void CanCreateDerivedTypesInAnyOrder()
+ {
+ var builder = new ODataModelBuilder();
+
+ builder
+ .Entity<Cruiser>()
+ .DerivesFrom<Motorcycle>();
+
+ builder
+ .Entity<Car>()
+ .DerivesFrom<Vehicle>();
+
+ builder
+ .Entity<Vehicle>();
+
+ builder
+ .Entity<Motorcycle>()
+ .DerivesFrom<Vehicle>();
+
+ IEdmModel model = builder.GetEdmModel();
+
+ model.AssertHasEntityType(typeof(Vehicle));
+ model.AssertHasEntityType(typeof(Car), typeof(Vehicle));
+ model.AssertHasEntityType(typeof(Motorcycle), typeof(Vehicle));
+ model.AssertHasEntityType(typeof(Cruiser), typeof(Motorcycle));
+ }
+
+ [Fact]
+ public void HasKeyOnDerivedTypes_Throws()
+ {
+ var builder = new ODataModelBuilder();
+
+ Assert.Throws<InvalidOperationException>(
+ () => builder
+ .Entity<Motorcycle>()
+ .DerivesFrom<Vehicle>()
+ .HasKey(m => m.ID),
+ "Cannot define keys on type 'System.Web.Http.OData.Builder.TestModels.Motorcycle' deriving from 'System.Web.Http.OData.Builder.TestModels.Vehicle'. Only the root type in the entity inheritance hierarchy can contain keys.");
+ }
+
+ [Fact]
+ public void CanDefinePropertyOnDerivedType_NotPresentInBaseEdmType_ButPresentInBaseClrType()
+ {
+ var builder = new ODataModelBuilder();
+
+ builder
+ .Entity<Motorcycle>()
+ .DerivesFrom<Vehicle>()
+ .Property(m => m.Model);
+
+ var model = builder.GetEdmModel();
+
+ var edmVehicle = model.AssertHasEntityType(typeof(Vehicle));
+ Assert.Null(edmVehicle.BaseEntityType());
+ Assert.Equal(0, edmVehicle.Properties().Count());
+
+ var edmMotorcycle = model.AssertHasEntityType(typeof(Motorcycle));
+ Assert.Equal(edmVehicle, edmMotorcycle.BaseEntityType());
+ Assert.Equal(1, edmMotorcycle.Properties().Count());
+ edmMotorcycle.AssertHasPrimitiveProperty(model, "Model", EdmPrimitiveTypeKind.Int32, isNullable: false);
+ }
+
+ [Fact]
+ public void RedefiningBaseTypeProperty_Throws()
+ {
+ var builder = new ODataModelBuilder();
+
+ builder
+ .Entity<Vehicle>()
+ .Property(v => v.WheelCount);
+
+ Assert.Throws<InvalidOperationException>(
+ () => builder
+ .Entity<Motorcycle>()
+ .DerivesFrom<Vehicle>()
+ .Property(m => m.WheelCount),
+ "Cannot redefine property 'WheelCount' already defined on the base type 'System.Web.Http.OData.Builder.TestModels.Vehicle'.");
+ }
+
+ [Fact]
+ public void DefiningPropertyOnBaseTypeAlreadyPresentInDerivedType_Throws()
+ {
+ var builder = new ODataModelBuilder();
+
+ builder
+ .Entity<Motorcycle>()
+ .DerivesFrom<Vehicle>()
+ .Property(m => m.Model);
+
+ Assert.Throws<InvalidOperationException>(
+ () => builder
+ .Entity<Vehicle>()
+ .Property(v => v.Model),
+ "Cannot define property 'Model' in the base entity type 'System.Web.Http.OData.Builder.TestModels.Vehicle' as the derived type 'System.Web.Http.OData.Builder.TestModels.Motorcycle' already defines it.");
+ }
+
+ [Fact]
+ public void DerivesFrom_Throws_IfDerivedTypeHasKeys()
+ {
+ var builder = new ODataModelBuilder();
+
+ Assert.Throws<InvalidOperationException>(
+ () => builder
+ .Entity<Motorcycle>()
+ .HasKey(m => m.Model)
+ .DerivesFrom<Vehicle>(),
+ "Cannot define keys on type 'System.Web.Http.OData.Builder.TestModels.Motorcycle' deriving from 'System.Web.Http.OData.Builder.TestModels.Vehicle'. Only the root type in the entity inheritance hierarchy can contain keys.");
+ }
+
+ [Fact]
+ public void DerivesFrom_Throws_IfDerivedTypeDoesntDeriveFromBaseType()
+ {
+ var builder = new ODataModelBuilder();
+
+ Assert.Throws<InvalidOperationException>(
+ () => builder
+ .Entity<string>()
+ .DerivesFrom<Vehicle>(),
+ "'System.String' does not inherit from 'System.Web.Http.OData.Builder.TestModels.Vehicle'.");
+ }
+
+ [Fact]
+ public void DerivesFrom_Throws_WhenSettingTheBaseType_IfDuplicatePropertyInBaseType()
+ {
+ var builder = new ODataModelBuilder();
+
+ builder
+ .Entity<Vehicle>()
+ .Property(v => v.Model);
+
+ var motorcycle = builder
+ .Entity<Motorcycle>();
+ motorcycle.Property(m => m.Model);
+
+ Assert.Throws<InvalidOperationException>(
+ () => motorcycle.DerivesFrom<Vehicle>(),
+ "Cannot redefine property 'Model' already defined on the base type 'System.Web.Http.OData.Builder.TestModels.Vehicle'.");
+ }
+
+ [Fact]
+ public void DerivesFrom_Throws_WhenSettingTheBaseType_IfDuplicatePropertyInDerivedType()
+ {
+ var builder = new ODataModelBuilder();
+
+ builder
+ .Entity<Vehicle>()
+ .Property(v => v.Model);
+
+ builder
+ .Entity<Cruiser>()
+ .DerivesFrom<Motorcycle>()
+ .Property(c => c.Model);
+
+ Assert.Throws<InvalidOperationException>(
+ () => builder
+ .Entity<Motorcycle>()
+ .DerivesFrom<Vehicle>(),
+ "Cannot define property 'Model' in the base entity type 'System.Web.Http.OData.Builder.TestModels.Motorcycle' as the derived type 'System.Web.Http.OData.Builder.TestModels.Cruiser' already defines it.");
+ }
+
+ [Fact]
+ public void CannotDeriveFromItself()
+ {
+ var builder = new ODataModelBuilder();
+
+ Assert.Throws<InvalidOperationException>(
+ () => builder
+ .Entity<Vehicle>()
+ .DerivesFrom<Vehicle>(),
+ "'System.Web.Http.OData.Builder.TestModels.Vehicle' does not inherit from 'System.Web.Http.OData.Builder.TestModels.Vehicle'.");
+ }
}
}
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
new file mode 100644
index 00000000..bc4a2f8e
--- /dev/null
+++ b/test/System.Web.Http.OData.Test/OData/Builder/TestModels/InheritanceModels.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System.ComponentModel.DataAnnotations;
+
+namespace System.Web.Http.OData.Builder.TestModels
+{
+ public abstract class Vehicle
+ {
+ [Key]
+ public int Model { get; set; }
+
+ [Key]
+ public string Name { get; set; }
+
+ public abstract int WheelCount { get; }
+ }
+
+ public class Car : Vehicle
+ {
+ public override int WheelCount
+ {
+ get { return 4; }
+ }
+
+ public int SeatingCapacity { get; set; }
+ }
+
+ public class Motorcycle : Vehicle
+ {
+ public override int WheelCount
+ {
+ get { return 2; }
+ }
+
+ public bool CanDoAWheelie { get; set; }
+
+ public int ID { get; set; }
+ }
+
+ public class Cruiser : 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 bb939a74..abdc0321 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
@@ -107,6 +107,8 @@
<Compile Include="OData\Builder\ParameterConfigurationTest.cs" />
<Compile Include="OData\Builder\CollectionPropertyConfigurationTest.cs" />
<Compile Include="OData\Formatter\PartialTrustTest.cs" />
+ <Compile Include="OData\Builder\EdmTypeConfigurationExtensionsTest.cs" />
+ <Compile Include="OData\Builder\TestModels\InheritanceModels.cs" />
<Compile Include="OData\Formatter\Deserialization\ODataCollectionDeserializerTests.cs" />
<Compile Include="OData\Formatter\Deserialization\ODataEntryDeserializerOfTItemTests.cs" />
<Compile Include="OData\Builder\Conventions\SelfLinksGenerationConventionTest.cs" />
@@ -126,7 +128,7 @@
<Compile Include="OData\Builder\Conventions\Attributes\AttributeConventionTests.cs" />
<Compile Include="OData\Builder\Conventions\Attributes\AttributeEdmPropertyConventionTests.cs" />
<Compile Include="OData\Builder\Conventions\Attributes\NotMappedAttributeConventionTests.cs" />
- <Compile Include="OData\Builder\Conventions\EdmModelAsserts.cs" />
+ <Compile Include="OData\Builder\EdmModelAsserts.cs" />
<Compile Include="OData\Query\HandleNullPropagationOptionHelperTest.cs" />
<Compile Include="OData\Query\ODataQuerySettingsTest.cs" />
<Compile Include="OData\Query\OrderByPropertyNodeTest.cs" />