Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 71d4ab3

Browse filesBrowse files
Chris Martinezcommonsensesoftware
authored andcommitted
Add ad hoc annotation and resolve annotations via extension method
1 parent 756f21b commit 71d4ab3
Copy full SHA for 71d4ab3

File tree

Expand file treeCollapse file tree

13 files changed

+113
-44
lines changed
Filter options
Expand file treeCollapse file tree

13 files changed

+113
-44
lines changed

‎src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs

Copy file name to clipboardExpand all lines: src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs
+1-5Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,12 @@ public EdmModelSelector( IEnumerable<IEdmModel> models, IApiVersionSelector apiV
130130

131131
private static void AddVersionFromModel( IEdmModel model, IList<ApiVersion> versions, IDictionary<ApiVersion, IEdmModel> collection )
132132
{
133-
var annotation = model.GetAnnotationValue<ApiVersionAnnotation>( model );
134-
135-
if ( annotation == null )
133+
if ( model.GetApiVersion() is not ApiVersion version )
136134
{
137135
var message = string.Format( CultureInfo.CurrentCulture, SR.MissingAnnotation, typeof( ApiVersionAnnotation ).Name );
138136
throw new ArgumentException( message );
139137
}
140138

141-
var version = annotation.ApiVersion;
142-
143139
collection.Add( version, model );
144140
versions.Add( version );
145141
}

‎src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs

Copy file name to clipboardExpand all lines: src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void get_edm_models_should_return_expected_results()
3434
var model = builder.GetEdmModels().Single();
3535

3636
// assert
37-
model.GetAnnotationValue<ApiVersionAnnotation>( model ).ApiVersion.Should().Be( apiVersion );
37+
model.GetApiVersion().Should().Be( apiVersion );
3838
modelCreated.Verify( f => f( It.IsAny<ODataModelBuilder>(), model ), Times.Once() );
3939
}
4040

‎src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs

