Skip to content

Commit 7b01416

Browse files
author
Chris Santero
committed
ensure materialized updates create empty collection property values
This was causing null reference exceptions when materializing new objects of types that have to-many relationships.
1 parent 9fbe305 commit 7b01416

3 files changed

Lines changed: 59 additions & 56 deletions

File tree

JSONAPI.EntityFramework/EntityFrameworkMaterializer.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ public virtual async Task<object> MaterializeAsync(Type type, object ephemeral)
104104
{
105105
// Didn't find it...create a new one!
106106
retval = Activator.CreateInstance(type);
107+
107108
DbContext.Set(type).Add(retval);
108109
if (!anyNull)
109110
{
@@ -331,6 +332,12 @@ private async Task Merge (Type type, object ephemeral, object material)
331332
Type elementType = GetSingleType(prop.PropertyType);
332333

333334
var materialMany = (IEnumerable<Object>)prop.GetValue(material, null);
335+
if (materialMany == null)
336+
{
337+
materialMany = prop.PropertyType.CreateEnumerableInstance();
338+
prop.SetValue(material, materialMany);
339+
}
340+
334341
var ephemeralMany = (IEnumerable<Object>)prop.GetValue(ephemeral, null);
335342

336343
var materialKeys = new HashSet<EntityKey>();
@@ -389,7 +396,8 @@ private async Task Merge (Type type, object ephemeral, object material)
389396
else
390397
{
391398
object[] idParams = ephemeralKey.EntityKeyValues.Select(ekv => ekv.Value).ToArray();
392-
prop.SetValue(material, await GetByIdAsync(prop.PropertyType, idParams), null);
399+
var relatedMaterial = await GetByIdAsync(prop.PropertyType, idParams);
400+
prop.SetValue(material, relatedMaterial, null);
393401
}
394402
}
395403
}

JSONAPI/Extensions/TypeExtensions.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections.Generic;
3+
using Newtonsoft.Json;
24

35
namespace JSONAPI.Extensions
46
{
@@ -18,5 +20,44 @@ public static bool CanWriteAsJsonApiAttribute(this Type objectType)
1820
|| objectType.IsEnum;
1921
}
2022

23+
public static IEnumerable<object> CreateEnumerableInstance(this Type type)
24+
{
25+
Type relType;
26+
if (type.IsGenericType)
27+
{
28+
relType = type.GetGenericArguments()[0];
29+
}
30+
else
31+
{
32+
// Must be an array at this point, right??
33+
relType = type.GetElementType();
34+
}
35+
36+
// Hmm...now we have to create an object that fits this property. This could get messy...
37+
if (!type.IsInterface && !type.IsAbstract)
38+
{
39+
// Whew...okay, just instantiate one of these...
40+
return (IEnumerable<Object>)Activator.CreateInstance(type);
41+
}
42+
43+
// Ugh...now we're really in trouble...hopefully one of these will work:
44+
if (type.IsGenericType)
45+
{
46+
if (type.IsAssignableFrom(typeof(List<>).MakeGenericType(relType)))
47+
{
48+
return (IEnumerable<Object>) Activator.CreateInstance(typeof(List<>).MakeGenericType(relType));
49+
}
50+
51+
if (type.IsAssignableFrom(typeof(HashSet<>).MakeGenericType(relType)))
52+
{
53+
return
54+
(IEnumerable<Object>) Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(relType));
55+
}
56+
57+
//TODO: Other likely candidates??
58+
}
59+
60+
return null;
61+
}
2162
}
2263
}

JSONAPI/Json/JsonApiFormatter.cs

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using JSONAPI.Attributes;
1+
using System.Collections.ObjectModel;
2+
using JSONAPI.Attributes;
23
using JSONAPI.Core;
34
using Newtonsoft.Json;
45
using Newtonsoft.Json.Linq;
@@ -902,63 +903,16 @@ private void DeserializeLinkedResources(object obj, JsonReader reader)
902903
{
903904
// To-many relationship
904905

905-
Type relType;
906-
if (prop.PropertyType.IsGenericType)
907-
{
908-
relType = prop.PropertyType.GetGenericArguments()[0];
909-
}
910-
else
911-
{
912-
// Must be an array at this point, right??
913-
relType = prop.PropertyType.GetElementType();
914-
}
915-
916906
var hmrel = (IEnumerable<Object>) prop.GetValue(obj, null);
917907
if (hmrel == null)
918908
{
919-
// Hmm...now we have to create an object that fits this property. This could get messy...
920-
if (!prop.PropertyType.IsInterface && !prop.PropertyType.IsAbstract)
921-
{
922-
// Whew...okay, just instantiate one of these...
923-
hmrel = (IEnumerable<Object>) Activator.CreateInstance(prop.PropertyType);
924-
}
925-
else
926-
{
927-
// Ugh...now we're really in trouble...hopefully one of these will work:
928-
if (prop.PropertyType.IsGenericType)
929-
{
930-
if (prop.PropertyType.IsAssignableFrom(typeof (List<>).MakeGenericType(relType)))
931-
{
932-
hmrel =
933-
(IEnumerable<Object>)
934-
Activator.CreateInstance(typeof (List<>).MakeGenericType(relType));
935-
}
936-
else if (
937-
prop.PropertyType.IsAssignableFrom(
938-
typeof (HashSet<>).MakeGenericType(relType)))
939-
{
940-
hmrel =
941-
(IEnumerable<Object>)
942-
Activator.CreateInstance(
943-
typeof (HashSet<>).MakeGenericType(relType));
944-
}
945-
//TODO: Other likely candidates??
946-
else
947-
{
948-
// punt!
949-
throw new JsonReaderException(
950-
String.Format(
951-
"Could not create empty container for relationship property {0}!",
952-
prop));
953-
}
954-
}
955-
else
956-
{
957-
// erm...Array??!?
958-
hmrel =
959-
(IEnumerable<Object>) Array.CreateInstance(relType, linkageObjects.Count);
960-
}
961-
}
909+
hmrel = prop.PropertyType.CreateEnumerableInstance();
910+
if (hmrel == null)
911+
// punt!
912+
throw new JsonReaderException(
913+
String.Format(
914+
"Could not create empty container for relationship property {0}!",
915+
prop));
962916
}
963917

964918
// We're having problems with how to generalize/cast/generic-ize this code, so for the time

0 commit comments

Comments
 (0)