diff --git a/JSONAPI.Tests/Core/ModelManagerTests.cs b/JSONAPI.Tests/Core/ModelManagerTests.cs new file mode 100644 index 00000000..7d364777 --- /dev/null +++ b/JSONAPI.Tests/Core/ModelManagerTests.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using JSONAPI.Core; +using JSONAPI.Tests.Models; +using System.Reflection; + +namespace JSONAPI.Tests.Core +{ + [TestClass] + public class ModelManagerTests + { + private class InvalidModel + { + public string Data { get; set; } + } + + [TestMethod] + public void FindsIdNamedId() + { + // Arrange + var mm = new ModelManager(); + + // Act + PropertyInfo idprop = mm.GetIdProperty(typeof(Author)); + + // Assert + Assert.AreSame(typeof(Author).GetProperty("Id"), idprop); + } + + [TestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public void DoesntFindMissingId() + { + // Arrange + var mm = new ModelManager(); + + // Act + PropertyInfo idprop = mm.GetIdProperty(typeof(InvalidModel)); + + // Assert + Assert.Fail("An InvalidOperationException should be thrown and we shouldn't get here!"); + } + } +} diff --git a/JSONAPI.Tests/JSONAPI.Tests.csproj b/JSONAPI.Tests/JSONAPI.Tests.csproj index 9e36c180..3f00810a 100644 --- a/JSONAPI.Tests/JSONAPI.Tests.csproj +++ b/JSONAPI.Tests/JSONAPI.Tests.csproj @@ -77,6 +77,7 @@ + diff --git a/JSONAPI/Core/IModelManager.cs b/JSONAPI/Core/IModelManager.cs new file mode 100644 index 00000000..78c21d83 --- /dev/null +++ b/JSONAPI/Core/IModelManager.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace JSONAPI.Core +{ + public interface IModelManager + { + PropertyInfo GetIdProperty(Type type); + } +} diff --git a/JSONAPI/Core/ModelManager.cs b/JSONAPI/Core/ModelManager.cs new file mode 100644 index 00000000..93292a79 --- /dev/null +++ b/JSONAPI/Core/ModelManager.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace JSONAPI.Core +{ + public class ModelManager : IModelManager + { + public ModelManager() { } + + #region Cache storage + + private Lazy> _idProperties + = new Lazy>( + () => new Dictionary() + ); + + #endregion + + #region Id property determination + + public PropertyInfo GetIdProperty(Type type) + { + PropertyInfo idprop = null; + + var idPropCache = _idProperties.Value; + + lock (idPropCache) + { + if (idPropCache.TryGetValue(type, out idprop)) return idprop; + + //TODO: Enable attribute-based determination + + idprop = type.GetProperty("Id"); + + if (idprop == null) + throw new InvalidOperationException(String.Format("Unable to determine Id property for type {0}", type)); + + idPropCache.Add(type, idprop); + } + + return idprop; + } + + #endregion + } +} diff --git a/JSONAPI/JSONAPI.csproj b/JSONAPI/JSONAPI.csproj index b7f4ff2b..5b9fc1e4 100644 --- a/JSONAPI/JSONAPI.csproj +++ b/JSONAPI/JSONAPI.csproj @@ -68,9 +68,11 @@ + + diff --git a/JSONAPI/Json/JsonApiFormatter.cs b/JSONAPI/Json/JsonApiFormatter.cs index 63a47e01..e1f62f4c 100644 --- a/JSONAPI/Json/JsonApiFormatter.cs +++ b/JSONAPI/Json/JsonApiFormatter.cs @@ -22,16 +22,23 @@ public class JsonApiFormatter : JsonMediaTypeFormatter public JsonApiFormatter() : this(new ErrorSerializer()) { + if (_modelManager == null) _modelManager = new ModelManager(); + SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/vnd.api+json")); } internal JsonApiFormatter(IErrorSerializer errorSerializer) { _errorSerializer = errorSerializer; - SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/vnd.api+json")); } - public IPluralizationService PluralizationService { get; set; } + public JsonApiFormatter(IModelManager modelManager) : this() + { + _modelManager = modelManager; + } + + public IPluralizationService PluralizationService { get; set; } //FIXME: Deprecated, will be removed shortly private readonly IErrorSerializer _errorSerializer; + private readonly IModelManager _modelManager; private Lazy> _relationAggregators = new Lazy>( @@ -152,7 +159,7 @@ protected void Serialize(object value, Stream writeStream, JsonWriter writer, Js // Do the Id now... writer.WritePropertyName("id"); - var idProp = GetIdProperty(value.GetType()); + var idProp = _modelManager.GetIdProperty(value.GetType()); writer.WriteValue(GetValueForIdProperty(idProp, value)); PropertyInfo[] props = value.GetType().GetProperties(); @@ -819,15 +826,17 @@ protected object GetById(Type type, string id) { // Only good for creating dummy relationship objects... object retval = Activator.CreateInstance(type); - PropertyInfo idprop = GetIdProperty(type); + PropertyInfo idprop = _modelManager.GetIdProperty(type); idprop.SetValue(retval, System.Convert.ChangeType(id, idprop.PropertyType)); return retval; } + /* protected PropertyInfo GetIdProperty(Type type) { return type.GetProperty("Id"); } + */ protected string GetValueForIdProperty(PropertyInfo idprop, object obj) { @@ -846,7 +855,7 @@ protected string GetValueForIdProperty(PropertyInfo idprop, object obj) protected string GetIdFor(object obj) { Type type = obj.GetType(); - PropertyInfo idprop = GetIdProperty(type); + PropertyInfo idprop = _modelManager.GetIdProperty(type); return GetValueForIdProperty(idprop, obj); }