Copy file name to clipboardExpand all lines: src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ private static bool TryMatchModelVersion(
221221
for ( var i = 0; i < items.Count; i++ )
222222
{
223223
var item = items[i];
224-
var otherApiVersion = item.Model.GetAnnotationValue<ApiVersionAnnotation>( item.Model )?.ApiVersion;
224+
var otherApiVersion = item.Model.GetApiVersion();
225225

226226
if ( apiVersion.Equals( otherApiVersion ) )
227227
{

‎src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs

Copy file name to clipboardExpand all lines: src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public partial class ODataQueryOptionDescriptionContext
3434
foreach ( var item in items )
3535
{
3636
var model = item.Model;
37-
var otherVersion = model.GetAnnotationValue<ApiVersionAnnotation>( model )?.ApiVersion;
37+
var otherVersion = model.GetApiVersion();
3838

3939
if ( version.Equals( otherVersion ) )
4040
{

‎src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs

Copy file name to clipboardExpand all lines: src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs
+5-1Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,11 @@ private void AddRouteComponents(
152152
for ( var i = 0; i < models.Count; i++ )
153153
{
154154
var model = models[i];
155-
var version = model.GetAnnotationValue<ApiVersionAnnotation>( model ).ApiVersion;
155+
156+
if ( model.GetApiVersion() is not ApiVersion version )
157+
{
158+
continue;
159+
}
156160

157161
if ( !mappings.TryGetValue( version, out var options ) )
158162
{

‎src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs

Copy file name to clipboardExpand all lines: src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public sealed class VersionedODataTemplateTranslator : IODataTemplateTranslator
3939
else
4040
{
4141
var model = context.Model;
42-
var otherApiVersion = model.GetAnnotationValue<ApiVersionAnnotation>( model )?.ApiVersion;
42+
var otherApiVersion = model.GetApiVersion();
4343

4444
// HACK: a version-neutral endpoint can fail to match here because odata tries to match the
4545
// first endpoint metadata when there could be multiple. such an endpoint is expected to be

‎src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs

Copy file name to clipboardExpand all lines: src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public override bool AppliesToAction( ODataControllerActionContext context )
5555
return false;
5656
}
5757

58-
var apiVersion = edm.GetAnnotationValue<ApiVersionAnnotation>( edm )?.ApiVersion;
58+
var apiVersion = edm.GetApiVersion();
5959

6060
if ( apiVersion == null || !metadata.IsMappedTo( apiVersion ) )
6161
{

‎src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs

Copy file name to clipboardExpand all lines: src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void get_edm_models_should_return_expected_results()
2929
var model = builder.GetEdmModels().Single();
3030

3131
// assert
32-
model.GetAnnotationValue<ApiVersionAnnotation>( model ).ApiVersion.Should().Be( apiVersion );
32+
model.GetApiVersion().Should().Be( apiVersion );
3333
modelCreated.Verify( f => f( It.IsAny<ODataModelBuilder>(), model ), Once() );
3434
}
3535

‎src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs

Copy file name to clipboardExpand all lines: src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs
+42-13Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,19 @@ namespace Asp.Versioning.OData;
2525
public sealed class DefaultModelTypeBuilder : IModelTypeBuilder
2626
{
2727
private static Type? ienumerableOfT;
28+
private readonly bool adHoc;
29+
private DefaultModelTypeBuilder? adHocBuilder;
2830
private ConcurrentDictionary<ApiVersion, ModuleBuilder>? modules;
2931
private ConcurrentDictionary<ApiVersion, IDictionary<EdmTypeKey, Type>>? generatedEdmTypesPerVersion;
3032
private ConcurrentDictionary<ApiVersion, ConcurrentDictionary<EdmTypeKey, Type>>? generatedActionParamsPerVersion;
3133

34+
private DefaultModelTypeBuilder( bool adHoc ) => this.adHoc = adHoc;
35+
36+
/// <summary>
37+
/// Initializes a new instance of the <see cref="DefaultModelTypeBuilder"/> class.
38+
/// </summary>
39+
public DefaultModelTypeBuilder() { }
40+
3241
/// <inheritdoc />
3342
public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredType, Type clrType, ApiVersion apiVersion )
3443
{
@@ -37,6 +46,12 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp
3746
throw new ArgumentNullException( nameof( model ) );
3847
}
3948

49+
if ( !adHoc && model.IsAdHoc() )
50+
{
51+
adHocBuilder ??= new( adHoc: true );
52+
return adHocBuilder.NewStructuredType( model, structuredType, clrType, apiVersion );
53+
}
54+
4055
if ( structuredType == null )
4156
{
4257
throw new ArgumentNullException( nameof( structuredType ) );
@@ -54,10 +69,9 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp
5469

5570
generatedEdmTypesPerVersion ??= new();
5671

57-
var typeKey = new EdmTypeKey( structuredType, apiVersion );
5872
var edmTypes = generatedEdmTypesPerVersion.GetOrAdd( apiVersion, key => GenerateTypesForEdmModel( model, key ) );
5973

60-
return edmTypes[typeKey];
74+
return edmTypes[new( structuredType, apiVersion )];
6175
}
6276

6377
/// <inheritdoc />
@@ -68,6 +82,12 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont
6882
throw new ArgumentNullException( nameof( model ) );
6983
}
7084

85+
if ( !adHoc && model.IsAdHoc() )
86+
{
87+
adHocBuilder ??= new( adHoc: true );
88+
return adHocBuilder.NewActionParameters( model, action, controllerName, apiVersion );
89+
}
90+
7191
if ( action == null )
7292
{
7393
throw new ArgumentNullException( nameof( action ) );
@@ -85,7 +105,7 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont
85105

86106
generatedActionParamsPerVersion ??= new();
87107

88-
var paramTypes = generatedActionParamsPerVersion.GetOrAdd( apiVersion, _ => new ConcurrentDictionary<EdmTypeKey, Type>() );
108+
var paramTypes = generatedActionParamsPerVersion.GetOrAdd( apiVersion, _ => new() );
89109
var fullTypeName = $"{controllerName}.{action.Namespace}.{controllerName}{action.Name}Parameters";
90110
var key = new EdmTypeKey( fullTypeName, apiVersion );
91111
var type = paramTypes.GetOrAdd( key, _ =>
@@ -322,8 +342,7 @@ private static Type ResolveType(
322342
}
323343

324344
[MethodImpl( MethodImplOptions.AggressiveInlining )]
325-
private static Type MakeEnumerable( Type itemType ) =>
326-
( ienumerableOfT ??= typeof( IEnumerable<> ) ).MakeGenericType( itemType );
345+
private static Type MakeEnumerable( Type itemType ) => ( ienumerableOfT ??= typeof( IEnumerable<> ) ).MakeGenericType( itemType );
327346

328347
[MethodImpl( MethodImplOptions.AggressiveInlining )]
329348
private static Type CreateTypeFromSignature( ModuleBuilder moduleBuilder, ClassSignature @class ) =>
@@ -389,7 +408,11 @@ private static IDictionary<EdmTypeKey, Type> ResolveDependencies( BuilderContext
389408
return edmTypes;
390409
}
391410

392-
private static PropertyBuilder AddProperty( TypeBuilder addTo, Type shouldBeAdded, string name, IReadOnlyList<CustomAttributeBuilder> customAttributes )
411+
private static PropertyBuilder AddProperty(
412+
TypeBuilder addTo,
413+
Type shouldBeAdded,
414+
string name,
415+
IReadOnlyList<CustomAttributeBuilder> customAttributes )
393416
{
394417
const MethodAttributes propertyMethodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
395418
var field = addTo.DefineField( "_" + name, shouldBeAdded, FieldAttributes.Private );
@@ -418,7 +441,7 @@ private static PropertyBuilder AddProperty( TypeBuilder addTo, Type shouldBeAdde
418441
return propertyBuilder;
419442
}
420443

421-
private static AssemblyName NewAssemblyName(ApiVersion apiVersion)
444+
private static AssemblyName NewAssemblyName( ApiVersion apiVersion, bool adHoc )
422445
{
423446
// this is not strictly necessary, but it makes debugging a bit easier as each
424447
// assembly-qualified type name provides visibility as to which api version a
@@ -455,20 +478,26 @@ private static AssemblyName NewAssemblyName(ApiVersion apiVersion)
455478
}
456479

457480
name.Insert( 0, 'V' )
458-
.Append( NewGuid().ToString( "n", InvariantCulture ) )
459-
.Append( ".DynamicModels" );
481+
.Append( NewGuid().ToString( "n", InvariantCulture ) );
482+
483+
if ( adHoc )
484+
{
485+
name.Append( ".AdHoc" );
486+
}
487+
488+
name.Append( ".DynamicModels" );
460489

461490
return new( name.ToString() );
462491
}
463492

464493
[MethodImpl( MethodImplOptions.AggressiveInlining )]
465-
private static ModuleBuilder CreateModuleForApiVersion( ApiVersion apiVersion )
494+
private ModuleBuilder CreateModuleForApiVersion( ApiVersion apiVersion )
466495
{
467-
var name = NewAssemblyName( apiVersion );
496+
var assemblyName = NewAssemblyName( apiVersion, adHoc );
468497
#if NETFRAMEWORK
469-
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( name, Run );
498+
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( assemblyName, Run );
470499
#else
471-
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly( name, Run );
500+
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly( assemblyName, Run );
472501
#endif
473502
return assemblyBuilder.DefineDynamicModule( "<module>" );
474503
}

‎src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs

Copy file name to clipboardExpand all lines: src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs
+15-18Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
namespace Asp.Versioning.OData;
44

55
#if NETFRAMEWORK
6+
using Microsoft.OData.Edm;
67
using System.Net.Http;
78
using System.Web.Http;
89
#else
910
using Microsoft.AspNetCore.Mvc;
1011
using Microsoft.AspNetCore.OData.Results;
12+
using Microsoft.OData.Edm;
1113
#endif
1214
using System.Reflection;
1315
using System.Reflection.Emit;
@@ -54,12 +56,11 @@ public static Type SubstituteIfNecessary( this Type type, TypeSubstitutionContex
5456
var openTypes = new Stack<Type>();
5557
var apiVersion = context.ApiVersion;
5658
var resolver = new StructuredTypeResolver( context.Model );
59+
IEdmStructuredType? structuredType;
5760

5861
if ( IsSubstitutableGeneric( type, openTypes, out var innerType ) )
5962
{
60-
var structuredType = resolver.GetStructuredType( innerType! );
61-
62-
if ( structuredType == null )
63+
if ( ( structuredType = resolver.GetStructuredType( innerType! ) ) == null )
6364
{
6465
return type;
6566
}
@@ -74,14 +75,9 @@ public static Type SubstituteIfNecessary( this Type type, TypeSubstitutionContex
7475
return CloseGeneric( openTypes, newType );
7576
}
7677

77-
if ( CanBeSubstituted( type ) )
78+
if ( CanBeSubstituted( type ) && ( structuredType = resolver.GetStructuredType( type ) ) != null )
7879
{
79-
var structuredType = resolver.GetStructuredType( type );
80-
81-
if ( structuredType != null )
82-
{
83-
type = context.ModelTypeBuilder.NewStructuredType( context.Model, structuredType, type, apiVersion );
84-
}
80+
type = context.ModelTypeBuilder.NewStructuredType( context.Model, structuredType, type, apiVersion );
8581
}
8682

8783
return type;
@@ -242,16 +238,15 @@ private static Type CloseGeneric( Stack<Type> openTypes, Type innerType )
242238
return type;
243239
}
244240

245-
private static bool CanBeSubstituted( Type type )
246-
{
247-
return Type.GetTypeCode( type ) == TypeCode.Object &&
248-
!type.IsValueType &&
249-
!type.Equals( ActionResultType ) &&
241+
[MethodImpl( MethodImplOptions.AggressiveInlining )]
242+
private static bool CanBeSubstituted( Type type ) =>
243+
Type.GetTypeCode( type ) == TypeCode.Object &&
244+
!type.IsValueType &&
245+
!type.Equals( ActionResultType ) &&
250246
#if NETFRAMEWORK
251-
!type.Equals( HttpResponseType ) &&
247+
!type.Equals( HttpResponseType ) &&
252248
#endif
253-
!type.IsODataActionParameters();
254-
}
249+
!type.IsODataActionParameters();
255250

256251
internal static bool IsEnumerable(
257252
this Type type,
@@ -295,6 +290,7 @@ internal static bool IsEnumerable(
295290
return false;
296291
}
297292

293+
[MethodImpl( MethodImplOptions.AggressiveInlining )]
298294
private static bool IsSingleResult( this Type type ) => type.Is( SingleResultOfT );
299295

300296
private static bool IsODataValue( this Type? type )
@@ -323,6 +319,7 @@ private static bool IsODataValue( this Type? type )
323319
private static bool Is( this Type type, Type typeDefinition ) =>
324320
type.IsGenericType && type.GetGenericTypeDefinition().Equals( typeDefinition );
325321

322+
[MethodImpl( MethodImplOptions.AggressiveInlining )]
326323
private static bool ShouldExtractInnerType( this Type type ) =>
327324
type.IsDelta() ||
328325
#if !NETFRAMEWORK

‎src/Common/src/Common.OData.ApiExplorer/OData/TypeSubstitutionContext.cs

Copy file name to clipboardExpand all lines: src/Common/src/Common.OData.ApiExplorer/OData/TypeSubstitutionContext.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class TypeSubstitutionContext
3636
/// Gets API version associated with the source model.
3737
/// </summary>
3838
/// <value>The associated <see cref="ApiVersion">API version</see>.</value>
39-
public ApiVersion ApiVersion => apiVersion ??= Model.GetAnnotationValue<ApiVersionAnnotation>( Model )?.ApiVersion ?? ApiVersion.Neutral;
39+
public ApiVersion ApiVersion => apiVersion ??= Model.GetApiVersion() ?? ApiVersion.Neutral;
4040

4141
/// <summary>
4242
/// Gets the model type builder used to create substitution types.
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
3+
namespace Microsoft.OData.Edm;
4+
5+
using Asp.Versioning;
6+
using Asp.Versioning.OData;
7+
8+
/// <summary>
9+
/// Provides extension methods for <see cref="IEdmModel"/>.
10+
/// </summary>
11+
public static class IEdmModelExtensions
12+
{
13+
/// <summary>
14+
/// Gets the API version associated with the Entity Data Model (EDM).
15+
/// </summary>
16+
/// <param name="model">The extended <see cref="IEdmModel">EDM</see>.</param>
17+
/// <returns>The associated <see cref="ApiVersion">API version</see> or <c>null</c>.</returns>
18+
public static ApiVersion? GetApiVersion( this IEdmModel model ) =>
19+
model.GetAnnotationValue<ApiVersionAnnotation>( model )?.ApiVersion;
20+
21+
/// <summary>
22+
/// Gets a value indicating whether the Entity Data Model (EDM) is for defined ad hoc usage.
23+
/// </summary>
24+
/// <param name="model">The extended <see cref="IEdmModel">EDM</see>.</param>
25+
/// <returns>True if the EDM is defined for ad hoc usage; otherwise, false.</returns>
26+
public static bool IsAdHoc( this IEdmModel model ) =>
27+
model.GetAnnotationValue<AdHocAnnotation>( model ) is not null;
28+
}
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
3+
namespace Asp.Versioning.OData;
4+
5+
/// <summary>
6+
/// Represents an annotation for ad hoc usage.
7+
/// </summary>
8+
public sealed class AdHocAnnotation
9+
{
10+
/// <summary>
11+
/// Gets a singleton instance of the annotation.
12+
/// </summary>
13+
/// <value>A singleton <see cref="AdHocAnnotation">annotation</see> instance.</value>
14+
public static AdHocAnnotation Instance { get; } = new();
15+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.