From 9c2da55659b5f0d5847bb351983624722b22cc5f Mon Sep 17 00:00:00 2001 From: maca88 Date: Thu, 5 Jul 2018 23:46:44 +0200 Subject: [PATCH 1/6] Added a common cache serializer interface and an implementation by using Json.NET --- .../BinaryCacheSerializerFixture.cs | 15 + .../CacheSerializerFixture.cs | 463 ++++++++++++++++++ .../NHibernate.Caches.Common.Tests.csproj | 4 + .../BinaryCacheSerializer.cs | 30 ++ NHibernate.Caches.Common/ICacheSerializer.cs | 28 ++ .../NHibernate.Caches.Common.csproj | 29 ++ NHibernate.Caches.Everything.sln | 21 + .../JsonCacheSerializerFixture.cs | 120 +++++ ...te.Caches.Util.JsonSerializer.Tests.csproj | 28 ++ .../Program.cs | 12 + .../JsonCacheSerializer.cs | 448 +++++++++++++++++ ...ibernate.Caches.Util.JsonSerializer.csproj | 32 ++ appveyor.yml | 4 +- 13 files changed, 1232 insertions(+), 2 deletions(-) create mode 100644 NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs create mode 100644 NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs create mode 100644 NHibernate.Caches.Common/BinaryCacheSerializer.cs create mode 100644 NHibernate.Caches.Common/ICacheSerializer.cs create mode 100644 NHibernate.Caches.Common/NHibernate.Caches.Common.csproj create mode 100644 Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs create mode 100644 Util/NHibernate.Caches.Util.JsonSerializer.Tests/NHibernate.Caches.Util.JsonSerializer.Tests.csproj create mode 100644 Util/NHibernate.Caches.Util.JsonSerializer.Tests/Program.cs create mode 100644 Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs create mode 100644 Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj diff --git a/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs b/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs new file mode 100644 index 00000000..9b4759df --- /dev/null +++ b/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace NHibernate.Caches.Common.Tests +{ + [TestFixture] + public class BinaryCacheSerializerFixture : CacheSerializerFixture + { + protected override Func SerializerProvider => () => new BinaryCacheSerializer(); + } +} diff --git a/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs b/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs new file mode 100644 index 00000000..eded5cc8 --- /dev/null +++ b/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs @@ -0,0 +1,463 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Globalization; +using System.Linq; +using System.Xml; +using NHibernate.Cache; +using NHibernate.Cache.Entry; +using NHibernate.Engine; +using NHibernate.Intercept; +using NHibernate.Properties; +using NHibernate.Type; +using NSubstitute; +using NUnit.Framework; + +namespace NHibernate.Caches.Common.Tests +{ + [TestFixture] + public abstract class CacheSerializerFixture + { + protected abstract Func SerializerProvider { get; } + + protected ICacheSerializer DefaultSerializer { get; private set; } + + [OneTimeSetUp] + public void FixtureSetup() + { + DefaultSerializer = SerializerProvider(); + } + + [Test] + public void TestInteger() + { + var original = 15; + var data = DefaultSerializer.Serialize(original); + var copy = DefaultSerializer.Deserialize(data); + + Assert.That(copy, Is.TypeOf()); + Assert.That(copy, Is.EqualTo(original)); + } + + [Test] + public void TestObjectArray() + { + var original = GetAllNHibernateTypeValues(); + var data = DefaultSerializer.Serialize(original); + var copy = (object[]) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestListOfObjects() + { + var original = CreateListOfObjects(); + var data = DefaultSerializer.Serialize(original); + var copy = (List) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestHashtableIntegerKey() + { + var original = CreateHashtable(i => i); + var data = DefaultSerializer.Serialize(original); + var copy = (Hashtable) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestHashtableGuidKey() + { + var original = CreateHashtable(i => Guid.NewGuid()); + var data = DefaultSerializer.Serialize(original); + var copy = (Hashtable) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestHashtableStringKey() + { + var original = CreateHashtable(i => i.ToString()); + var data = DefaultSerializer.Serialize(original); + var copy = (Hashtable) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestHashtableCharKey() + { + var original = CreateHashtable(i => (char) (64 + i)); + var data = DefaultSerializer.Serialize(original); + var copy = (Hashtable) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestHashtableDateTime() + { + var original = CreateHashtable(i => DateTime.Now.AddDays(i)); + var data = DefaultSerializer.Serialize(original); + var copy = (Hashtable) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestCustomObject() + { + var original = new CustomEntity {Id = 10}; + var data = DefaultSerializer.Serialize(original); + var copy = (CustomEntity) DefaultSerializer.Deserialize(data); + + Assert.That(copy.Id, Is.EqualTo(original.Id)); + } + + [Test] + public void TestNullableInt32Type() + { + var serializer = DefaultSerializer; + var original = new object[] {NullableInt32.Default, new NullableInt32(32)}; + var data = serializer.Serialize(original); + var copy = (object[]) serializer.Deserialize(data); + AssertEqual(original, copy); + } + + protected void AssertEqual(Hashtable original, Hashtable copy) + { + Assert.That(copy, Has.Count.EqualTo(original.Count)); + foreach (DictionaryEntry entry in original) + { + Assert.That(copy.ContainsKey(entry.Key), Is.True, $"Key {entry.Key} for value {entry.Value} was not found."); + AssertEqual(entry.Value, copy[entry.Key]); + } + } + + protected void AssertEqual(object[] original, object[] copy) + { + Assert.That(copy, Has.Length.EqualTo(original.Length)); + for (var i = 0; i < original.Length; i++) + { + AssertEqual(original[i], copy[i]); + } + } + + protected void AssertEqual(List original, List copy) + { + Assert.That(copy, Has.Count.EqualTo(original.Count)); + for (var i = 0; i < original.Count; i++) + { + AssertEqual(original[i], copy[i]); + } + } + + protected void AssertEqual(object original, object copy) + { + if (original == null) + { + Assert.That(copy, Is.Null); + return; + } + Assert.That(copy, Is.TypeOf(original.GetType())); + Assert.That(copy, Is.EqualTo(original)); + } + + protected Hashtable CreateHashtable(Func keyProvider) + { + var hashtable = new Hashtable(); + var values = GetAllNHibernateTypeValues(); + for (var i = 0; i < values.Length; i++) + { + hashtable.Add(keyProvider(i), values[i]); + } + + return hashtable; + } + + protected List CreateListOfObjects() + { + return GetAllNHibernateTypeValues().ToList(); + } + + // TODO: make tests after upgraded to NHiberante 5.2 + protected CacheEntry CreateCacheEntry() + { + var types = GetNHibernateTypes(); + return new CacheEntry(types.Values.ToArray(), null, false, null, null, null); + } + + // TODO: make tests after upgraded to NHiberante 5.2 + protected CollectionCacheEntry CreateCollectionCacheEntry() + { + return new CollectionCacheEntry(null, null); + } + + // TODO: make tests after upgraded to NHiberante 5.2 + protected CacheLock CreateCacheLock() + { + return new CacheLock(1234, 1, 5); + } + + // TODO: make tests after upgraded to NHiberante 5.2 + protected CachedItem CreateCachedItem(object data) + { + return new CachedItem(data, 111, 5); + } + + // TODO: make tests after upgraded to NHiberante 5.2 + protected AnyType.ObjectTypeCacheEntry CreateObjectTypeCacheEntry() + { + return null; + } + + [Serializable] + public class CustomEntity + { + public int Id { get; set; } + } + + protected Dictionary GetNHibernateTypes() + { + var entityName = nameof(CustomEntity); + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml("XmlDoc"); + return new Dictionary + { + {NHibernateUtil.AnsiString, "test"}, + {NHibernateUtil.Binary, new byte[] {1, 2, 3, 4}}, + {NHibernateUtil.BinaryBlob, new byte[] {1, 2, 3, 4}}, + {NHibernateUtil.Boolean, true}, + {NHibernateUtil.Byte, (byte) 1}, + {NHibernateUtil.Character, 'a'}, + // TODO: enable after upgraded to NHiberante 5.2 + //{NHibernateUtil.CultureInfo, CultureInfo.CurrentCulture}, + {NHibernateUtil.DateTime, DateTime.Now}, + {NHibernateUtil.DateTimeNoMs, DateTime.Now}, + {NHibernateUtil.LocalDateTime, DateTime.Now}, + {NHibernateUtil.UtcDateTime, DateTime.UtcNow}, + {NHibernateUtil.LocalDateTimeNoMs, DateTime.Now}, + {NHibernateUtil.UtcDateTimeNoMs, DateTime.UtcNow}, + {NHibernateUtil.DateTimeOffset, DateTimeOffset.Now}, + {NHibernateUtil.Date, DateTime.Today}, + {NHibernateUtil.Decimal, 2.5m}, + {NHibernateUtil.Double, 2.5d}, + {NHibernateUtil.Currency, 2.5m}, + {NHibernateUtil.Guid, Guid.NewGuid()}, + {NHibernateUtil.Int16, (short) 1}, + {NHibernateUtil.Int32, 3}, + {NHibernateUtil.Int64, 3L}, + {NHibernateUtil.SByte, (sbyte) 1}, + {NHibernateUtil.UInt16, (ushort) 1}, + {NHibernateUtil.UInt32, (uint) 1}, + {NHibernateUtil.UInt64, (ulong) 1}, + {NHibernateUtil.Single, 1.1f}, + {NHibernateUtil.String, "test"}, + {NHibernateUtil.StringClob, "test"}, + {NHibernateUtil.Time, DateTime.Now}, + {NHibernateUtil.Ticks, DateTime.Now}, + {NHibernateUtil.TimeAsTimeSpan, TimeSpan.FromMilliseconds(15)}, + {NHibernateUtil.TimeSpan, TimeSpan.FromMilliseconds(1234)}, + {NHibernateUtil.DbTimestamp, DateTime.Now}, + {NHibernateUtil.TrueFalse, false}, + {NHibernateUtil.YesNo, true}, + // TODO: enable after upgraded to NHiberante 5.2 + //{NHibernateUtil.Class, typeof(IType)}, + {NHibernateUtil.ClassMetaType, entityName}, + {NHibernateUtil.Serializable, new CustomEntity {Id = 1}}, + // TODO: enable after upgraded to NHiberante 5.2 + //{NHibernateUtil.Object, new CustomEntity {Id = 10}}, + {NHibernateUtil.AnsiChar, 'a'}, + // TODO: enable after upgraded to NHiberante 5.2 + //{NHibernateUtil.XmlDoc, xmlDoc}, + //{NHibernateUtil.XDoc, XDocument.Parse("XDoc")}, + //{NHibernateUtil.Uri, new Uri("http://test.com")} + }; + } + + protected object[] GetAllNHibernateTypeValues() + { + var types = GetNHibernateTypes(); + var sessionImpl = Substitute.For(); + sessionImpl.BestGuessEntityName(Arg.Any()).Returns(o => o[0].GetType().Name); + sessionImpl.GetContextEntityIdentifier(Arg.Is(o => o is CustomEntity)).Returns(o => ((CustomEntity) o[0]).Id); + return TypeHelper.Disassemble( + types.Values.ToArray(), + types.Keys.Cast().ToArray(), + null, + sessionImpl, + null) + .Concat( + new[] + { + LazyPropertyInitializer.UnfetchedProperty, + BackrefPropertyAccessor.Unknown, + null + }) + .ToArray(); + } + + + [Serializable, TypeConverter(typeof(NullableInt32Converter))] + public struct NullableInt32 : IComparable + { + public static readonly NullableInt32 Default = new NullableInt32(); + + private readonly int _value; + + public NullableInt32(int value) + { + _value = value; + HasValue = true; + } + + public int CompareTo(object obj) + { + if (!(obj is NullableInt32 value)) + { + throw new ArgumentException("NullableInt32 can only compare to another NullableInt32 or a System.Int32"); + } + + if (value.HasValue == HasValue) + { + return HasValue ? Value.CompareTo(value.Value) : 0; + } + return HasValue ? 1 : -1; + } + + public bool HasValue { get; } + + public int Value + { + get + { + if (HasValue) + return _value; + throw new InvalidOperationException("Nullable type must have a value."); + } + } + + public static explicit operator int(NullableInt32 nullable) + { + if (!nullable.HasValue) + throw new NullReferenceException(); + + return nullable.Value; + } + + public static implicit operator NullableInt32(int value) + { + return new NullableInt32(value); + } + + public override string ToString() + { + return HasValue ? Value.ToString() : string.Empty; + } + + public override int GetHashCode() + { + return HasValue ? Value.GetHashCode() : 0; + } + + public override bool Equals(object obj) + { + if (obj is NullableInt32 int32) + { + return Equals(int32); + } + return false; + } + + public bool Equals(NullableInt32 x) + { + return Equals(this, x); + } + + public static bool Equals(NullableInt32 x, NullableInt32 y) + { + if (x.HasValue != y.HasValue) + return false; + if (x.HasValue) + return x.Value == y.Value; + return true; + } + } + + public class NullableInt32Converter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType) + { + return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType) + { + return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + switch (value) + { + case null: + return NullableInt32.Default; + case string _: + var stringValue = ((string) value).Trim(); + + if (stringValue == string.Empty) + return NullableInt32.Default; + + //get underlying types converter + var converter = TypeDescriptor.GetConverter(typeof(int)); + var newValue = (int) converter.ConvertFromString(context, culture, stringValue); + return new NullableInt32(newValue); + default: + return base.ConvertFrom(context, culture, value); + } + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, System.Type destinationType) + { + if (destinationType != typeof(InstanceDescriptor) || !(value is NullableInt32)) + { + return base.ConvertTo(context, culture, value, destinationType); + } + + var nullable = (NullableInt32) value; + + var constructorArgTypes = new[] {typeof(int)}; + var constructor = typeof(NullableInt32).GetConstructor(constructorArgTypes); + + if (constructor == null) + { + return base.ConvertTo(context, culture, value, destinationType); + } + + var constructorArgValues = new object[] {nullable.Value}; + return new InstanceDescriptor(constructor, constructorArgValues); + } + + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) + { + return new NullableInt32((int) propertyValues["Value"]); + } + + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) + { + return true; + } + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, + Attribute[] attributes) + { + return TypeDescriptor.GetProperties(typeof(NullableInt32), attributes); + } + + public override bool GetPropertiesSupported(ITypeDescriptorContext context) + { + return true; + } + } + } +} diff --git a/NHibernate.Caches.Common.Tests/NHibernate.Caches.Common.Tests.csproj b/NHibernate.Caches.Common.Tests/NHibernate.Caches.Common.Tests.csproj index b8537f32..203c1f28 100644 --- a/NHibernate.Caches.Common.Tests/NHibernate.Caches.Common.Tests.csproj +++ b/NHibernate.Caches.Common.Tests/NHibernate.Caches.Common.Tests.csproj @@ -14,5 +14,9 @@ + + + + diff --git a/NHibernate.Caches.Common/BinaryCacheSerializer.cs b/NHibernate.Caches.Common/BinaryCacheSerializer.cs new file mode 100644 index 00000000..b30139f8 --- /dev/null +++ b/NHibernate.Caches.Common/BinaryCacheSerializer.cs @@ -0,0 +1,30 @@ +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +namespace NHibernate.Caches.Common +{ + /// + public class BinaryCacheSerializer : ICacheSerializer + { + /// + public byte[] Serialize(object value) + { + var serializer = new BinaryFormatter(); + using (var stream = new MemoryStream()) + { + serializer.Serialize(stream, value); + return stream.ToArray(); + } + } + + /// + public object Deserialize(byte[] value) + { + var serializer = new BinaryFormatter(); + using (var stream = new MemoryStream(value)) + { + return serializer.Deserialize(stream); + } + } + } +} diff --git a/NHibernate.Caches.Common/ICacheSerializer.cs b/NHibernate.Caches.Common/ICacheSerializer.cs new file mode 100644 index 00000000..20b5204f --- /dev/null +++ b/NHibernate.Caches.Common/ICacheSerializer.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NHibernate.Caches.Common +{ + /// + /// Defines methods for serializing objects that will be stored in a distributed cache. + /// + public interface ICacheSerializer + { + /// + /// Serialize the object. + /// + /// The object to serialize. + /// The serialized object. + byte[] Serialize(object value); + + /// + /// Deserialize the object. + /// + /// The data of the object to deserialize. + /// The deserialized object. + object Deserialize(byte[] data); + } +} diff --git a/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj b/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj new file mode 100644 index 00000000..0ae609c5 --- /dev/null +++ b/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj @@ -0,0 +1,29 @@ + + + + NHibernate.Caches.Common + NHibernate.Caches.Common + Common types of NHibernate.Caches providers. + netstandard2.0 + True + ..\NHibernate.Caches.snk + true + + + NETFX;$(DefineConstants) + + + + + + + + + + ./NHibernate.Caches.readme.md + + + ./NHibernate.Caches.license.txt + + + diff --git a/NHibernate.Caches.Everything.sln b/NHibernate.Caches.Everything.sln index e49cda03..7448ffcb 100644 --- a/NHibernate.Caches.Everything.sln +++ b/NHibernate.Caches.Everything.sln @@ -68,6 +68,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHibernate.Caches.CoreDistr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHibernate.Caches.CoreDistributedCache.Memcached", "CoreDistributedCache\NHibernate.Caches.CoreDistributedCache.Memcached\NHibernate.Caches.CoreDistributedCache.Memcached.csproj", "{CB7BEB99-1964-4D83-86AD-100439E04186}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHibernate.Caches.Common", "NHibernate.Caches.Common\NHibernate.Caches.Common.csproj", "{15C72C05-4976-4401-91CB-31EF769AADD7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHibernate.Caches.Util.JsonSerializer", "Util\NHibernate.Caches.Util.JsonSerializer\NHibernate.Caches.Util.JsonSerializer.csproj", "{2327C330-0CE6-4F58-96A3-013BEFDFCCEB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHibernate.Caches.Util.JsonSerializer.Tests", "Util\NHibernate.Caches.Util.JsonSerializer.Tests\NHibernate.Caches.Util.JsonSerializer.Tests.csproj", "{26EAA903-63F7-41B1-9197-5944CEBF00C2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -174,6 +180,18 @@ Global {CB7BEB99-1964-4D83-86AD-100439E04186}.Debug|Any CPU.Build.0 = Debug|Any CPU {CB7BEB99-1964-4D83-86AD-100439E04186}.Release|Any CPU.ActiveCfg = Release|Any CPU {CB7BEB99-1964-4D83-86AD-100439E04186}.Release|Any CPU.Build.0 = Release|Any CPU + {15C72C05-4976-4401-91CB-31EF769AADD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {15C72C05-4976-4401-91CB-31EF769AADD7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {15C72C05-4976-4401-91CB-31EF769AADD7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {15C72C05-4976-4401-91CB-31EF769AADD7}.Release|Any CPU.Build.0 = Release|Any CPU + {2327C330-0CE6-4F58-96A3-013BEFDFCCEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2327C330-0CE6-4F58-96A3-013BEFDFCCEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2327C330-0CE6-4F58-96A3-013BEFDFCCEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2327C330-0CE6-4F58-96A3-013BEFDFCCEB}.Release|Any CPU.Build.0 = Release|Any CPU + {26EAA903-63F7-41B1-9197-5944CEBF00C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {26EAA903-63F7-41B1-9197-5944CEBF00C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26EAA903-63F7-41B1-9197-5944CEBF00C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {26EAA903-63F7-41B1-9197-5944CEBF00C2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -204,5 +222,8 @@ Global {63AD02AD-1F35-4341-A4C7-7EAEA238F08C} = {9BC335BB-4F31-44B4-9C2D-5A97B4742675} {03A80D4B-6C72-4F31-8680-DD6119E34CDF} = {9BC335BB-4F31-44B4-9C2D-5A97B4742675} {CB7BEB99-1964-4D83-86AD-100439E04186} = {9BC335BB-4F31-44B4-9C2D-5A97B4742675} + {15C72C05-4976-4401-91CB-31EF769AADD7} = {9BC335BB-4F31-44B4-9C2D-5A97B4742675} + {2327C330-0CE6-4F58-96A3-013BEFDFCCEB} = {9BC335BB-4F31-44B4-9C2D-5A97B4742675} + {26EAA903-63F7-41B1-9197-5944CEBF00C2} = {55271617-8CB8-4225-B338-069033160497} EndGlobalSection EndGlobal diff --git a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs new file mode 100644 index 00000000..a30abdff --- /dev/null +++ b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Newtonsoft.Json.Serialization; +using NHibernate.Caches.Common; +using NHibernate.Caches.Common.Tests; +using NHibernate.Intercept; +using NHibernate.Properties; +using NUnit.Framework; + +namespace NHibernate.Caches.Util.JsonSerializer.Tests +{ + [TestFixture] + public class JsonCacheSerializerFixture : CacheSerializerFixture + { + protected override Func SerializerProvider => CreateDefaultSerializer; + + [Test] + public void TestStrictSerialization() + { + var serializer = new JsonCacheSerializer(); + Assert.Throws(() => serializer.Serialize(new CustomEntity {Id = 10}), + "Non standard types should be registered explicitly"); + } + + [Test] + public void TestAliasNames() + { + var original = new object[] + { + (short) 1, + (ushort) 2, + 3, + (uint) 4, + (long) 5, + (ulong) 6, + (sbyte) 7, + (byte) 8, + 9.1m, + 10.2f, + 11.3, + Guid.Empty, + 'a', + TimeSpan.FromTicks(1234), + DateTimeOffset.FromUnixTimeSeconds(100), + new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), + new byte[] {12}, + new List{13}, + new Hashtable{{14, 14}}, + // TODO: add missing NH types when upgraded to 5.2 + new UnfetchedLazyProperty(), + new UnknownBackrefProperty() + }; + var expectedJson = + "{\"$t\":\"oa\",\"$vs\":[" + + "{\"$t\":\"s\",\"$v\":1}," + + "{\"$t\":\"us\",\"$v\":2}," + + "{\"$t\":\"i\",\"$v\":3}," + + "{\"$t\":\"ui\",\"$v\":4}," + + "5," + + "{\"$t\":\"ul\",\"$v\":6}," + + "{\"$t\":\"sb\",\"$v\":7}," + + "{\"$t\":\"b\",\"$v\":8}," + + "{\"$t\":\"d\",\"$v\":9.1}," + + "{\"$t\":\"f\",\"$v\":10.2}," + + "11.3," + + "{\"$t\":\"g\",\"$v\":\"00000000-0000-0000-0000-000000000000\"}," + + "{\"$t\":\"c\",\"$v\":\"a\"}," + + "{\"$t\":\"ts\",\"$v\":\"00:00:00.0001234\"}," + + "{\"$t\":\"do\",\"$v\":\"1970-01-01T00:01:40+00:00\"}," + + "\"2000-01-01T00:00:00Z\"," + + "{\"$t\":\"ba\",\"$v\":\"DA==\"}," + + "{\"$t\":\"lo\",\"$vs\":[{\"$t\":\"i\",\"$v\":13}]}," + + "{\"$t\":\"ht\",\"i:14\":{\"$t\":\"i\",\"$v\":14}}," + + "{\"$t\":\"up\"}," + + "{\"$t\":\"ub\"}" + + "]}"; + var data = DefaultSerializer.Serialize(original); + var json = Encoding.UTF8.GetString(data); + Assert.That(json, Is.EqualTo(expectedJson)); + + var copy = DefaultSerializer.Deserialize(data); + + AssertEqual(original, copy); + } + + private ICacheSerializer CreateDefaultSerializer() + { + var serializer = new JsonCacheSerializer(); + serializer.RegisterType(typeof(CustomEntity), "cue"); + // Because of the type converter attribute, the default resolved json contract for NullableInt32 is JsonStringContract. + serializer.RegisterType(typeof(NullableInt32), "nint", jsonContract => + { + // Use fields instead of properties + var contract = (JsonObjectContract) jsonContract; + contract.Properties.Clear(); + var properties = typeof(NullableInt32).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Select(f => new JsonProperty + { + PropertyName = f.Name, + PropertyType = f.FieldType, + DeclaringType = f.DeclaringType, + ValueProvider = new ReflectionValueProvider(f), + AttributeProvider = new ReflectionAttributeProvider(f), + Readable = true, + Writable = true + + }); + foreach (var property in properties) + { + contract.Properties.AddProperty(property); + } + }, typeof(JsonObjectContract)); + return serializer; + } + } +} diff --git a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/NHibernate.Caches.Util.JsonSerializer.Tests.csproj b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/NHibernate.Caches.Util.JsonSerializer.Tests.csproj new file mode 100644 index 00000000..3ee228ec --- /dev/null +++ b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/NHibernate.Caches.Util.JsonSerializer.Tests.csproj @@ -0,0 +1,28 @@ + + + + NHibernate.Caches.Util.JsonSerializer.Tests + Unit tests for json serializer. + net461;netcoreapp2.0 + true + + + NETFX;$(DefineConstants) + + + Exe + false + + + + + + + + + + + + + + diff --git a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/Program.cs b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/Program.cs new file mode 100644 index 00000000..b20736be --- /dev/null +++ b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/Program.cs @@ -0,0 +1,12 @@ +#if !NETFX +namespace NHibernate.Caches.Util.JsonSerializer.Tests +{ + public class Program + { + public static int Main(string[] args) + { + return new NUnitLite.AutoRun(typeof(Program).Assembly).Execute(args); + } + } +} +#endif diff --git a/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs b/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs new file mode 100644 index 00000000..3f17b5e7 --- /dev/null +++ b/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs @@ -0,0 +1,448 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using NHibernate.Cache; +using NHibernate.Cache.Entry; +using NHibernate.Caches.Common; +using NHibernate.Intercept; +using NHibernate.Properties; +using NHibernate.Type; +using NHibernate.UserTypes; +using Serializer = Newtonsoft.Json.JsonSerializer; + +namespace NHibernate.Caches.Util.JsonSerializer +{ + /// + /// A serializer that uses Json.Net to serialize the data that will be stored in a distributed cache. + /// If the cached have a method which does + /// not yield only basic value types or array of them, their return type has to be registered explicitly + /// by method. + /// + public class JsonCacheSerializer : ICacheSerializer + { + private const string TypeMetadataName = "$type"; + private const string ShortTypeMetadataName = "$t"; + private const string ValueMetadataName = "$value"; + private const string ShortValueMetadataName = "$v"; + private const string ValuesMetadataName = "$values"; + private const string ShortValuesMetadataName = "$vs"; + + /// + /// Types that are serialized as number or string and need the type metadata property to be correctly deserialized. + /// + /// + /// When deserializing an array of objects all numbers will be deserialized as double or long by default. + /// In order to prevent that we have to add the type metadata so that Json.Net will correctly deserialize + /// the number to the original type. + /// + private static readonly Dictionary ExplicitTypes = new Dictionary + { + // Serialized as number + {typeof(short), "s"}, + {typeof(ushort), "us"}, + {typeof(int), "i"}, + {typeof(uint), "ui"}, + {typeof(ulong), "ul"}, + {typeof(sbyte), "sb"}, + {typeof(byte), "b"}, + {typeof(decimal), "d"}, + {typeof(float), "f"}, + // Serialized as string + {typeof(Guid), "g"}, + {typeof(char), "c"}, + {typeof(TimeSpan), "ts"}, + {typeof(DateTimeOffset), "do"}, + {typeof(byte[]), "ba"} + }; + + // The types that are allowed to be serialized and deserialized in order to prevent + // exposing any security vulnerability as we are not using TypeNameHandling.None + private static readonly Dictionary TypeAliases = + new Dictionary(ExplicitTypes) + { + // Added for completeness, they will never be requested by Json.NET + {typeof(long), "l"}, + {typeof(double), "db"}, + {typeof(DateTime), "dt"}, + + // Used by NHibernate + {typeof(object[]), "oa"}, + {typeof(List), "lo"}, + {typeof(Hashtable), "ht"}, + {typeof(CacheEntry), "ce"}, + {typeof(CacheLock), "cl"}, + {typeof(CachedItem), "ci"}, + {typeof(CollectionCacheEntry), "cc"}, + {typeof(AnyType.ObjectTypeCacheEntry), "at"}, + {typeof(UnfetchedLazyProperty), "up"}, + {typeof(UnknownBackrefProperty), "ub"} + }; + + private readonly Serializer _serializer; + private readonly ExplicitSerializationBinder _serializationBinder = new ExplicitSerializationBinder(); + private readonly CustomDefaultContractResolver _contractResolver = new CustomDefaultContractResolver(); + + /// + public JsonCacheSerializer() + { + var settings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto, + DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind, + Formatting = Formatting.None, + SerializationBinder = _serializationBinder, + ContractResolver = _contractResolver + }; + settings.Converters.Add(new ExplicitTypesConverter()); + // Setup the Hashtable serialization + var contract = settings.ContractResolver.ResolveContract(typeof(Hashtable)); + contract.Converter = new HashtableConverter(); + contract.OnDeserializedCallbacks.Add((o, context) => HashtableConverter.OnDeserialized(o, _serializer)); + + _serializer = Serializer.Create(settings); + } + + /// + /// Register a type that is allowed to be serialized with an alias. + /// + /// The type allowed to be serialized. + /// The shorten name of the type. + public void RegisterType(System.Type type, string alias) + { + RegisterType(type, alias, null, null); + } + + /// + /// Register a type that is allowed to be serialized with an alias. + /// + /// The type allowed to be serialized. + /// The shorten name of the type. + /// The action to setup the of the type. + public void RegisterType(System.Type type, string alias, Action setupContractAction) + { + RegisterType(type, alias, setupContractAction, null); + } + + /// + /// Register a type that is allowed to be serialized with an alias. + /// + /// The type allowed to be serialized. + /// The shorten name of the type. + /// The action to setup the of the type. + /// The concrete type of a to use for the type. + public void RegisterType(System.Type type, string alias, Action setupContractAction, System.Type contractType) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + _serializationBinder.RegisterType(type, alias); + if (contractType != null) + { + _contractResolver.SetTypeContract(type, contractType); + } + setupContractAction?.Invoke(_serializer.ContractResolver.ResolveContract(type)); + } + + /// + public object Deserialize(byte[] value) + { + using (var reader = new CustomJsonTextReader(new StringReader(Encoding.UTF8.GetString(value)))) + { + return _serializer.Deserialize(reader); + } + } + + /// + public byte[] Serialize(object value) + { + using (var stringWriter = new StringWriter(new StringBuilder(256), CultureInfo.InvariantCulture)) + using (var writer = new CustomJsonTextWriter(stringWriter)) + { + writer.Formatting = _serializer.Formatting; + _serializer.Serialize(writer, value, typeof(object)); + return Encoding.UTF8.GetBytes(stringWriter.ToString()); + } + } + + /// + /// Allows to override the default that will be created for a given type. + /// + private class CustomDefaultContractResolver : DefaultContractResolver + { + private static readonly Dictionary> + ContractCreators = new Dictionary> + { + {typeof(JsonObjectContract), (r, t) => r.CreateObjectContract(t)}, + {typeof(JsonArrayContract), (r, t) => r.CreateArrayContract(t)}, + {typeof(JsonDictionaryContract), (r, t) => r.CreateDictionaryContract(t)}, + {typeof(JsonDynamicContract), (r, t) => r.CreateDynamicContract(t)}, + {typeof(JsonISerializableContract), (r, t) => r.CreateISerializableContract(t)}, + {typeof(JsonLinqContract), (r, t) => r.CreateLinqContract(t)}, + {typeof(JsonPrimitiveContract), (r, t) => r.CreatePrimitiveContract(t)}, + {typeof(JsonStringContract), (r, t) => r.CreateStringContract(t)} + }; + + private readonly Dictionary _typeContracts = new Dictionary(); + + public void SetTypeContract(System.Type type, System.Type contractType) + { + if (!ContractCreators.ContainsKey(contractType)) + { + throw new InvalidOperationException( + $"Invalid json contract type {contractType}. List of valid json contract types: {string.Join(", ", ContractCreators.Keys)}"); + } + + if (_typeContracts.ContainsKey(type)) + { + throw new InvalidOperationException($"A json contract type was already set for type {type}"); + } + _typeContracts.Add(type, contractType); + } + + protected override JsonContract CreateContract(System.Type objectType) + { + return !_typeContracts.TryGetValue(objectType, out var contractType) + ? base.CreateContract(objectType) + : ContractCreators[contractType](this, objectType); + } + } + + /// + /// A serialization binder that allows only registered types to be serialized. + /// + private class ExplicitSerializationBinder : ISerializationBinder + { + private readonly Dictionary _typeAliases; + private readonly Dictionary _aliasTypes; + + public ExplicitSerializationBinder() + { + _typeAliases = new Dictionary(TypeAliases); + _aliasTypes = _typeAliases.ToDictionary(o => o.Value, o => o.Key); + } + + public void RegisterType(System.Type type, string alias) + { + if (string.IsNullOrEmpty(alias)) + { + alias = type.AssemblyQualifiedName; + } + + if (alias == null) + { + throw new ArgumentNullException(nameof(alias)); + } + + if (_typeAliases.ContainsKey(type)) + { + throw new InvalidOperationException($"Type {type} is already registered."); + } + + if (_aliasTypes.ContainsKey(alias)) + { + throw new InvalidOperationException($"Alias {alias} is already registered."); + } + + _typeAliases.Add(type, alias); + _aliasTypes.Add(alias, type); + } + + public void BindToName(System.Type serializedType, out string assemblyName, out string typeName) + { + if (!_typeAliases.TryGetValue(serializedType, out typeName)) + { + throw new InvalidOperationException( + $"Unknown type '{serializedType.AssemblyQualifiedName}', use JsonCacheSerializer.RegisterType method to register it."); + } + + assemblyName = null; + } + + public System.Type BindToType(string assemblyName, string typeName) + { + if (!_aliasTypes.TryGetValue(typeName, out var type)) + { + throw new InvalidOperationException( + $"Unknown type '{typeName}, {assemblyName}', use JsonCacheSerializer.RegisterType method to register it."); + } + + return type; + } + } + + /// + /// A converter that preserves the key type. is not + /// implemented because it is not called by the serializer when the is located on + /// a property of type , which can happen when wrapping the value in . + /// Instead, the method should be appended to the + /// of the . + /// + private class HashtableConverter : JsonConverter + { + /// + public override void WriteJson(JsonWriter writer, Hashtable value, Serializer serializer) + { + writer.WriteStartObject(); + writer.WritePropertyName(ShortTypeMetadataName); + writer.WriteValue(TypeAliases[typeof(Hashtable)]); + + foreach (DictionaryEntry entry in value) + { + var type = entry.Key.GetType(); + if (type == typeof(string)) + { + writer.WritePropertyName(entry.Key.ToString()); + serializer.Serialize(writer, entry.Value, typeof(object)); + } + else + { + serializer.SerializationBinder.BindToName(type, out var assemblyName, out var typeName); + writer.WritePropertyName(string.IsNullOrEmpty(assemblyName) + ? $"{typeName}:{JsonConvert.ToString(entry.Key)}" + : $"{typeName};{assemblyName}:{JsonConvert.ToString(entry.Key)}"); + serializer.Serialize(writer, entry.Value, typeof(object)); + } + } + writer.WriteEndObject(); + } + + /// + public override Hashtable ReadJson(JsonReader reader, System.Type objectType, Hashtable existingValue, bool hasExistingValue, + Serializer serializer) + { + throw new NotSupportedException(); + } + + public static void OnDeserialized(object o, Serializer serializer) + { + var hashtable = (Hashtable) o; + var keys = hashtable.Keys.Cast().ToList(); + foreach (var key in keys) + { + var index = key.IndexOf(':'); + if (index < 0) + { + // Key is a string + continue; + } + + var typeAssembly = key.Substring(0, index).Split(';'); + var type = serializer.SerializationBinder.BindToType( + typeAssembly.Length > 1 ? typeAssembly[1] : null, + typeAssembly[typeAssembly.Length - 1]); + var keyString = key.Substring(index + 1); + var keyValue = serializer.Deserialize(new StringReader(keyString), type); + + hashtable.Add(keyValue, hashtable[key]); + hashtable.Remove(key); + } + } + + /// + public override bool CanRead => false; + } + + /// + /// A json converter that adds the type metadata for . + /// + private class ExplicitTypesConverter : JsonConverter + { + public override bool CanConvert(System.Type objectType) + { + return ExplicitTypes.ContainsKey(objectType); + } + + public override bool CanRead => false; + + public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, Serializer serializer) + { + throw new NotSupportedException(); + } + + public override void WriteJson(JsonWriter writer, object value, Serializer serializer) + { + writer.WriteStartObject(); + writer.WritePropertyName(ShortTypeMetadataName); + var typeName = ExplicitTypes[value.GetType()]; + writer.WriteValue(typeName); + writer.WritePropertyName(ShortValueMetadataName); + writer.WriteValue(value); + writer.WriteEndObject(); + } + } + + #region Reader & Writer + + /// + /// Restores the metadata property names modified by . + /// + private class CustomJsonTextReader : JsonTextReader + { + public CustomJsonTextReader(TextReader reader) : base(reader) + { + } + + public override bool Read() + { + var hasToken = base.Read(); + if (!hasToken || TokenType != JsonToken.PropertyName || !(Value is string str)) + { + return hasToken; + } + + switch (str) + { + case ShortTypeMetadataName: + SetToken(JsonToken.PropertyName, TypeMetadataName); + break; + case ShortValueMetadataName: + SetToken(JsonToken.PropertyName, ValueMetadataName); + break; + case ShortValuesMetadataName: + SetToken(JsonToken.PropertyName, ValuesMetadataName); + break; + } + + return true; + } + } + + /// + /// Reduces the json size by shortening the metadata properties names. + /// + private class CustomJsonTextWriter : JsonTextWriter + { + public CustomJsonTextWriter(TextWriter textWriter) : base(textWriter) + { + } + + public override void WritePropertyName(string name, bool escape) + { + switch (name) + { + case TypeMetadataName: + name = ShortTypeMetadataName; + break; + case ValueMetadataName: + name = ShortValueMetadataName; + break; + case ValuesMetadataName: + name = ShortValuesMetadataName; + break; + } + + base.WritePropertyName(name, escape); + } + } + + #endregion + + } +} diff --git a/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj b/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj new file mode 100644 index 00000000..d4cc9334 --- /dev/null +++ b/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj @@ -0,0 +1,32 @@ + + + + NHibernate.Caches.Util.JsonSerializer + NHibernate.Caches.Util.JsonSerializer + Json.NET serializer for distributed NHibernate.Caches providers. + netstandard2.0 + True + ..\..\NHibernate.Caches.snk + true + + + NETFX;$(DefineConstants) + + + + + + + + + + + + + ./NHibernate.Caches.readme.md + + + ./NHibernate.Caches.license.txt + + + diff --git a/appveyor.yml b/appveyor.yml index 4182dcd3..abbe23fd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -102,7 +102,7 @@ test_script: $TestsFailed = $FALSE #netFx tests If ($env:TESTS -eq 'net') { - @('EnyimMemcached', 'Prevalence', 'RtMemoryCache', 'SysCache', 'SysCache2', 'CoreMemoryCache', 'CoreDistributedCache') | ForEach-Object { + @('EnyimMemcached', 'Prevalence', 'RtMemoryCache', 'SysCache', 'SysCache2', 'CoreMemoryCache', 'CoreDistributedCache', 'Common', 'Util.JsonSerializer') | ForEach-Object { nunit3-console (Join-Path $env:APPVEYOR_BUILD_FOLDER "$_\NHibernate.Caches.$_.Tests\bin\$env:CONFIGURATION\$env:NETTARGETFX\NHibernate.Caches.$_.Tests.dll") "--result=$_-NetTestResult.xml;format=AppVeyor" If ($LASTEXITCODE -ne 0) { $TestsFailed = $TRUE @@ -112,7 +112,7 @@ test_script: #core tests If ($env:TESTS -eq 'core') { - @('CoreMemoryCache', 'CoreDistributedCache', 'RtMemoryCache') | ForEach-Object { + @('CoreMemoryCache', 'CoreDistributedCache', 'RtMemoryCache', 'Common', 'Util.JsonSerializer') | ForEach-Object { dotnet (Join-Path $env:APPVEYOR_BUILD_FOLDER "$_\NHibernate.Caches.$_.Tests\bin\$env:CONFIGURATION\$env:CORETARGETFX\NHibernate.Caches.$_.Tests.dll") --labels=before --nocolor "--result=$_-CoreTestResult.xml" If ($LASTEXITCODE -ne 0) { $TestsFailed = $TRUE From 9128e5dee31f8b446c1ee22aaf3fc569fcf82f50 Mon Sep 17 00:00:00 2001 From: maca88 Date: Fri, 6 Jul 2018 19:37:13 +0200 Subject: [PATCH 2/6] Renamed ICacheSerializer to CacheSerializerBase, added net461 target and removed test for checking the json output --- .../BinaryCacheSerializerFixture.cs | 2 +- .../CacheSerializerFixture.cs | 4 +- .../BinaryCacheSerializer.cs | 6 +- ...heSerializer.cs => CacheSerializerBase.cs} | 8 +-- .../NHibernate.Caches.Common.csproj | 2 +- .../JsonCacheSerializerFixture.cs | 65 +------------------ .../JsonCacheSerializer.cs | 6 +- ...ibernate.Caches.Util.JsonSerializer.csproj | 2 +- 8 files changed, 17 insertions(+), 78 deletions(-) rename NHibernate.Caches.Common/{ICacheSerializer.cs => CacheSerializerBase.cs} (70%) diff --git a/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs b/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs index 9b4759df..73820abb 100644 --- a/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs +++ b/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs @@ -10,6 +10,6 @@ namespace NHibernate.Caches.Common.Tests [TestFixture] public class BinaryCacheSerializerFixture : CacheSerializerFixture { - protected override Func SerializerProvider => () => new BinaryCacheSerializer(); + protected override Func SerializerProvider => () => new BinaryCacheSerializer(); } } diff --git a/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs b/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs index eded5cc8..f21ee3f2 100644 --- a/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs +++ b/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs @@ -20,9 +20,9 @@ namespace NHibernate.Caches.Common.Tests [TestFixture] public abstract class CacheSerializerFixture { - protected abstract Func SerializerProvider { get; } + protected abstract Func SerializerProvider { get; } - protected ICacheSerializer DefaultSerializer { get; private set; } + protected CacheSerializerBase DefaultSerializer { get; private set; } [OneTimeSetUp] public void FixtureSetup() diff --git a/NHibernate.Caches.Common/BinaryCacheSerializer.cs b/NHibernate.Caches.Common/BinaryCacheSerializer.cs index b30139f8..f50589a9 100644 --- a/NHibernate.Caches.Common/BinaryCacheSerializer.cs +++ b/NHibernate.Caches.Common/BinaryCacheSerializer.cs @@ -4,10 +4,10 @@ namespace NHibernate.Caches.Common { /// - public class BinaryCacheSerializer : ICacheSerializer + public class BinaryCacheSerializer : CacheSerializerBase { /// - public byte[] Serialize(object value) + public override byte[] Serialize(object value) { var serializer = new BinaryFormatter(); using (var stream = new MemoryStream()) @@ -18,7 +18,7 @@ public byte[] Serialize(object value) } /// - public object Deserialize(byte[] value) + public override object Deserialize(byte[] value) { var serializer = new BinaryFormatter(); using (var stream = new MemoryStream(value)) diff --git a/NHibernate.Caches.Common/ICacheSerializer.cs b/NHibernate.Caches.Common/CacheSerializerBase.cs similarity index 70% rename from NHibernate.Caches.Common/ICacheSerializer.cs rename to NHibernate.Caches.Common/CacheSerializerBase.cs index 20b5204f..08862d00 100644 --- a/NHibernate.Caches.Common/ICacheSerializer.cs +++ b/NHibernate.Caches.Common/CacheSerializerBase.cs @@ -7,22 +7,22 @@ namespace NHibernate.Caches.Common { /// - /// Defines methods for serializing objects that will be stored in a distributed cache. + /// Base class for serializing objects that will be stored in a distributed cache. /// - public interface ICacheSerializer + public abstract class CacheSerializerBase { /// /// Serialize the object. /// /// The object to serialize. /// The serialized object. - byte[] Serialize(object value); + public abstract byte[] Serialize(object value); /// /// Deserialize the object. /// /// The data of the object to deserialize. /// The deserialized object. - object Deserialize(byte[] data); + public abstract object Deserialize(byte[] data); } } diff --git a/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj b/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj index 0ae609c5..7285c487 100644 --- a/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj +++ b/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj @@ -4,7 +4,7 @@ NHibernate.Caches.Common NHibernate.Caches.Common Common types of NHibernate.Caches providers. - netstandard2.0 + net461;netstandard2.0 True ..\NHibernate.Caches.snk true diff --git a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs index a30abdff..0b5820c1 100644 --- a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs +++ b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs @@ -16,7 +16,7 @@ namespace NHibernate.Caches.Util.JsonSerializer.Tests [TestFixture] public class JsonCacheSerializerFixture : CacheSerializerFixture { - protected override Func SerializerProvider => CreateDefaultSerializer; + protected override Func SerializerProvider => CreateDefaultSerializer; [Test] public void TestStrictSerialization() @@ -26,68 +26,7 @@ public void TestStrictSerialization() "Non standard types should be registered explicitly"); } - [Test] - public void TestAliasNames() - { - var original = new object[] - { - (short) 1, - (ushort) 2, - 3, - (uint) 4, - (long) 5, - (ulong) 6, - (sbyte) 7, - (byte) 8, - 9.1m, - 10.2f, - 11.3, - Guid.Empty, - 'a', - TimeSpan.FromTicks(1234), - DateTimeOffset.FromUnixTimeSeconds(100), - new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), - new byte[] {12}, - new List{13}, - new Hashtable{{14, 14}}, - // TODO: add missing NH types when upgraded to 5.2 - new UnfetchedLazyProperty(), - new UnknownBackrefProperty() - }; - var expectedJson = - "{\"$t\":\"oa\",\"$vs\":[" + - "{\"$t\":\"s\",\"$v\":1}," + - "{\"$t\":\"us\",\"$v\":2}," + - "{\"$t\":\"i\",\"$v\":3}," + - "{\"$t\":\"ui\",\"$v\":4}," + - "5," + - "{\"$t\":\"ul\",\"$v\":6}," + - "{\"$t\":\"sb\",\"$v\":7}," + - "{\"$t\":\"b\",\"$v\":8}," + - "{\"$t\":\"d\",\"$v\":9.1}," + - "{\"$t\":\"f\",\"$v\":10.2}," + - "11.3," + - "{\"$t\":\"g\",\"$v\":\"00000000-0000-0000-0000-000000000000\"}," + - "{\"$t\":\"c\",\"$v\":\"a\"}," + - "{\"$t\":\"ts\",\"$v\":\"00:00:00.0001234\"}," + - "{\"$t\":\"do\",\"$v\":\"1970-01-01T00:01:40+00:00\"}," + - "\"2000-01-01T00:00:00Z\"," + - "{\"$t\":\"ba\",\"$v\":\"DA==\"}," + - "{\"$t\":\"lo\",\"$vs\":[{\"$t\":\"i\",\"$v\":13}]}," + - "{\"$t\":\"ht\",\"i:14\":{\"$t\":\"i\",\"$v\":14}}," + - "{\"$t\":\"up\"}," + - "{\"$t\":\"ub\"}" + - "]}"; - var data = DefaultSerializer.Serialize(original); - var json = Encoding.UTF8.GetString(data); - Assert.That(json, Is.EqualTo(expectedJson)); - - var copy = DefaultSerializer.Deserialize(data); - - AssertEqual(original, copy); - } - - private ICacheSerializer CreateDefaultSerializer() + private CacheSerializerBase CreateDefaultSerializer() { var serializer = new JsonCacheSerializer(); serializer.RegisterType(typeof(CustomEntity), "cue"); diff --git a/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs b/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs index 3f17b5e7..a761ba4f 100644 --- a/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs +++ b/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs @@ -24,7 +24,7 @@ namespace NHibernate.Caches.Util.JsonSerializer /// not yield only basic value types or array of them, their return type has to be registered explicitly /// by method. /// - public class JsonCacheSerializer : ICacheSerializer + public class JsonCacheSerializer : CacheSerializerBase { private const string TypeMetadataName = "$type"; private const string ShortTypeMetadataName = "$t"; @@ -151,7 +151,7 @@ public void RegisterType(System.Type type, string alias, Action se } /// - public object Deserialize(byte[] value) + public override object Deserialize(byte[] value) { using (var reader = new CustomJsonTextReader(new StringReader(Encoding.UTF8.GetString(value)))) { @@ -160,7 +160,7 @@ public object Deserialize(byte[] value) } /// - public byte[] Serialize(object value) + public override byte[] Serialize(object value) { using (var stringWriter = new StringWriter(new StringBuilder(256), CultureInfo.InvariantCulture)) using (var writer = new CustomJsonTextWriter(stringWriter)) diff --git a/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj b/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj index d4cc9334..c4ecd578 100644 --- a/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj +++ b/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj @@ -4,7 +4,7 @@ NHibernate.Caches.Util.JsonSerializer NHibernate.Caches.Util.JsonSerializer Json.NET serializer for distributed NHibernate.Caches providers. - netstandard2.0 + net461;netstandard2.0 True ..\..\NHibernate.Caches.snk true From 4512ba109f07c7e409d115ef8d48923fbcb7f770 Mon Sep 17 00:00:00 2001 From: maca88 Date: Fri, 6 Jul 2018 19:43:51 +0200 Subject: [PATCH 3/6] Reduced the minimum Json.NET version to 10.0.1 --- ...te.Caches.Util.JsonSerializer.Tests.csproj | 2 +- .../JsonCacheSerializer.cs | 21 ++++++++++++------- ...ibernate.Caches.Util.JsonSerializer.csproj | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/NHibernate.Caches.Util.JsonSerializer.Tests.csproj b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/NHibernate.Caches.Util.JsonSerializer.Tests.csproj index 3ee228ec..d2952d80 100644 --- a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/NHibernate.Caches.Util.JsonSerializer.Tests.csproj +++ b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/NHibernate.Caches.Util.JsonSerializer.Tests.csproj @@ -19,7 +19,7 @@ - + diff --git a/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs b/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs index a761ba4f..69c7599f 100644 --- a/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs +++ b/Util/NHibernate.Caches.Util.JsonSerializer/JsonCacheSerializer.cs @@ -284,16 +284,17 @@ public System.Type BindToType(string assemblyName, string typeName) /// Instead, the method should be appended to the /// of the . /// - private class HashtableConverter : JsonConverter + private class HashtableConverter : JsonConverter { /// - public override void WriteJson(JsonWriter writer, Hashtable value, Serializer serializer) + public override void WriteJson(JsonWriter writer, object value, Serializer serializer) { writer.WriteStartObject(); writer.WritePropertyName(ShortTypeMetadataName); writer.WriteValue(TypeAliases[typeof(Hashtable)]); - foreach (DictionaryEntry entry in value) + var hashtable = (Hashtable) value; + foreach (DictionaryEntry entry in hashtable) { var type = entry.Key.GetType(); if (type == typeof(string)) @@ -314,12 +315,19 @@ public override void WriteJson(JsonWriter writer, Hashtable value, Serializer se } /// - public override Hashtable ReadJson(JsonReader reader, System.Type objectType, Hashtable existingValue, bool hasExistingValue, - Serializer serializer) + public override bool CanRead => false; + + /// + public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, Serializer serializer) { throw new NotSupportedException(); } + public override bool CanConvert(System.Type objectType) + { + return typeof(Hashtable) == objectType; + } + public static void OnDeserialized(object o, Serializer serializer) { var hashtable = (Hashtable) o; @@ -344,9 +352,6 @@ public static void OnDeserialized(object o, Serializer serializer) hashtable.Remove(key); } } - - /// - public override bool CanRead => false; } /// diff --git a/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj b/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj index c4ecd578..fcde23f7 100644 --- a/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj +++ b/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj @@ -19,7 +19,7 @@ - + From 50d900d2f006dd73a4f611c58e802d04b5ee7ec8 Mon Sep 17 00:00:00 2001 From: maca88 Date: Fri, 6 Jul 2018 22:06:45 +0200 Subject: [PATCH 4/6] Added nant build files and fixed appveyor --- .../NHibernate.Caches.Common.Tests.csproj | 7 +++++ NHibernate.Caches.Common.Tests/Program.cs | 12 ++++++++ .../NHibernate.Caches.Common.csproj | 1 + NHibernate.Caches.Common/default.build | 30 +++++++++++++++++++ ...ibernate.Caches.Util.JsonSerializer.csproj | 1 + Util/default.build | 30 +++++++++++++++++++ appveyor.yml | 19 +++++++++--- default.build | 2 ++ 8 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 NHibernate.Caches.Common.Tests/Program.cs create mode 100644 NHibernate.Caches.Common/default.build create mode 100644 Util/default.build diff --git a/NHibernate.Caches.Common.Tests/NHibernate.Caches.Common.Tests.csproj b/NHibernate.Caches.Common.Tests/NHibernate.Caches.Common.Tests.csproj index 203c1f28..cb462c13 100644 --- a/NHibernate.Caches.Common.Tests/NHibernate.Caches.Common.Tests.csproj +++ b/NHibernate.Caches.Common.Tests/NHibernate.Caches.Common.Tests.csproj @@ -9,6 +9,10 @@ NETFX;$(DefineConstants) + + Exe + false + @@ -16,6 +20,9 @@ + + + diff --git a/NHibernate.Caches.Common.Tests/Program.cs b/NHibernate.Caches.Common.Tests/Program.cs new file mode 100644 index 00000000..6b55c96f --- /dev/null +++ b/NHibernate.Caches.Common.Tests/Program.cs @@ -0,0 +1,12 @@ +#if !NETFX +namespace NHibernate.Caches.Common.Tests +{ + public class Program + { + public static int Main(string[] args) + { + return new NUnitLite.AutoRun(typeof(Program).Assembly).Execute(args); + } + } +} +#endif diff --git a/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj b/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj index 7285c487..c19ad6a3 100644 --- a/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj +++ b/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj @@ -14,6 +14,7 @@ + diff --git a/NHibernate.Caches.Common/default.build b/NHibernate.Caches.Common/default.build new file mode 100644 index 00000000..5d0165a2 --- /dev/null +++ b/NHibernate.Caches.Common/default.build @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj b/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj index fcde23f7..e6f9b4a9 100644 --- a/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj +++ b/Util/NHibernate.Caches.Util.JsonSerializer/NHibernate.Caches.Util.JsonSerializer.csproj @@ -14,6 +14,7 @@ + diff --git a/Util/default.build b/Util/default.build new file mode 100644 index 00000000..8eb7455b --- /dev/null +++ b/Util/default.build @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appveyor.yml b/appveyor.yml index abbe23fd..527878e4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -100,10 +100,18 @@ test_script: - ps: >- Invoke-Command -ScriptBlock { $TestsFailed = $FALSE + $target = If ($env:TESTS -eq 'net') {$env:NETTARGETFX} Else {$env:CORETARGETFX} + $projects = @{} + $projects.Add('Common', "NHibernate.Caches.Common.Tests\bin\$env:CONFIGURATION\$target\NHibernate.Caches.Common.Tests.dll") + $projects.Add('Util.JsonSerializer', "Util\NHibernate.Caches.Util.JsonSerializer.Tests\bin\$env:CONFIGURATION\$target\NHibernate.Caches.Util.JsonSerializer.Tests.dll") + #netFx tests If ($env:TESTS -eq 'net') { - @('EnyimMemcached', 'Prevalence', 'RtMemoryCache', 'SysCache', 'SysCache2', 'CoreMemoryCache', 'CoreDistributedCache', 'Common', 'Util.JsonSerializer') | ForEach-Object { - nunit3-console (Join-Path $env:APPVEYOR_BUILD_FOLDER "$_\NHibernate.Caches.$_.Tests\bin\$env:CONFIGURATION\$env:NETTARGETFX\NHibernate.Caches.$_.Tests.dll") "--result=$_-NetTestResult.xml;format=AppVeyor" + @('EnyimMemcached', 'Prevalence', 'RtMemoryCache', 'SysCache', 'SysCache2', 'CoreMemoryCache', 'CoreDistributedCache') | ForEach-Object { + $projects.Add($_, "$_\NHibernate.Caches.$_.Tests\bin\$env:CONFIGURATION\$target\NHibernate.Caches.$_.Tests.dll") + } + ForEach ($project in $projects.GetEnumerator()) { + nunit3-console (Join-Path $env:APPVEYOR_BUILD_FOLDER $project.Value) "--result=$($project.Name)-NetTestResult.xml;format=AppVeyor" If ($LASTEXITCODE -ne 0) { $TestsFailed = $TRUE } @@ -112,8 +120,11 @@ test_script: #core tests If ($env:TESTS -eq 'core') { - @('CoreMemoryCache', 'CoreDistributedCache', 'RtMemoryCache', 'Common', 'Util.JsonSerializer') | ForEach-Object { - dotnet (Join-Path $env:APPVEYOR_BUILD_FOLDER "$_\NHibernate.Caches.$_.Tests\bin\$env:CONFIGURATION\$env:CORETARGETFX\NHibernate.Caches.$_.Tests.dll") --labels=before --nocolor "--result=$_-CoreTestResult.xml" + @('CoreMemoryCache', 'CoreDistributedCache', 'RtMemoryCache') | ForEach-Object { + $projects.Add($_, "$_\NHibernate.Caches.$_.Tests\bin\$env:CONFIGURATION\$target\NHibernate.Caches.$_.Tests.dll") + } + ForEach ($project in $projects.GetEnumerator()) { + dotnet (Join-Path $env:APPVEYOR_BUILD_FOLDER $project.Value) --labels=before --nocolor "--result=$($project.Name)-CoreTestResult.xml" If ($LASTEXITCODE -ne 0) { $TestsFailed = $TRUE } diff --git a/default.build b/default.build index c1724a00..637138d7 100644 --- a/default.build +++ b/default.build @@ -7,6 +7,8 @@ + + From 855575931e104618f7647b3131f861a66070dd88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Wed, 5 Dec 2018 11:30:12 +0100 Subject: [PATCH 5/6] Update to NH5.2 And some cleanup --- .../BinaryCacheSerializerFixture.cs | 4 ---- .../CacheSerializerFixture.cs | 16 +++++++++++----- NHibernate.Caches.Common/CacheSerializerBase.cs | 8 +------- .../NHibernate.Caches.Common.csproj | 2 +- .../JsonCacheSerializerFixture.cs | 5 ----- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs b/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs index 73820abb..f27d7df1 100644 --- a/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs +++ b/NHibernate.Caches.Common.Tests/BinaryCacheSerializerFixture.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using NUnit.Framework; namespace NHibernate.Caches.Common.Tests diff --git a/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs b/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs index f21ee3f2..76d6f030 100644 --- a/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs +++ b/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs @@ -184,25 +184,31 @@ protected List CreateListOfObjects() protected CacheEntry CreateCacheEntry() { var types = GetNHibernateTypes(); - return new CacheEntry(types.Values.ToArray(), null, false, null, null, null); + return CacheEntry.Create(types.Values.ToArray(), null, false, null, null, null); } // TODO: make tests after upgraded to NHiberante 5.2 protected CollectionCacheEntry CreateCollectionCacheEntry() { - return new CollectionCacheEntry(null, null); + return CollectionCacheEntry.Create(null, null); } // TODO: make tests after upgraded to NHiberante 5.2 protected CacheLock CreateCacheLock() { - return new CacheLock(1234, 1, 5); + return new CacheLock + { + Timeout = 1234, Id = 1, Version = 5 + }; } // TODO: make tests after upgraded to NHiberante 5.2 protected CachedItem CreateCachedItem(object data) { - return new CachedItem(data, 111, 5); + return new CachedItem + { + Value = data, FreshTimestamp = 111, Version = 5 + }; } // TODO: make tests after upgraded to NHiberante 5.2 @@ -263,7 +269,7 @@ protected Dictionary GetNHibernateTypes() {NHibernateUtil.YesNo, true}, // TODO: enable after upgraded to NHiberante 5.2 //{NHibernateUtil.Class, typeof(IType)}, - {NHibernateUtil.ClassMetaType, entityName}, + {NHibernateUtil.MetaType, entityName}, {NHibernateUtil.Serializable, new CustomEntity {Id = 1}}, // TODO: enable after upgraded to NHiberante 5.2 //{NHibernateUtil.Object, new CustomEntity {Id = 10}}, diff --git a/NHibernate.Caches.Common/CacheSerializerBase.cs b/NHibernate.Caches.Common/CacheSerializerBase.cs index 08862d00..d056d449 100644 --- a/NHibernate.Caches.Common/CacheSerializerBase.cs +++ b/NHibernate.Caches.Common/CacheSerializerBase.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace NHibernate.Caches.Common +namespace NHibernate.Caches.Common { /// /// Base class for serializing objects that will be stored in a distributed cache. diff --git a/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj b/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj index c19ad6a3..5785748f 100644 --- a/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj +++ b/NHibernate.Caches.Common/NHibernate.Caches.Common.csproj @@ -17,7 +17,7 @@ - + diff --git a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs index 0b5820c1..ba6a049a 100644 --- a/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs +++ b/Util/NHibernate.Caches.Util.JsonSerializer.Tests/JsonCacheSerializerFixture.cs @@ -1,14 +1,9 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; using Newtonsoft.Json.Serialization; using NHibernate.Caches.Common; using NHibernate.Caches.Common.Tests; -using NHibernate.Intercept; -using NHibernate.Properties; using NUnit.Framework; namespace NHibernate.Caches.Util.JsonSerializer.Tests From 6ffa7def4141d6cf5934fee4a9a279177f7266a0 Mon Sep 17 00:00:00 2001 From: maca88 Date: Wed, 5 Dec 2018 19:28:19 +0100 Subject: [PATCH 6/6] Implemented all TODOs --- .../CacheSerializerFixture.cs | 194 ++++++++++++++++-- 1 file changed, 175 insertions(+), 19 deletions(-) diff --git a/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs b/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs index 76d6f030..15853266 100644 --- a/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs +++ b/NHibernate.Caches.Common.Tests/CacheSerializerFixture.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using System.Xml; +using System.Xml.Linq; using NHibernate.Cache; using NHibernate.Cache.Entry; using NHibernate.Engine; @@ -124,6 +125,150 @@ public void TestNullableInt32Type() AssertEqual(original, copy); } + [Test] + public void TestCacheEntry() + { + var original = CreateCacheEntry(); + var data = DefaultSerializer.Serialize(original); + var copy = (CacheEntry) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestCollectionCacheEntry() + { + var original = CreateCollectionCacheEntry(); + var data = DefaultSerializer.Serialize(original); + var copy = (CollectionCacheEntry) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestCacheLock() + { + var original = CreateCacheLock(); + var data = DefaultSerializer.Serialize(original); + var copy = (CacheLock) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestCachedItem() + { + // CacheEntry + var original = CreateCachedItem(CreateCacheEntry()); + var data = DefaultSerializer.Serialize(original); + var copy = (CachedItem) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + + // CollectionCacheEntry + original = CreateCachedItem(CreateCollectionCacheEntry()); + data = DefaultSerializer.Serialize(original); + copy = (CachedItem) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + [Test] + public void TestAnyTypeObjectTypeCacheEntry() + { + var original = CreateObjectTypeCacheEntry(); + var data = DefaultSerializer.Serialize(original); + var copy = (AnyType.ObjectTypeCacheEntry) DefaultSerializer.Deserialize(data); + AssertEqual(original, copy); + } + + protected void AssertEqual(CacheEntry original, CacheEntry copy) + { + Assert.That(copy.Version, Is.EqualTo(original.Version)); + Assert.That(copy.Version, Is.TypeOf(original.Version.GetType())); + Assert.That(copy.Subclass, Is.EqualTo(original.Subclass)); + Assert.That(copy.AreLazyPropertiesUnfetched, Is.EqualTo(original.AreLazyPropertiesUnfetched)); + for (var i = 0; i < copy.DisassembledState.Length; i++) + { + if (original.DisassembledState[i] == null) + { + Assert.That(copy.DisassembledState[i], Is.Null); + continue; + } + + Assert.That(copy.DisassembledState[i], Is.TypeOf(original.DisassembledState[i].GetType())); + if (original.DisassembledState[i] is AnyType.ObjectTypeCacheEntry originalAnyType) + { + var copyAnyType = (AnyType.ObjectTypeCacheEntry) copy.DisassembledState[i]; + AssertEqual(originalAnyType, copyAnyType); + } + else + { + Assert.That(copy.DisassembledState[i], Is.EqualTo(original.DisassembledState[i])); + } + } + } + + protected void AssertEqual(CachedItem original, CachedItem copy) + { + Assert.That(copy.Version, Is.EqualTo(original.Version)); + Assert.That(copy.Version, Is.TypeOf(original.Version.GetType())); + Assert.That(copy.Value, Is.TypeOf(original.Value.GetType())); + switch (original.Value) + { + case CacheEntry cacheEntry: + AssertEqual(cacheEntry, (CacheEntry) copy.Value); + break; + case CollectionCacheEntry colleectionCacheEntry: + AssertEqual(colleectionCacheEntry, (CollectionCacheEntry) copy.Value); + break; + default: + Assert.That(copy.Value, Is.EqualTo(original.Value)); + break; + } + Assert.That(copy.FreshTimestamp, Is.EqualTo(original.FreshTimestamp)); + } + + protected void AssertEqual(CollectionCacheEntry original, CollectionCacheEntry copy) + { + Assert.That(copy.State, Is.TypeOf(original.State.GetType())); + + var originalArray = original.State; + var copyArray = copy.State; + + for (var i = 0; i < copyArray.Length; i++) + { + if (originalArray[i] == null) + { + Assert.That(copyArray[i], Is.Null); + continue; + } + + Assert.That(copyArray[i], Is.TypeOf(originalArray[i].GetType())); + if (originalArray[i] is AnyType.ObjectTypeCacheEntry originalAnyType) + { + var copyAnyType = (AnyType.ObjectTypeCacheEntry) copyArray[i]; + AssertEqual(originalAnyType, copyAnyType); + } + else + { + Assert.That(copyArray[i], Is.EqualTo(originalArray[i])); + } + } + } + + protected void AssertEqual(CacheLock original, CacheLock copy) + { + Assert.That(copy.Version, Is.EqualTo(original.Version)); + Assert.That(copy.Version, Is.TypeOf(original.Version.GetType())); + Assert.That(copy.Id, Is.EqualTo(original.Id)); + Assert.That(copy.Multiplicity, Is.EqualTo(original.Multiplicity)); + Assert.That(copy.Timeout, Is.EqualTo(original.Timeout)); + Assert.That(copy.UnlockTimestamp, Is.EqualTo(original.UnlockTimestamp)); + Assert.That(copy.WasLockedConcurrently, Is.EqualTo(original.WasLockedConcurrently)); + } + + protected void AssertEqual(AnyType.ObjectTypeCacheEntry original, AnyType.ObjectTypeCacheEntry copy) + { + Assert.That(copy.Id, Is.EqualTo(original.Id)); + Assert.That(copy.EntityName, Is.EqualTo(original.EntityName)); + } + protected void AssertEqual(Hashtable original, Hashtable copy) { Assert.That(copy, Has.Count.EqualTo(original.Count)); @@ -159,6 +304,14 @@ protected void AssertEqual(object original, object copy) Assert.That(copy, Is.Null); return; } + + if (original is AnyType.ObjectTypeCacheEntry anyCacheEntry) + { + Assert.That(copy, Is.TypeOf()); + AssertEqual(anyCacheEntry, (AnyType.ObjectTypeCacheEntry) copy); + return; + } + Assert.That(copy, Is.TypeOf(original.GetType())); Assert.That(copy, Is.EqualTo(original)); } @@ -180,20 +333,25 @@ protected List CreateListOfObjects() return GetAllNHibernateTypeValues().ToList(); } - // TODO: make tests after upgraded to NHiberante 5.2 protected CacheEntry CreateCacheEntry() { - var types = GetNHibernateTypes(); - return CacheEntry.Create(types.Values.ToArray(), null, false, null, null, null); + return new CacheEntry + { + DisassembledState = GetAllNHibernateTypeValues(), + Version = 1, + Subclass = "TestClass", + AreLazyPropertiesUnfetched = true + }; } - // TODO: make tests after upgraded to NHiberante 5.2 protected CollectionCacheEntry CreateCollectionCacheEntry() { - return CollectionCacheEntry.Create(null, null); + return new CollectionCacheEntry + { + State = GetAllNHibernateTypeValues() + }; } - // TODO: make tests after upgraded to NHiberante 5.2 protected CacheLock CreateCacheLock() { return new CacheLock @@ -202,7 +360,6 @@ protected CacheLock CreateCacheLock() }; } - // TODO: make tests after upgraded to NHiberante 5.2 protected CachedItem CreateCachedItem(object data) { return new CachedItem @@ -211,10 +368,13 @@ protected CachedItem CreateCachedItem(object data) }; } - // TODO: make tests after upgraded to NHiberante 5.2 protected AnyType.ObjectTypeCacheEntry CreateObjectTypeCacheEntry() { - return null; + return new AnyType.ObjectTypeCacheEntry + { + EntityName = "Test", + Id = 1 + }; } [Serializable] @@ -236,8 +396,7 @@ protected Dictionary GetNHibernateTypes() {NHibernateUtil.Boolean, true}, {NHibernateUtil.Byte, (byte) 1}, {NHibernateUtil.Character, 'a'}, - // TODO: enable after upgraded to NHiberante 5.2 - //{NHibernateUtil.CultureInfo, CultureInfo.CurrentCulture}, + {NHibernateUtil.CultureInfo, CultureInfo.CurrentCulture}, {NHibernateUtil.DateTime, DateTime.Now}, {NHibernateUtil.DateTimeNoMs, DateTime.Now}, {NHibernateUtil.LocalDateTime, DateTime.Now}, @@ -267,17 +426,14 @@ protected Dictionary GetNHibernateTypes() {NHibernateUtil.DbTimestamp, DateTime.Now}, {NHibernateUtil.TrueFalse, false}, {NHibernateUtil.YesNo, true}, - // TODO: enable after upgraded to NHiberante 5.2 - //{NHibernateUtil.Class, typeof(IType)}, + {NHibernateUtil.Class, typeof(IType)}, {NHibernateUtil.MetaType, entityName}, {NHibernateUtil.Serializable, new CustomEntity {Id = 1}}, - // TODO: enable after upgraded to NHiberante 5.2 - //{NHibernateUtil.Object, new CustomEntity {Id = 10}}, + {NHibernateUtil.Object, new CustomEntity {Id = 10}}, {NHibernateUtil.AnsiChar, 'a'}, - // TODO: enable after upgraded to NHiberante 5.2 - //{NHibernateUtil.XmlDoc, xmlDoc}, - //{NHibernateUtil.XDoc, XDocument.Parse("XDoc")}, - //{NHibernateUtil.Uri, new Uri("http://test.com")} + {NHibernateUtil.XmlDoc, xmlDoc}, + {NHibernateUtil.XDoc, XDocument.Parse("XDoc")}, + {NHibernateUtil.Uri, new Uri("http://test.com")} }; }