4
4
using Microsoft . Web . Http ;
5
5
using System ;
6
6
using System . Collections . Concurrent ;
7
+ using System . Collections . Generic ;
7
8
using System . Diagnostics . Contracts ;
8
9
using System . Linq ;
9
10
using System . Net . Http . Formatting ;
11
+ using System . Net . Http . Headers ;
12
+ using System . Reflection ;
10
13
using static System . Linq . Expressions . Expression ;
11
14
using static System . Net . Http . Headers . MediaTypeHeaderValue ;
12
15
using static System . Reflection . BindingFlags ;
@@ -28,13 +31,7 @@ internal static Func<MediaTypeFormatter, MediaTypeFormatter> GetOrCreateCloneFun
28
31
return cloneFunctions . GetOrAdd ( type , NewCloneFunction ) ;
29
32
}
30
33
31
- static MediaTypeFormatter UseICloneable ( MediaTypeFormatter instance )
32
- {
33
- Contract . Requires ( instance != null ) ;
34
- Contract . Ensures ( Contract . Result < MediaTypeFormatter > ( ) != null ) ;
35
-
36
- return ( MediaTypeFormatter ) ( ( ICloneable ) instance ) . Clone ( ) ;
37
- }
34
+ static MediaTypeFormatter UseICloneable ( MediaTypeFormatter instance ) => ( MediaTypeFormatter ) ( ( ICloneable ) instance ) . Clone ( ) ;
38
35
39
36
static Func < MediaTypeFormatter , MediaTypeFormatter > NewCloneFunction ( Type type )
40
37
{
@@ -45,7 +42,7 @@ static Func<MediaTypeFormatter, MediaTypeFormatter> NewCloneFunction( Type type
45
42
NewParameterlessConstructorActivator ( type ) ??
46
43
throw new InvalidOperationException ( LocalSR . MediaTypeFormatterNotCloneable . FormatDefault ( type . Name , typeof ( ICloneable ) . Name ) ) ;
47
44
48
- return instance => CloneMediaTypes ( clone ( instance ) ) ;
45
+ return instance => CloneMediaTypes ( clone ( instance ) , instance ) ;
49
46
}
50
47
51
48
static Func < MediaTypeFormatter , MediaTypeFormatter > NewCopyConstructorActivator ( Type type )
@@ -67,7 +64,20 @@ static Func<MediaTypeFormatter, MediaTypeFormatter> NewCopyConstructorActivator(
67
64
var @new = New ( constructor , Convert ( formatter , type ) ) ;
68
65
var lambda = Lambda < Func < MediaTypeFormatter , MediaTypeFormatter > > ( @new , formatter ) ;
69
66
70
- return lambda . Compile ( ) ; // formatter => new MediaTypeFormatter( formatter );
67
+ return ReinitializeSupportedMediaTypes ( lambda . Compile ( ) ) ; // formatter => new MediaTypeFormatter( formatter );
68
+ }
69
+
70
+ static Func < MediaTypeFormatter , MediaTypeFormatter > ReinitializeSupportedMediaTypes ( Func < MediaTypeFormatter , MediaTypeFormatter > clone )
71
+ {
72
+ Contract . Requires ( clone != null ) ;
73
+ Contract . Ensures ( Contract . Result < Func < MediaTypeFormatter , MediaTypeFormatter > > ( ) != null ) ;
74
+
75
+ return formatter =>
76
+ {
77
+ var instance = clone ( formatter ) ;
78
+ SupportedMediaTypesInitializer . Initialize ( instance ) ;
79
+ return instance ;
80
+ } ;
71
81
}
72
82
73
83
static Func < MediaTypeFormatter , MediaTypeFormatter > NewParameterlessConstructorActivator ( Type type )
@@ -92,21 +102,58 @@ static Func<MediaTypeFormatter, MediaTypeFormatter> NewParameterlessConstructorA
92
102
return lambda . Compile ( ) ; // formatter => new MediaTypeFormatter();
93
103
}
94
104
95
- static MediaTypeFormatter CloneMediaTypes ( MediaTypeFormatter instance )
105
+ static MediaTypeFormatter CloneMediaTypes ( MediaTypeFormatter target , MediaTypeFormatter source )
96
106
{
97
- Contract . Requires ( instance != null ) ;
107
+ Contract . Requires ( target != null ) ;
108
+ Contract . Requires ( source != null ) ;
98
109
Contract . Ensures ( Contract . Result < MediaTypeFormatter > ( ) != null ) ;
99
110
100
- var mediaTypes = instance . SupportedMediaTypes . ToArray ( ) ;
111
+ target . SupportedMediaTypes . Clear ( ) ;
101
112
102
- instance . SupportedMediaTypes . Clear ( ) ;
113
+ foreach ( var mediaType in source . SupportedMediaTypes )
114
+ {
115
+ target . SupportedMediaTypes . Add ( Parse ( mediaType . ToString ( ) ) ) ;
116
+ }
117
+
118
+ return target ;
119
+ }
120
+
121
+ /// <summary>
122
+ /// Supports cloning with a copy constructor.
123
+ /// </summary>
124
+ /// <remarks>
125
+ /// The <see cref="MediaTypeFormatter"/> copy constructor does not clone the SupportedMediaTypes property or backing field.
126
+ /// <seealso cref="!:https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4e40cdef9c8a8226685f95ef03b746bc8322aa92/src/System.Net.Http.Formatting/Formatting/MediaTypeFormatter.cs#L62"/>
127
+ /// </remarks>
128
+ static class SupportedMediaTypesInitializer
129
+ {
130
+ static FieldInfo field ;
131
+ static PropertyInfo property ;
132
+ static readonly ConstructorInfo newCollection ;
103
133
104
- foreach ( var mediaType in mediaTypes )
134
+ static SupportedMediaTypesInitializer ( )
105
135
{
106
- instance . SupportedMediaTypes . Add ( Parse ( mediaType . ToString ( ) ) ) ;
136
+ var flags = Public | NonPublic | Instance ;
137
+ var mediaTypeFormatter = typeof ( MediaTypeFormatter ) ;
138
+
139
+ field = mediaTypeFormatter . GetField ( "_supportedMediaTypes" , flags ) ;
140
+ property = mediaTypeFormatter . GetProperty ( nameof ( MediaTypeFormatter . SupportedMediaTypes ) , flags ) ;
141
+ newCollection = mediaTypeFormatter . GetNestedType ( "MediaTypeHeaderValueCollection" , flags ) . GetConstructors ( flags ) . Single ( ) ;
107
142
}
108
143
109
- return instance ;
144
+ internal static void Initialize ( MediaTypeFormatter instance )
145
+ {
146
+ var list = new List < MediaTypeHeaderValue > ( ) ;
147
+ var collection = newCollection . Invoke ( new object [ ] { list } ) ;
148
+
149
+ // the _supportedMediaTypes field is "readonly", which is why we must use Reflection instead of compiling an expression;
150
+ // interestingly, the Reflection API lets us break rules that expression compilation does not
151
+ field . SetValue ( instance , list ) ;
152
+
153
+ // since the value for the SupportedMediaTypes property comes from the backing field, we must do this here, even
154
+ // though it's possible to set this property with a compiled expression
155
+ property . SetMethod . Invoke ( instance , new object [ ] { collection } ) ;
156
+ }
110
157
}
111
158
}
112
159
}
0 commit comments