Skip to content

Commit 98287c0

Browse files
committed
Merge pull request #25 from SphtKr/cache-propmaps-issue23
Cache reflection operations
2 parents b01fa0a + 5558fcf commit 98287c0

File tree

8 files changed

+405
-131
lines changed

8 files changed

+405
-131
lines changed

JSONAPI.EntityFramework.Tests/EntityConverterTests.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ public void SetupEntities()
107107
public void SerializeTest()
108108
{
109109
// Arrange
110-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
111-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
110+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
112111
MemoryStream stream = new MemoryStream();
113112

114113
// Act
@@ -125,8 +124,7 @@ public void SerializeTest()
125124
public async Task DeserializePostIntegrationTest()
126125
{
127126
// Arrange
128-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
129-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
127+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
130128
MemoryStream stream = new MemoryStream();
131129

132130
EntityFrameworkMaterializer materializer = new EntityFrameworkMaterializer(context);
@@ -160,8 +158,7 @@ public async Task DeserializePostIntegrationTest()
160158
public async Task UnderpostingTest()
161159
{
162160
// Arrange
163-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
164-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
161+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
165162
MemoryStream stream = new MemoryStream();
166163

167164
EntityFrameworkMaterializer materializer = new EntityFrameworkMaterializer(context);

JSONAPI.Tests/Core/MetadataManagerTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ public void PropertyWasPresentTest()
1515
{
1616
// Arrange
1717

18-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
19-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
18+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
2019
MemoryStream stream = new MemoryStream();
2120

2221
stream = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(@"{""posts"":{""id"":42,""links"":{""author"":""18""}}}"));

JSONAPI.Tests/Core/ModelManagerTests.cs

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
using JSONAPI.Core;
44
using JSONAPI.Tests.Models;
55
using System.Reflection;
6+
using System.Collections.Generic;
7+
using System.Collections;
68

79
namespace JSONAPI.Tests.Core
810
{
911
[TestClass]
1012
public class ModelManagerTests
1113
{
12-
private class InvalidModel
14+
private class InvalidModel // No Id discernable!
1315
{
1416
public string Data { get; set; }
1517
}
@@ -18,7 +20,7 @@ private class InvalidModel
1820
public void FindsIdNamedId()
1921
{
2022
// Arrange
21-
var mm = new ModelManager();
23+
var mm = new ModelManager(new PluralizationService());
2224

2325
// Act
2426
PropertyInfo idprop = mm.GetIdProperty(typeof(Author));
@@ -32,13 +34,119 @@ public void FindsIdNamedId()
3234
public void DoesntFindMissingId()
3335
{
3436
// Arrange
35-
var mm = new ModelManager();
37+
var mm = new ModelManager(new PluralizationService());
3638

3739
// Act
3840
PropertyInfo idprop = mm.GetIdProperty(typeof(InvalidModel));
3941

4042
// Assert
4143
Assert.Fail("An InvalidOperationException should be thrown and we shouldn't get here!");
4244
}
45+
46+
[TestMethod]
47+
public void GetJsonKeyForTypeTest()
48+
{
49+
// Arrange
50+
var pluralizationService = new PluralizationService();
51+
var mm = new ModelManager(pluralizationService);
52+
53+
// Act
54+
var postKey = mm.GetJsonKeyForType(typeof(Post));
55+
var authorKey = mm.GetJsonKeyForType(typeof(Author));
56+
var commentKey = mm.GetJsonKeyForType(typeof(Comment));
57+
58+
// Assert
59+
Assert.AreEqual("posts", postKey);
60+
Assert.AreEqual("authors", authorKey);
61+
Assert.AreEqual("comments", commentKey);
62+
}
63+
64+
[TestMethod]
65+
public void GetJsonKeyForPropertyTest()
66+
{
67+
// Arrange
68+
var pluralizationService = new PluralizationService();
69+
var mm = new ModelManager(pluralizationService);
70+
71+
// Act
72+
var idKey = mm.GetJsonKeyForProperty(typeof(Author).GetProperty("Id"));
73+
var nameKey = mm.GetJsonKeyForProperty(typeof(Author).GetProperty("Name"));
74+
var postsKey = mm.GetJsonKeyForProperty(typeof(Author).GetProperty("Posts"));
75+
76+
// Assert
77+
Assert.AreEqual("id", idKey);
78+
Assert.AreEqual("name", nameKey);
79+
Assert.AreEqual("posts", postsKey);
80+
81+
}
82+
83+
[TestMethod]
84+
public void GetPropertyForJsonKeyTest()
85+
{
86+
// Arrange
87+
var pluralizationService = new PluralizationService();
88+
var mm = new ModelManager(pluralizationService);
89+
Type authorType = typeof(Author).GetType();
90+
91+
// Act
92+
var idProp = mm.GetPropertyForJsonKey(authorType, "id");
93+
var nameProp = mm.GetPropertyForJsonKey(authorType, "name");
94+
var postsProp = mm.GetPropertyForJsonKey(authorType, "posts");
95+
96+
// Assert
97+
Assert.AreSame(authorType.GetProperty("Id"), idProp);
98+
Assert.AreSame(authorType.GetProperty("Name"), nameProp);
99+
Assert.AreSame(authorType.GetProperty("Posts"), postsProp);
100+
101+
}
102+
103+
[TestMethod]
104+
public void IsSerializedAsManyTest()
105+
{
106+
// Arrange
107+
var mm = new ModelManager(new PluralizationService());
108+
109+
// Act
110+
bool isArray = mm.IsSerializedAsMany(typeof(Post[]));
111+
bool isGenericEnumerable = mm.IsSerializedAsMany(typeof(IEnumerable<Post>));
112+
bool isString = mm.IsSerializedAsMany(typeof(string));
113+
bool isAuthor = mm.IsSerializedAsMany(typeof(Author));
114+
bool isNonGenericEnumerable = mm.IsSerializedAsMany(typeof(IEnumerable));
115+
116+
// Assert
117+
Assert.IsTrue(isArray);
118+
Assert.IsTrue(isGenericEnumerable);
119+
Assert.IsFalse(isString);
120+
Assert.IsFalse(isAuthor);
121+
Assert.IsFalse(isNonGenericEnumerable);
122+
}
123+
124+
[TestMethod]
125+
public void GetElementTypeTest()
126+
{
127+
// Arrange
128+
var mm = new ModelManager(new PluralizationService());
129+
130+
// Act
131+
Type postTypeFromArray = mm.GetElementType(typeof(Post[]));
132+
Type postTypeFromEnumerable = mm.GetElementType(typeof(IEnumerable<Post>));
133+
134+
// Assert
135+
Assert.AreSame(typeof(Post), postTypeFromArray);
136+
Assert.AreSame(typeof(Post), postTypeFromEnumerable);
137+
}
138+
139+
[TestMethod]
140+
public void GetElementTypeInvalidArgumentTest()
141+
{
142+
// Arrange
143+
var mm = new ModelManager(new PluralizationService());
144+
145+
// Act
146+
Type x = mm.GetElementType(typeof(Author));
147+
148+
// Assert
149+
Assert.IsNull(x, "Return value of GetElementType should be null for a non-Many type argument!");
150+
}
43151
}
44152
}

JSONAPI.Tests/Json/JsonApiMediaFormaterTests.cs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ private enum TestEnum
107107
public void CanWritePrimitiveTest()
108108
{
109109
// Arrange
110-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
110+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new PluralizationService());
111111
// Act
112112
// Assert
113113
Assert.IsTrue(formatter.CanWriteTypeAsPrimitive(typeof(Int32)), "CanWriteTypeAsPrimitive returned wrong answer for Integer!");
@@ -133,8 +133,7 @@ public void SerializerIntegrationTest()
133133
//ModelConverter mc = new ModelConverter();
134134
//ContractResolver.PluralizationService = new PluralizationService();
135135

136-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
137-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
136+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
138137
MemoryStream stream = new MemoryStream();
139138

140139
// Act
@@ -158,8 +157,7 @@ public void SerializeArrayIntegrationTest()
158157
//ModelConverter mc = new ModelConverter();
159158
//ContractResolver.PluralizationService = new PluralizationService();
160159

161-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
162-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
160+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
163161
MemoryStream stream = new MemoryStream();
164162

165163
// Act
@@ -180,7 +178,6 @@ public void Should_serialize_error()
180178
{
181179
// Arrange
182180
var formatter = new JSONAPI.Json.JsonApiFormatter(new MockErrorSerializer());
183-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
184181
var stream = new MemoryStream();
185182

186183
// Act
@@ -199,8 +196,7 @@ public void Should_serialize_error()
199196
public void SerializeErrorIntegrationTest()
200197
{
201198
// Arrange
202-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
203-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
199+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
204200
MemoryStream stream = new MemoryStream();
205201

206202
// Act
@@ -224,8 +220,7 @@ public void SerializeErrorIntegrationTest()
224220
public void DeserializeCollectionIntegrationTest()
225221
{
226222
// Arrange
227-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
228-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
223+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
229224
MemoryStream stream = new MemoryStream();
230225

231226
formatter.WriteToStreamAsync(typeof(Post), new List<Post> {p, p2}, stream, (System.Net.Http.HttpContent)null, (System.Net.TransportContext)null);
@@ -246,8 +241,7 @@ public void DeserializeCollectionIntegrationTest()
246241
[TestMethod(), Timeout(1000)]
247242
public void DeserializeExtraPropertyTest()
248243
{
249-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
250-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
244+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
251245
MemoryStream stream = new MemoryStream();
252246

253247
stream = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(@"{""authors"":{""id"":13,""name"":""Jason Hater"",""bogus"":""PANIC!"",""links"":{""posts"":[]}}}"));
@@ -264,8 +258,7 @@ public void DeserializeExtraPropertyTest()
264258
[TestMethod(), Timeout(1000)]
265259
public void DeserializeExtraRelationshipTest()
266260
{
267-
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter();
268-
formatter.PluralizationService = new JSONAPI.Core.PluralizationService();
261+
JsonApiFormatter formatter = new JSONAPI.Json.JsonApiFormatter(new JSONAPI.Core.PluralizationService());
269262
MemoryStream stream = new MemoryStream();
270263

271264
stream = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(@"{""authors"":{""id"":13,""name"":""Jason Hater"",""links"":{""posts"":[],""bogus"":[""PANIC!""]}}}"));

JSONAPI.Tests/Json/LinkTemplateTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ public void SetupModels()
5050
public void GetResourceWithLinkTemplateRelationship()
5151
{
5252
var formatter = new JsonApiFormatter
53-
{
54-
PluralizationService = new JSONAPI.Core.PluralizationService()
55-
};
53+
(
54+
new JSONAPI.Core.PluralizationService()
55+
);
5656
var stream = new MemoryStream();
5757

5858
formatter.WriteToStreamAsync(typeof(Post), ThePost, stream, null, null);

JSONAPI/Core/IModelManager.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,70 @@ namespace JSONAPI.Core
99
{
1010
public interface IModelManager
1111
{
12+
IPluralizationService PluralizationService { get; }
13+
14+
/// <summary>
15+
/// Returns the property that is treated as the unique identifier in a given class.
16+
/// This is used most importantly by JsonApiFormatter to determine what value to
17+
/// write when serializing a "Many" relationship as an array of Ids. It is also
18+
/// used to make dummy related objects (with only the Id property set) when
19+
/// deserializing a JSON payload that specifies a related object only by Id.
20+
///
21+
/// Rules for determining this may vary by implementation.
22+
/// </summary>
23+
/// <param name="type"></param>
24+
/// <returns>The property determined to represent the Id.</returns>
1225
PropertyInfo GetIdProperty(Type type);
26+
27+
/// <summary>
28+
/// Returns the key that will be used to represent a collection of objects of a
29+
/// given type, for example in the top-level of a JSON API document or within
30+
/// the "linked" objects section of a payload.
31+
/// </summary>
32+
/// <param name="type">The serializable Type</param>
33+
/// <returns>The string denoting the given type in JSON documents.</returns>
34+
string GetJsonKeyForType(Type type);
35+
36+
/// <summary>
37+
/// Returns the key that will be used to represent the given property in serialized
38+
/// JSON. Inverse of GetPropertyForJsonKey.
39+
/// </summary>
40+
/// <param name="propInfo">The serializable property</param>
41+
/// <returns>The string denoting the given property within a JSON document.</returns>
42+
string GetJsonKeyForProperty(PropertyInfo propInfo); //TODO: Do we need to have a type parameter here, in case the property is inherited?
43+
44+
/// <summary>
45+
/// Returns the property corresponding to a given JSON Key. Inverse of GetJsonKeyForProperty.
46+
/// </summary>
47+
/// <param name="type">The Type to find the property on</param>
48+
/// <param name="jsonKey">The JSON key representing a property</param>
49+
/// <returns></returns>
50+
PropertyInfo GetPropertyForJsonKey(Type type, string jsonKey);
51+
52+
/// <summary>
53+
/// Analogue to System.Type.GetProperties(), but made available so that any caching done
54+
/// by an IModelManager can be leveraged to return the results faster.
55+
/// </summary>
56+
/// <param name="type">The type to get properties from</param>
57+
/// <returns>All properties recognized by the IModelManager.</returns>
58+
//TODO: This needs to include JsonIgnore'd properties, so that they can be found and explicitly included at runtime...confusing? Add another method that excludes these?
59+
PropertyInfo[] GetProperties(Type type);
60+
61+
/// <summary>
62+
/// Determines whether or not the given type will be treated as a "Many" relationship.
63+
/// </summary>
64+
/// <param name="type">The serializable Type</param>
65+
/// <returns>True for Array and IEnumerable&lt;T&gt; types, false otherwise.</returns>
66+
bool IsSerializedAsMany(Type type);
67+
68+
/// <summary>
69+
/// Analogue for System.Type.GetElementType, but works for arrays or IEnumerable&lt;T&gt;,
70+
/// and provides a capture point to cache potentially expensive reflection operations that
71+
/// have to occur repeatedly in JsonApiFormatter.
72+
/// </summary>
73+
/// <param name="manyType">A type which must be either an Array type or implement IEnumerable&lt;T&gt;.</param>
74+
/// <returns>The element type of an Array, or the first generic parameter of an IEnumerable&lt;T&gt;.</returns>
75+
Type GetElementType(Type manyType);
76+
1377
}
1478
}

0 commit comments

Comments
 (0)