diff --git a/src/NHibernate.Test/Async/CacheTest/SerializationFixture.cs b/src/NHibernate.Test/Async/CacheTest/SerializationFixture.cs new file mode 100644 index 00000000000..274dc727bbf --- /dev/null +++ b/src/NHibernate.Test/Async/CacheTest/SerializationFixture.cs @@ -0,0 +1,377 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Xml; +using System.Xml.Linq; +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.Test.CacheTest +{ + using System.Threading.Tasks; + [TestFixture] + public class SerializationFixtureAsync + { + private static readonly List KnownTypes = new List + { + typeof(AnyType.ObjectTypeCacheEntry), + typeof(DateTimeOffset), + typeof(TimeSpan), + typeof(UnfetchedLazyProperty), + typeof(UnknownBackrefProperty), + typeof(object[]), + typeof(CacheEntry), + typeof(CacheLock), + typeof(CachedItem), + typeof(CollectionCacheEntry) + }; + + [Test] + public async Task TestCacheEntrySerializationAsync() + { + var item = CreateCacheEntry(); + var copy = await (TestDataContractSerializerAsync(item)); + CheckCacheEntry(item, copy); + + copy = TestBinaryFormatter(item); + CheckCacheEntry(item, copy); + } + + [Test] + public async Task TestCollectionCacheEntrySerializationAsync() + { + var item = CreateCollectionCacheEntry(); + var copy = await (TestDataContractSerializerAsync(item)); + CheckCollectionCacheEntry(item, copy); + + copy = TestBinaryFormatter(item); + CheckCollectionCacheEntry(item, copy); + } + + [Test] + public async Task TestCachedItemSerializationAsync() + { + // CacheEntry + var item = CreateCachedItem(CreateCacheEntry()); + var copy = await (TestDataContractSerializerAsync(item)); + CheckCachedItem(item, copy); + + copy = TestBinaryFormatter(item); + CheckCachedItem(item, copy); + + // CollectionCacheEntry + item = CreateCachedItem(CreateCollectionCacheEntry()); + copy = await (TestDataContractSerializerAsync(item)); + CheckCachedItem(item, copy); + + copy = TestBinaryFormatter(item); + CheckCachedItem(item, copy); + } + + [Test] + public async Task TestCacheLockSerializationAsync() + { + var item = new CacheLock + { + Version = 3.5m, + Id = 100, + Timeout = 999, + UnlockTimestamp = 444, + Multiplicity = 5, + WasLockedConcurrently = false + }; + var copy = await (TestDataContractSerializerAsync(item)); + CheckCacheLock(item, copy); + + copy = TestBinaryFormatter(item); + CheckCacheLock(item, copy); + } + + [Test] + public async Task TestAnyTypeObjectTypeCacheEntrySerializationAsync() + { + var item = CreateObjectTypeCacheEntry(); + var copy = await (TestDataContractSerializerAsync(item)); + CheckObjectTypeCacheEntry(item, copy); + + copy = TestBinaryFormatter(item); + CheckObjectTypeCacheEntry(item, copy); + } + + + [Serializable] + public class MyEntity + { + public int Id { get; set; } + } + + private CachedItem CreateCachedItem(object value) + { + return new CachedItem + { + Version = 55L, + Value = value, + FreshTimestamp = 500 + }; + } + + private CacheEntry CreateCacheEntry() + { + return new CacheEntry + { + DisassembledState = GetAllKnownTypeValues(), + Version = 55, + Subclass = "Test", + AreLazyPropertiesUnfetched = true + }; + } + + private CollectionCacheEntry CreateCollectionCacheEntry() + { + return new CollectionCacheEntry + { + State = GetAllKnownTypeValues() + }; + } + + private object[] GetAllKnownTypeValues() + { + var entityName = nameof(MyEntity); + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml("XmlDoc"); + var types = 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'}, + {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}, + {NHibernateUtil.Class, typeof(IType)}, + {NHibernateUtil.ClassMetaType, entityName}, + {NHibernateUtil.Serializable, new MyEntity {Id = 1}}, + {NHibernateUtil.Object, new MyEntity {Id = 10}}, + {NHibernateUtil.AnsiChar, 'a'}, + {NHibernateUtil.XmlDoc, xmlDoc}, + {NHibernateUtil.XDoc, XDocument.Parse("XDoc")}, + {NHibernateUtil.Uri, new Uri("http://test.com")} + }; + + var sessionImpl = Substitute.For(); + sessionImpl.BestGuessEntityName(Arg.Any()).Returns(o => o[0].GetType().Name); + sessionImpl.GetContextEntityIdentifier(Arg.Is(o => o is MyEntity)).Returns(o => ((MyEntity) o[0]).Id); + return TypeHelper.Disassemble( + types.Values.ToArray(), + types.Keys.Cast().ToArray(), + null, + sessionImpl, + null) + .Concat( + new[] + { + LazyPropertyInitializer.UnfetchedProperty, + BackrefPropertyAccessor.Unknown + }) + .ToArray(); + } + + private AnyType.ObjectTypeCacheEntry CreateObjectTypeCacheEntry() + { + return new AnyType.ObjectTypeCacheEntry + { + Id = 100, + EntityName = "Test" + }; + } + + private void CheckCacheEntry(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++) + { + 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]; + CheckObjectTypeCacheEntry(originalAnyType, copyAnyType); + } + else + { + Assert.That(copy.DisassembledState[i], Is.EqualTo(original.DisassembledState[i])); + } + } + } + + private void CheckCachedItem(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: + CheckCacheEntry(cacheEntry, (CacheEntry) copy.Value); + break; + case CollectionCacheEntry colleectionCacheEntry: + CheckCollectionCacheEntry(colleectionCacheEntry, (CollectionCacheEntry) copy.Value); + break; + default: + Assert.That(copy.Value, Is.EqualTo(original.Value)); + break; + } + Assert.That(copy.FreshTimestamp, Is.EqualTo(original.FreshTimestamp)); + } + + private void CheckCollectionCacheEntry(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++) + { + Assert.That(copyArray[i], Is.TypeOf(originalArray[i].GetType())); + if (originalArray[i] is AnyType.ObjectTypeCacheEntry originalAnyType) + { + var copyAnyType = (AnyType.ObjectTypeCacheEntry) copyArray[i]; + CheckObjectTypeCacheEntry(originalAnyType, copyAnyType); + } + else + { + Assert.That(copyArray[i], Is.EqualTo(originalArray[i])); + } + } + } + + private void CheckCacheLock(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)); + } + + private void CheckObjectTypeCacheEntry(AnyType.ObjectTypeCacheEntry original, AnyType.ObjectTypeCacheEntry copy) + { + Assert.That(copy.Id, Is.EqualTo(original.Id)); + Assert.That(copy.EntityName, Is.EqualTo(original.EntityName)); + } + + private static async Task TestDataContractSerializerAsync(T obj) + { + var xml = await (DataContractSerializerToXmlAsync(obj)); + obj = DataContractSerializerFromXml(xml); + Assert.That(xml, Is.EqualTo(await (DataContractSerializerToXmlAsync(obj)))); + return obj; + } + + private static T TestBinaryFormatter(T obj) + { + var bytes = BinaryFormatterToBinary(obj); + obj = BinaryFormatterFromBinary(bytes); + Assert.That(bytes, Is.EqualTo(BinaryFormatterToBinary(obj))); + return obj; + } + + private static async Task DataContractSerializerToXmlAsync(T obj) + { + using (var memoryStream = new MemoryStream()) + using (var reader = new StreamReader(memoryStream)) + { + var serializer = new DataContractSerializer(typeof(T), KnownTypes); + serializer.WriteObject(memoryStream, obj); + memoryStream.Position = 0; + return await (reader.ReadToEndAsync()); + } + } + + private static T DataContractSerializerFromXml(string xml) + { + using (var stream = new MemoryStream()) + { + var data = Encoding.UTF8.GetBytes(xml); + stream.Write(data, 0, data.Length); + stream.Position = 0; + var deserializer = new DataContractSerializer(typeof(T), KnownTypes); + return (T) deserializer.ReadObject(stream); + } + } + + private static byte[] BinaryFormatterToBinary(T obj) + { + var serializer = new BinaryFormatter(); + using (var stream = new MemoryStream()) + { + serializer.Serialize(stream, obj); + return stream.ToArray(); + } + } + + private static T BinaryFormatterFromBinary(byte[] value) + { + var serializer = new BinaryFormatter(); + using (var stream = new MemoryStream(value)) + { + return (T)serializer.Deserialize(stream); + } + } + } +} diff --git a/src/NHibernate.Test/CacheTest/SerializationFixture.cs b/src/NHibernate.Test/CacheTest/SerializationFixture.cs new file mode 100644 index 00000000000..e133678fe65 --- /dev/null +++ b/src/NHibernate.Test/CacheTest/SerializationFixture.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Xml; +using System.Xml.Linq; +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.Test.CacheTest +{ + [TestFixture] + public class SerializationFixture + { + private static readonly List KnownTypes = new List + { + typeof(AnyType.ObjectTypeCacheEntry), + typeof(DateTimeOffset), + typeof(TimeSpan), + typeof(UnfetchedLazyProperty), + typeof(UnknownBackrefProperty), + typeof(object[]), + typeof(CacheEntry), + typeof(CacheLock), + typeof(CachedItem), + typeof(CollectionCacheEntry) + }; + + [Test] + public void TestCacheEntrySerialization() + { + var item = CreateCacheEntry(); + var copy = TestDataContractSerializer(item); + CheckCacheEntry(item, copy); + + copy = TestBinaryFormatter(item); + CheckCacheEntry(item, copy); + } + + [Test] + public void TestCollectionCacheEntrySerialization() + { + var item = CreateCollectionCacheEntry(); + var copy = TestDataContractSerializer(item); + CheckCollectionCacheEntry(item, copy); + + copy = TestBinaryFormatter(item); + CheckCollectionCacheEntry(item, copy); + } + + [Test] + public void TestCachedItemSerialization() + { + // CacheEntry + var item = CreateCachedItem(CreateCacheEntry()); + var copy = TestDataContractSerializer(item); + CheckCachedItem(item, copy); + + copy = TestBinaryFormatter(item); + CheckCachedItem(item, copy); + + // CollectionCacheEntry + item = CreateCachedItem(CreateCollectionCacheEntry()); + copy = TestDataContractSerializer(item); + CheckCachedItem(item, copy); + + copy = TestBinaryFormatter(item); + CheckCachedItem(item, copy); + } + + [Test] + public void TestCacheLockSerialization() + { + var item = new CacheLock + { + Version = 3.5m, + Id = 100, + Timeout = 999, + UnlockTimestamp = 444, + Multiplicity = 5, + WasLockedConcurrently = false + }; + var copy = TestDataContractSerializer(item); + CheckCacheLock(item, copy); + + copy = TestBinaryFormatter(item); + CheckCacheLock(item, copy); + } + + [Test] + public void TestAnyTypeObjectTypeCacheEntrySerialization() + { + var item = CreateObjectTypeCacheEntry(); + var copy = TestDataContractSerializer(item); + CheckObjectTypeCacheEntry(item, copy); + + copy = TestBinaryFormatter(item); + CheckObjectTypeCacheEntry(item, copy); + } + + + [Serializable] + public class MyEntity + { + public int Id { get; set; } + } + + private CachedItem CreateCachedItem(object value) + { + return new CachedItem + { + Version = 55L, + Value = value, + FreshTimestamp = 500 + }; + } + + private CacheEntry CreateCacheEntry() + { + return new CacheEntry + { + DisassembledState = GetAllKnownTypeValues(), + Version = 55, + Subclass = "Test", + AreLazyPropertiesUnfetched = true + }; + } + + private CollectionCacheEntry CreateCollectionCacheEntry() + { + return new CollectionCacheEntry + { + State = GetAllKnownTypeValues() + }; + } + + private object[] GetAllKnownTypeValues() + { + var entityName = nameof(MyEntity); + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml("XmlDoc"); + var types = 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'}, + {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}, + {NHibernateUtil.Class, typeof(IType)}, + {NHibernateUtil.ClassMetaType, entityName}, + {NHibernateUtil.Serializable, new MyEntity {Id = 1}}, + {NHibernateUtil.Object, new MyEntity {Id = 10}}, + {NHibernateUtil.AnsiChar, 'a'}, + {NHibernateUtil.XmlDoc, xmlDoc}, + {NHibernateUtil.XDoc, XDocument.Parse("XDoc")}, + {NHibernateUtil.Uri, new Uri("http://test.com")} + }; + + var sessionImpl = Substitute.For(); + sessionImpl.BestGuessEntityName(Arg.Any()).Returns(o => o[0].GetType().Name); + sessionImpl.GetContextEntityIdentifier(Arg.Is(o => o is MyEntity)).Returns(o => ((MyEntity) o[0]).Id); + return TypeHelper.Disassemble( + types.Values.ToArray(), + types.Keys.Cast().ToArray(), + null, + sessionImpl, + null) + .Concat( + new[] + { + LazyPropertyInitializer.UnfetchedProperty, + BackrefPropertyAccessor.Unknown + }) + .ToArray(); + } + + private AnyType.ObjectTypeCacheEntry CreateObjectTypeCacheEntry() + { + return new AnyType.ObjectTypeCacheEntry + { + Id = 100, + EntityName = "Test" + }; + } + + private void CheckCacheEntry(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++) + { + 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]; + CheckObjectTypeCacheEntry(originalAnyType, copyAnyType); + } + else + { + Assert.That(copy.DisassembledState[i], Is.EqualTo(original.DisassembledState[i])); + } + } + } + + private void CheckCachedItem(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: + CheckCacheEntry(cacheEntry, (CacheEntry) copy.Value); + break; + case CollectionCacheEntry colleectionCacheEntry: + CheckCollectionCacheEntry(colleectionCacheEntry, (CollectionCacheEntry) copy.Value); + break; + default: + Assert.That(copy.Value, Is.EqualTo(original.Value)); + break; + } + Assert.That(copy.FreshTimestamp, Is.EqualTo(original.FreshTimestamp)); + } + + private void CheckCollectionCacheEntry(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++) + { + Assert.That(copyArray[i], Is.TypeOf(originalArray[i].GetType())); + if (originalArray[i] is AnyType.ObjectTypeCacheEntry originalAnyType) + { + var copyAnyType = (AnyType.ObjectTypeCacheEntry) copyArray[i]; + CheckObjectTypeCacheEntry(originalAnyType, copyAnyType); + } + else + { + Assert.That(copyArray[i], Is.EqualTo(originalArray[i])); + } + } + } + + private void CheckCacheLock(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)); + } + + private void CheckObjectTypeCacheEntry(AnyType.ObjectTypeCacheEntry original, AnyType.ObjectTypeCacheEntry copy) + { + Assert.That(copy.Id, Is.EqualTo(original.Id)); + Assert.That(copy.EntityName, Is.EqualTo(original.EntityName)); + } + + private static T TestDataContractSerializer(T obj) + { + var xml = DataContractSerializerToXml(obj); + obj = DataContractSerializerFromXml(xml); + Assert.That(xml, Is.EqualTo(DataContractSerializerToXml(obj))); + return obj; + } + + private static T TestBinaryFormatter(T obj) + { + var bytes = BinaryFormatterToBinary(obj); + obj = BinaryFormatterFromBinary(bytes); + Assert.That(bytes, Is.EqualTo(BinaryFormatterToBinary(obj))); + return obj; + } + + private static string DataContractSerializerToXml(T obj) + { + using (var memoryStream = new MemoryStream()) + using (var reader = new StreamReader(memoryStream)) + { + var serializer = new DataContractSerializer(typeof(T), KnownTypes); + serializer.WriteObject(memoryStream, obj); + memoryStream.Position = 0; + return reader.ReadToEnd(); + } + } + + private static T DataContractSerializerFromXml(string xml) + { + using (var stream = new MemoryStream()) + { + var data = Encoding.UTF8.GetBytes(xml); + stream.Write(data, 0, data.Length); + stream.Position = 0; + var deserializer = new DataContractSerializer(typeof(T), KnownTypes); + return (T) deserializer.ReadObject(stream); + } + } + + private static byte[] BinaryFormatterToBinary(T obj) + { + var serializer = new BinaryFormatter(); + using (var stream = new MemoryStream()) + { + serializer.Serialize(stream, obj); + return stream.ToArray(); + } + } + + private static T BinaryFormatterFromBinary(byte[] value) + { + var serializer = new BinaryFormatter(); + using (var stream = new MemoryStream(value)) + { + return (T)serializer.Deserialize(stream); + } + } + } +} diff --git a/src/NHibernate/Action/CollectionUpdateAction.cs b/src/NHibernate/Action/CollectionUpdateAction.cs index 7153105f704..f86c009e5d9 100644 --- a/src/NHibernate/Action/CollectionUpdateAction.cs +++ b/src/NHibernate/Action/CollectionUpdateAction.cs @@ -139,7 +139,7 @@ public override AfterTransactionCompletionProcessDelegate AfterTransactionComple // or detached from the session if (Collection.WasInitialized && Session.PersistenceContext.ContainsCollection(Collection)) { - CollectionCacheEntry entry = new CollectionCacheEntry(Collection, Persister); + CollectionCacheEntry entry = CollectionCacheEntry.Create(Collection, Persister); bool put = Persister.Cache.AfterUpdate(ck, entry, null, Lock); if (put && Session.Factory.Statistics.IsStatisticsEnabled) @@ -158,4 +158,4 @@ public override AfterTransactionCompletionProcessDelegate AfterTransactionComple } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Action/EntityInsertAction.cs b/src/NHibernate/Action/EntityInsertAction.cs index c34ec0e615a..6f4f9a71a45 100644 --- a/src/NHibernate/Action/EntityInsertAction.cs +++ b/src/NHibernate/Action/EntityInsertAction.cs @@ -74,7 +74,7 @@ public override void Execute() if (IsCachePutEnabled(persister)) { - CacheEntry ce = new CacheEntry(State, persister, persister.HasUninitializedLazyProperties(instance), version, session, instance); + CacheEntry ce = CacheEntry.Create(State, persister, persister.HasUninitializedLazyProperties(instance), version, session, instance); cacheEntry = persister.CacheEntryStructure.Structure(ce); CacheKey ck = Session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); diff --git a/src/NHibernate/Action/EntityUpdateAction.cs b/src/NHibernate/Action/EntityUpdateAction.cs index 68a92148f2d..dfac0d86120 100644 --- a/src/NHibernate/Action/EntityUpdateAction.cs +++ b/src/NHibernate/Action/EntityUpdateAction.cs @@ -114,7 +114,7 @@ public override void Execute() } else { - CacheEntry ce = new CacheEntry(state, persister, persister.HasUninitializedLazyProperties(instance), nextVersion, Session, instance); + CacheEntry ce = CacheEntry.Create(state, persister, persister.HasUninitializedLazyProperties(instance), nextVersion, Session, instance); cacheEntry = persister.CacheEntryStructure.Structure(ce); bool put = persister.Cache.Update(ck, cacheEntry, nextVersion, previousVersion); diff --git a/src/NHibernate/Async/Action/CollectionUpdateAction.cs b/src/NHibernate/Async/Action/CollectionUpdateAction.cs index 56fb853dc4b..8ca53b5bfa7 100644 --- a/src/NHibernate/Async/Action/CollectionUpdateAction.cs +++ b/src/NHibernate/Async/Action/CollectionUpdateAction.cs @@ -119,4 +119,4 @@ private async Task PostUpdateAsync(CancellationToken cancellationToken) } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Action/EntityInsertAction.cs b/src/NHibernate/Async/Action/EntityInsertAction.cs index 29ae4d55a13..4a0115dc06a 100644 --- a/src/NHibernate/Async/Action/EntityInsertAction.cs +++ b/src/NHibernate/Async/Action/EntityInsertAction.cs @@ -70,7 +70,7 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken) if (IsCachePutEnabled(persister)) { - CacheEntry ce = new CacheEntry(State, persister, persister.HasUninitializedLazyProperties(instance), version, session, instance); + CacheEntry ce = await (CacheEntry.CreateAsync(State, persister, persister.HasUninitializedLazyProperties(instance), version, session, instance, cancellationToken)).ConfigureAwait(false); cacheEntry = persister.CacheEntryStructure.Structure(ce); CacheKey ck = Session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); diff --git a/src/NHibernate/Async/Action/EntityUpdateAction.cs b/src/NHibernate/Async/Action/EntityUpdateAction.cs index 53a577aefb4..33f1cab8fdc 100644 --- a/src/NHibernate/Async/Action/EntityUpdateAction.cs +++ b/src/NHibernate/Async/Action/EntityUpdateAction.cs @@ -99,7 +99,7 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken) } else { - CacheEntry ce = new CacheEntry(state, persister, persister.HasUninitializedLazyProperties(instance), nextVersion, Session, instance); + CacheEntry ce = await (CacheEntry.CreateAsync(state, persister, persister.HasUninitializedLazyProperties(instance), nextVersion, Session, instance, cancellationToken)).ConfigureAwait(false); cacheEntry = persister.CacheEntryStructure.Structure(ce); bool put = await (persister.Cache.UpdateAsync(ck, cacheEntry, nextVersion, previousVersion, cancellationToken)).ConfigureAwait(false); diff --git a/src/NHibernate/Async/Cache/Entry/CacheEntry.cs b/src/NHibernate/Async/Cache/Entry/CacheEntry.cs index 68fcad94520..c59b8976e9c 100644 --- a/src/NHibernate/Async/Cache/Entry/CacheEntry.cs +++ b/src/NHibernate/Async/Cache/Entry/CacheEntry.cs @@ -9,6 +9,7 @@ using System; +using System.Runtime.Serialization; using NHibernate.Engine; using NHibernate.Event; using NHibernate.Persister.Entity; @@ -21,10 +22,24 @@ namespace NHibernate.Cache.Entry public sealed partial class CacheEntry { + public static async Task CreateAsync(object[] state, IEntityPersister persister, bool unfetched, object version, + ISessionImplementor session, object owner, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return new CacheEntry + { + //disassembled state gets put in a new array (we write to cache by value!) + DisassembledState = await (TypeHelper.DisassembleAsync(state, persister.PropertyTypes, null, session, owner, cancellationToken)).ConfigureAwait(false), + AreLazyPropertiesUnfetched = unfetched || !persister.IsLazyPropertiesCacheable, + Subclass = persister.EntityName, + Version = version + }; + } + public Task AssembleAsync(object instance, object id, IEntityPersister persister, IInterceptor interceptor, ISessionImplementor session, CancellationToken cancellationToken) { - if (!persister.EntityName.Equals(subclass)) + if (!persister.EntityName.Equals(Subclass)) { throw new AssertionFailure("Tried to assemble a different subclass instance"); } @@ -33,7 +48,7 @@ public Task AssembleAsync(object instance, object id, IEntityPersister return Task.FromCanceled(cancellationToken); } - return AssembleAsync(disassembledState, instance, id, persister, interceptor, session, cancellationToken); + return AssembleAsync(DisassembledState, instance, id, persister, interceptor, session, cancellationToken); } private static async Task AssembleAsync(object[] values, object result, object id, IEntityPersister persister, @@ -61,4 +76,4 @@ private static async Task AssembleAsync(object[] values, object result return assembledProps; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Cache/Entry/CollectionCacheEntry.cs b/src/NHibernate/Async/Cache/Entry/CollectionCacheEntry.cs index f82139126fe..6072be6a15e 100644 --- a/src/NHibernate/Async/Cache/Entry/CollectionCacheEntry.cs +++ b/src/NHibernate/Async/Cache/Entry/CollectionCacheEntry.cs @@ -9,6 +9,7 @@ using System; +using System.Runtime.Serialization; using NHibernate.Collection; using NHibernate.Persister.Collection; using NHibernate.Util; @@ -20,6 +21,15 @@ namespace NHibernate.Cache.Entry public partial class CollectionCacheEntry { + public static async Task CreateAsync(IPersistentCollection collection, ICollectionPersister persister, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return new CollectionCacheEntry + { + state = await (collection.DisassembleAsync(persister, cancellationToken)).ConfigureAwait(false) + }; + } + public virtual async Task AssembleAsync(IPersistentCollection collection, ICollectionPersister persister, object owner, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/NHibernate/Async/Cache/ReadWriteCache.cs b/src/NHibernate/Async/Cache/ReadWriteCache.cs index 4c83d96b122..3a307c975fe 100644 --- a/src/NHibernate/Async/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Async/Cache/ReadWriteCache.cs @@ -161,7 +161,7 @@ public async Task LockAsync(CacheKey key, object version, Cancellatio ILockable lockable = (ILockable) await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); long timeout = cache.NextTimestamp() + cache.Timeout; CacheLock @lock = lockable == null ? - new CacheLock(timeout, NextLockId(), version) : + CacheLock.Create(timeout, NextLockId(), version) : lockable.Lock(timeout, NextLockId()); await (cache.PutAsync(key, @lock, cancellationToken)).ConfigureAwait(false); return @lock; @@ -226,7 +226,7 @@ async Task InternalPutManyAsync() lockable.IsPuttable(timestamp, version, versionComparers[i]); if (puttable) { - putBatch.Add(key, new CachedItem(values[i], cache.NextTimestamp(), version)); + putBatch.Add(key, CachedItem.Create(values[i], cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Cached: {0}", key); @@ -302,7 +302,7 @@ public async Task PutAsync(CacheKey key, object value, long txTimestamp, o if (puttable) { - await (cache.PutAsync(key, new CachedItem(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); + await (cache.PutAsync(key, CachedItem.Create(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); if (log.IsDebugEnabled()) { log.Debug("Cached: {0}", key); @@ -395,7 +395,7 @@ internal Task HandleLockExpiryAsync(object key, CancellationToken cancellationTo log.Warn("An item was expired by the cache while it was locked (increase your cache timeout): {0}", key); long ts = cache.NextTimestamp() + cache.Timeout; // create new lock that times out immediately - CacheLock @lock = new CacheLock(ts, NextLockId(), null); + CacheLock @lock = CacheLock.Create(ts, NextLockId(), null); @lock.Unlock(ts); return cache.PutAsync(key, @lock, cancellationToken); } @@ -454,7 +454,7 @@ public async Task AfterUpdateAsync(CacheKey key, object value, object vers else { //recache the updated state - await (cache.PutAsync(key, new CachedItem(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); + await (cache.PutAsync(key, CachedItem.Create(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); if (log.IsDebugEnabled()) { log.Debug("Updated: {0}", key); @@ -492,7 +492,7 @@ public async Task AfterInsertAsync(CacheKey key, object value, object vers ILockable lockable = (ILockable) await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (lockable == null) { - await (cache.PutAsync(key, new CachedItem(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); + await (cache.PutAsync(key, CachedItem.Create(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); if (log.IsDebugEnabled()) { log.Debug("Inserted: {0}", key); diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index 1b73bc3322f..4a9899649ae 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -226,7 +226,7 @@ private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollec versionComparator = null; } - CollectionCacheEntry entry = new CollectionCacheEntry(lce.Collection, persister); + CollectionCacheEntry entry = await (CollectionCacheEntry.CreateAsync(lce.Collection, persister, cancellationToken)).ConfigureAwait(false); CacheKey cacheKey = session.GenerateCacheKey(lce.Key, persister.KeyType, persister.Role); if (persister.GetBatchSize() > 1 && persister.Cache.IsBatchingPutSupported()) diff --git a/src/NHibernate/Async/Engine/TwoPhaseLoad.cs b/src/NHibernate/Async/Engine/TwoPhaseLoad.cs index dda0ea12e0e..27d3581ae01 100644 --- a/src/NHibernate/Async/Engine/TwoPhaseLoad.cs +++ b/src/NHibernate/Async/Engine/TwoPhaseLoad.cs @@ -110,7 +110,7 @@ internal static async Task InitializeEntityAsync(object entity, bool readOnly, I object version = Versioning.GetVersion(hydratedState, persister); CacheEntry entry = - new CacheEntry(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity); + await (CacheEntry.CreateAsync(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity, cancellationToken)).ConfigureAwait(false); CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); if (cacheBatchingHandler != null && persister.IsBatchLoadable && persister.Cache.IsBatchingPutSupported()) diff --git a/src/NHibernate/Async/Type/AnyType.cs b/src/NHibernate/Async/Type/AnyType.cs index 57347f1c602..bc44efe796b 100644 --- a/src/NHibernate/Async/Type/AnyType.cs +++ b/src/NHibernate/Async/Type/AnyType.cs @@ -18,6 +18,7 @@ using NHibernate.SqlTypes; using NHibernate.Util; using System.Collections.Generic; +using System.Runtime.Serialization; namespace NHibernate.Type { @@ -41,9 +42,11 @@ public override async Task NullSafeGetAsync(DbDataReader rs, string[] na public override async Task HydrateAsync(DbDataReader rs, string[] names, ISessionImplementor session, object owner, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - string entityName = (string)await (metaType.NullSafeGetAsync(rs, names[0], session, owner, cancellationToken)).ConfigureAwait(false); - object id = await (identifierType.NullSafeGetAsync(rs, names[1], session, owner, cancellationToken)).ConfigureAwait(false); - return new ObjectTypeCacheEntry(entityName, id); + return new ObjectTypeCacheEntry + { + Id = await (identifierType.NullSafeGetAsync(rs, names[1], session, owner, cancellationToken)).ConfigureAwait(false), + EntityName = (string) await (metaType.NullSafeGetAsync(rs, names[0], session, owner, cancellationToken)).ConfigureAwait(false) + }; } public override Task ResolveIdentifierAsync(object value, ISessionImplementor session, object owner, CancellationToken cancellationToken) @@ -55,7 +58,7 @@ public override Task ResolveIdentifierAsync(object value, ISessionImplem try { ObjectTypeCacheEntry holder = (ObjectTypeCacheEntry) value; - return ResolveAnyAsync(holder.entityName, holder.id, session, cancellationToken); + return ResolveAnyAsync(holder.EntityName, holder.Id, session, cancellationToken); } catch (Exception ex) { @@ -119,7 +122,7 @@ public override Task AssembleAsync(object cached, ISessionImplementor se try { ObjectTypeCacheEntry e = cached as ObjectTypeCacheEntry; - return (e == null) ? Task.FromResult(null ): session.InternalLoadAsync(e.entityName, e.id, false, false, cancellationToken); + return (e == null) ? Task.FromResult(null ): session.InternalLoadAsync(e.EntityName, e.Id, false, false, cancellationToken); } catch (Exception ex) { @@ -130,9 +133,13 @@ public override Task AssembleAsync(object cached, ISessionImplementor se public override async Task DisassembleAsync(object value, ISessionImplementor session, object owner, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - return value == null ? null : - new ObjectTypeCacheEntry(session.BestGuessEntityName(value), - await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(session.BestGuessEntityName(value), value, session, cancellationToken)).ConfigureAwait(false)); + return value == null + ? null + : new ObjectTypeCacheEntry + { + EntityName = session.BestGuessEntityName(value), + Id = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(session.BestGuessEntityName(value), value, session, cancellationToken)).ConfigureAwait(false) + }; } public override async Task ReplaceAsync(object original, object current, ISessionImplementor session, object owner, @@ -206,8 +213,8 @@ public override async Task IsModifiedAsync(object old, object current, boo ObjectTypeCacheEntry holder = (ObjectTypeCacheEntry)old; bool[] idcheckable = new bool[checkable.Length - 1]; Array.Copy(checkable, 1, idcheckable, 0, idcheckable.Length); - return (checkable[0] && !holder.entityName.Equals(session.BestGuessEntityName(current))) || - await (identifierType.IsModifiedAsync(holder.id, await (IdAsync(current, session, cancellationToken)).ConfigureAwait(false), idcheckable, session, cancellationToken)).ConfigureAwait(false); + return (checkable[0] && !holder.EntityName.Equals(session.BestGuessEntityName(current))) || + await (identifierType.IsModifiedAsync(holder.Id, await (IdAsync(current, session, cancellationToken)).ConfigureAwait(false), idcheckable, session, cancellationToken)).ConfigureAwait(false); } private Task ResolveAnyAsync(string entityName, object id, ISessionImplementor session, CancellationToken cancellationToken) diff --git a/src/NHibernate/Cache/CacheLock.cs b/src/NHibernate/Cache/CacheLock.cs index 3b34986c335..f122cbd35b6 100644 --- a/src/NHibernate/Cache/CacheLock.cs +++ b/src/NHibernate/Cache/CacheLock.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Runtime.Serialization; using NHibernate.Cache.Access; namespace NHibernate.Cache @@ -12,20 +13,37 @@ namespace NHibernate.Cache /// This class was named Lock in H2.1 /// [Serializable] + [DataContract] public class CacheLock : ReadWriteCache.ILockable, ISoftLock { private long unlockTimestamp = -1; private int multiplicity = 1; private bool concurrentLock = false; private long timeout; - private readonly int id; - private readonly object version; + private int id; + private object version; + public CacheLock() + { + } + + // Since 5.2 + [Obsolete("Use object initializer instead.")] public CacheLock(long timeout, int id, object version) { - this.timeout = timeout; - this.id = id; - this.version = version; + Timeout = timeout; + Id = id; + Version = version; + } + + internal static CacheLock Create(long timeout, int id, object version) + { + return new CacheLock + { + Version = version, + Timeout = timeout, + Id = id + }; } /// @@ -34,9 +52,9 @@ public CacheLock(long timeout, int id, object version) /// public CacheLock Lock(long timeout, int id) { - concurrentLock = true; - multiplicity++; - this.timeout = timeout; + WasLockedConcurrently = true; + Multiplicity++; + Timeout = timeout; return this; } @@ -47,9 +65,9 @@ public CacheLock Lock(long timeout, int id) /// public void Unlock(long currentTimestamp) { - if (--multiplicity == 0) + if (--Multiplicity == 0) { - unlockTimestamp = currentTimestamp; + UnlockTimestamp = currentTimestamp; } } @@ -59,17 +77,17 @@ public void Unlock(long currentTimestamp) /// public bool IsPuttable(long txTimestamp, object newVersion, IComparer comparator) { - if (timeout < txTimestamp) + if (Timeout < txTimestamp) { return true; } - if (multiplicity > 0) + if (Multiplicity > 0) { return false; } - return version == null ? - unlockTimestamp < txTimestamp : - comparator.Compare(version, newVersion) < 0; + return Version == null ? + UnlockTimestamp < txTimestamp : + comparator.Compare(Version, newVersion) < 0; //by requiring <, we rely on lock timeout in the case of an unsuccessful update! } @@ -77,18 +95,18 @@ public bool IsPuttable(long txTimestamp, object newVersion, IComparer comparator /// Was this lock held concurrently by multiple /// transactions? /// + // 6.0 TODO convert to auto-property + [DataMember] public bool WasLockedConcurrently { - get { return concurrentLock; } + get => concurrentLock; + set => concurrentLock = value; } /// /// Yes, this is a lock /// - public bool IsLock - { - get { return true; } - } + public bool IsLock => true; /// /// locks are not returned to the client! @@ -98,18 +116,53 @@ public bool IsGettable(long txTimestamp) return false; } + // 6.0 TODO convert to auto-property + [DataMember] public int Id { - get { return id; } + get => id; + set => id = value; + } + + // 6.0 TODO convert to auto-property + [DataMember] + public object Version + { + get => version; + set => version = value; + } + + // 6.0 TODO convert to auto-property + [DataMember] + public long UnlockTimestamp + { + get => unlockTimestamp; + set => unlockTimestamp = value; + } + + // 6.0 TODO convert to auto-property + [DataMember] + public int Multiplicity + { + get => multiplicity; + set => multiplicity = value; + } + + // 6.0 TODO convert to auto-property + [DataMember] + public long Timeout + { + get => timeout; + set => timeout = value; } public override string ToString() { - return "CacheLock{id=" + id + - ",version=" + version + - ",multiplicity=" + multiplicity + - ",unlockTimestamp=" + unlockTimestamp + + return "CacheLock{id=" + Id + + ",version=" + Version + + ",multiplicity=" + Multiplicity + + ",unlockTimestamp=" + UnlockTimestamp + "}"; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/CachedItem.cs b/src/NHibernate/Cache/CachedItem.cs index 50448351e0f..279aa4fd5ce 100644 --- a/src/NHibernate/Cache/CachedItem.cs +++ b/src/NHibernate/Cache/CachedItem.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Runtime.Serialization; namespace NHibernate.Cache { @@ -8,33 +9,67 @@ namespace NHibernate.Cache /// when it was unlocked /// [Serializable] + [DataContract] public class CachedItem : ReadWriteCache.ILockable { - private readonly long freshTimestamp; - private readonly object value; - private readonly object version; + private long freshTimestamp; + private object value; + private object version; + public CachedItem() + { + } + + // Since 5.2 + [Obsolete("Use object initializer instead.")] public CachedItem(object value, long currentTimestamp, object version) { - this.value = value; - freshTimestamp = currentTimestamp; - this.version = version; + Value = value; + FreshTimestamp = currentTimestamp; + Version = version; + } + + internal static CachedItem Create(object value, long currentTimestamp, object version) + { + return new CachedItem + { + Version = version, + FreshTimestamp = currentTimestamp, + Value = value + }; } /// /// The timestamp on the cached data /// + // 6.0 TODO convert to auto-property + [DataMember] public long FreshTimestamp { - get { return freshTimestamp; } + get => freshTimestamp; + set => freshTimestamp = value; } /// /// The actual cached data /// + // 6.0 TODO convert to auto-property + [DataMember] public object Value { - get { return value; } + get => value; + set => this.value = value; + } + + /// + /// The version of the cached data + /// + // 6.0 TODO convert to auto-property + [DataMember] + public object Version + { + get => version; + set => version = value; } /// @@ -42,7 +77,7 @@ public object Value /// public CacheLock Lock(long timeout, int id) { - return new CacheLock(timeout, id, version); + return CacheLock.Create(timeout, id, Version); } /// @@ -60,7 +95,7 @@ public bool IsLock /// public bool IsGettable(long txTimestamp) { - return freshTimestamp < txTimestamp; + return FreshTimestamp < txTimestamp; } /// @@ -75,13 +110,13 @@ public bool IsPuttable(long txTimestamp, object newVersion, IComparer comparator // we really could refresh the item if it // is not a lock, but it might be slower //return freshTimestamp < txTimestamp - return version != null && comparator.Compare(version, newVersion) < 0; + return Version != null && comparator.Compare(Version, newVersion) < 0; } public override string ToString() { - return "Item{version=" + version + - ",freshTimestamp=" + freshTimestamp + + return "Item{version=" + Version + + ",freshTimestamp=" + FreshTimestamp + "}"; } } diff --git a/src/NHibernate/Cache/Entry/CacheEntry.cs b/src/NHibernate/Cache/Entry/CacheEntry.cs index cba2abddb67..01f7f728a6e 100644 --- a/src/NHibernate/Cache/Entry/CacheEntry.cs +++ b/src/NHibernate/Cache/Entry/CacheEntry.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; using NHibernate.Engine; using NHibernate.Event; using NHibernate.Persister.Entity; @@ -10,66 +11,86 @@ namespace NHibernate.Cache.Entry /// A cached instance of a persistent class /// [Serializable] + [DataContract] public sealed partial class CacheEntry { - private readonly object[] disassembledState; - private readonly string subclass; - private readonly bool lazyPropertiesAreUnfetched; - private readonly object version; + private object[] disassembledState; + private string subclass; + private bool lazyPropertiesAreUnfetched; + private object version; + public CacheEntry() + { + } + // Since 5.2 + [Obsolete("Please use CacheEntry.Create method instead.")] public CacheEntry(object[] state, IEntityPersister persister, bool unfetched, object version, ISessionImplementor session, object owner) { //disassembled state gets put in a new array (we write to cache by value!) - disassembledState = TypeHelper.Disassemble(state, persister.PropertyTypes, null, session, owner); - subclass = persister.EntityName; - lazyPropertiesAreUnfetched = unfetched || !persister.IsLazyPropertiesCacheable; - this.version = version; + DisassembledState = TypeHelper.Disassemble(state, persister.PropertyTypes, null, session, owner); + Subclass = persister.EntityName; + AreLazyPropertiesUnfetched = unfetched || !persister.IsLazyPropertiesCacheable; + Version = version; } - internal CacheEntry(object[] state, string subclass, bool unfetched, object version) + public static CacheEntry Create(object[] state, IEntityPersister persister, bool unfetched, object version, + ISessionImplementor session, object owner) { - disassembledState = state; - this.subclass = subclass; - lazyPropertiesAreUnfetched = unfetched; - this.version = version; + return new CacheEntry + { + //disassembled state gets put in a new array (we write to cache by value!) + DisassembledState = TypeHelper.Disassemble(state, persister.PropertyTypes, null, session, owner), + AreLazyPropertiesUnfetched = unfetched || !persister.IsLazyPropertiesCacheable, + Subclass = persister.EntityName, + Version = version + }; } + // 6.0 TODO convert to auto-property + [DataMember] public object Version { - get{return version;} + get => version; + set => version = value; } + // 6.0 TODO convert to auto-property + [DataMember] public string Subclass { - get { return subclass; } + get => subclass; + set => subclass = value; } + // 6.0 TODO convert to auto-property + [DataMember] public bool AreLazyPropertiesUnfetched { - get { return lazyPropertiesAreUnfetched; } + get => lazyPropertiesAreUnfetched; + set => lazyPropertiesAreUnfetched = value; } + // todo: this was added to support initializing an entity's EntityEntry snapshot during reattach; + // this should be refactored to instead expose a method to assemble a EntityEntry based on this + // state for return. + // 6.0 TODO convert to auto-property + [DataMember] public object[] DisassembledState { - get - { - // todo: this was added to support initializing an entity's EntityEntry snapshot during reattach; - // this should be refactored to instead expose a method to assemble a EntityEntry based on this - // state for return. - return disassembledState; - } + get => disassembledState; + set => disassembledState = value; } public object[] Assemble(object instance, object id, IEntityPersister persister, IInterceptor interceptor, ISessionImplementor session) { - if (!persister.EntityName.Equals(subclass)) + if (!persister.EntityName.Equals(Subclass)) { throw new AssertionFailure("Tried to assemble a different subclass instance"); } - return Assemble(disassembledState, instance, id, persister, interceptor, session); + return Assemble(DisassembledState, instance, id, persister, interceptor, session); } private static object[] Assemble(object[] values, object result, object id, IEntityPersister persister, @@ -96,4 +117,4 @@ private static object[] Assemble(object[] values, object result, object id, IEnt return assembledProps; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/Entry/CollectionCacheEntry.cs b/src/NHibernate/Cache/Entry/CollectionCacheEntry.cs index 84533511d54..7d5aeba52ec 100644 --- a/src/NHibernate/Cache/Entry/CollectionCacheEntry.cs +++ b/src/NHibernate/Cache/Entry/CollectionCacheEntry.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; using NHibernate.Collection; using NHibernate.Persister.Collection; using NHibernate.Util; @@ -6,23 +7,36 @@ namespace NHibernate.Cache.Entry { [Serializable] + [DataContract] public partial class CollectionCacheEntry { - private readonly object state; + private object state; + public CollectionCacheEntry() + { + } + + // Since 5.2 + [Obsolete("Use CollectionCacheEntry.Create method instead.")] public CollectionCacheEntry(IPersistentCollection collection, ICollectionPersister persister) { state = collection.Disassemble(persister); } - internal CollectionCacheEntry(object state) + public static CollectionCacheEntry Create(IPersistentCollection collection, ICollectionPersister persister) { - this.state = state; + return new CollectionCacheEntry + { + state = collection.Disassemble(persister) + }; } + // 6.0 TODO convert to auto-property + [DataMember] public virtual object[] State { - get { return (object[])state; }//TODO: assumes all collections disassemble to an array! + get => (object[]) state; //TODO: assumes all collections disassemble to an array! + set => state = value; } public virtual void Assemble(IPersistentCollection collection, ICollectionPersister persister, object owner) diff --git a/src/NHibernate/Cache/Entry/StructuredCacheEntry.cs b/src/NHibernate/Cache/Entry/StructuredCacheEntry.cs index f9b7e30f62b..bc9bf20372a 100644 --- a/src/NHibernate/Cache/Entry/StructuredCacheEntry.cs +++ b/src/NHibernate/Cache/Entry/StructuredCacheEntry.cs @@ -28,7 +28,13 @@ public object Destructure(object item, ISessionFactoryImplementor factory) { state[i] = map[names[i]]; } - return new CacheEntry(state, subclass, lazyPropertiesUnfetched, version); + return new CacheEntry + { + Subclass = subclass, + DisassembledState = state, + Version = version, + AreLazyPropertiesUnfetched = lazyPropertiesUnfetched + }; } public object Structure(object item) diff --git a/src/NHibernate/Cache/Entry/StructuredCollectionCacheEntry.cs b/src/NHibernate/Cache/Entry/StructuredCollectionCacheEntry.cs index e85c6828694..13a63af6719 100644 --- a/src/NHibernate/Cache/Entry/StructuredCollectionCacheEntry.cs +++ b/src/NHibernate/Cache/Entry/StructuredCollectionCacheEntry.cs @@ -20,7 +20,7 @@ public virtual object Destructure(object item, ISessionFactoryImplementor factor var objects = collection != null ? collection.Cast().ToArray() : Array.Empty(); - return new CollectionCacheEntry(objects); + return new CollectionCacheEntry {State = objects}; } } } diff --git a/src/NHibernate/Cache/Entry/StructuredMapCacheEntry.cs b/src/NHibernate/Cache/Entry/StructuredMapCacheEntry.cs index 169af790f22..8e1936450d7 100644 --- a/src/NHibernate/Cache/Entry/StructuredMapCacheEntry.cs +++ b/src/NHibernate/Cache/Entry/StructuredMapCacheEntry.cs @@ -27,7 +27,7 @@ public object Destructure(object item, ISessionFactoryImplementor factory) state[i++] = me.Key; state[i++] = me.Value; } - return new CollectionCacheEntry(state); + return new CollectionCacheEntry {State = state}; } } } diff --git a/src/NHibernate/Cache/ReadWriteCache.cs b/src/NHibernate/Cache/ReadWriteCache.cs index fa66ed9bd16..92a08c47f01 100644 --- a/src/NHibernate/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Cache/ReadWriteCache.cs @@ -208,7 +208,7 @@ public ISoftLock Lock(CacheKey key, object version) ILockable lockable = (ILockable) cache.Get(key); long timeout = cache.NextTimestamp() + cache.Timeout; CacheLock @lock = lockable == null ? - new CacheLock(timeout, NextLockId(), version) : + CacheLock.Create(timeout, NextLockId(), version) : lockable.Lock(timeout, NextLockId()); cache.Put(key, @lock); return @lock; @@ -266,7 +266,7 @@ public bool[] PutMany(CacheKey[] keys, object[] values, long timestamp, object[] lockable.IsPuttable(timestamp, version, versionComparers[i]); if (puttable) { - putBatch.Add(key, new CachedItem(values[i], cache.NextTimestamp(), version)); + putBatch.Add(key, CachedItem.Create(values[i], cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Cached: {0}", key); @@ -340,7 +340,7 @@ public bool Put(CacheKey key, object value, long txTimestamp, object version, IC if (puttable) { - cache.Put(key, new CachedItem(value, cache.NextTimestamp(), version)); + cache.Put(key, CachedItem.Create(value, cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Cached: {0}", key); @@ -415,7 +415,7 @@ internal void HandleLockExpiry(object key) log.Warn("An item was expired by the cache while it was locked (increase your cache timeout): {0}", key); long ts = cache.NextTimestamp() + cache.Timeout; // create new lock that times out immediately - CacheLock @lock = new CacheLock(ts, NextLockId(), null); + CacheLock @lock = CacheLock.Create(ts, NextLockId(), null); @lock.Unlock(ts); cache.Put(key, @lock); } @@ -472,7 +472,7 @@ public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock cl else { //recache the updated state - cache.Put(key, new CachedItem(value, cache.NextTimestamp(), version)); + cache.Put(key, CachedItem.Create(value, cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Updated: {0}", key); @@ -509,7 +509,7 @@ public bool AfterInsert(CacheKey key, object value, object version) ILockable lockable = (ILockable) cache.Get(key); if (lockable == null) { - cache.Put(key, new CachedItem(value, cache.NextTimestamp(), version)); + cache.Put(key, CachedItem.Create(value, cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Inserted: {0}", key); diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 89f154af1bd..588f17f1ad2 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -332,7 +332,7 @@ private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersist versionComparator = null; } - CollectionCacheEntry entry = new CollectionCacheEntry(lce.Collection, persister); + CollectionCacheEntry entry = CollectionCacheEntry.Create(lce.Collection, persister); CacheKey cacheKey = session.GenerateCacheKey(lce.Key, persister.KeyType, persister.Role); if (persister.GetBatchSize() > 1 && persister.Cache.IsBatchingPutSupported()) diff --git a/src/NHibernate/Engine/TwoPhaseLoad.cs b/src/NHibernate/Engine/TwoPhaseLoad.cs index c2df76ce4dc..39bbff25e75 100644 --- a/src/NHibernate/Engine/TwoPhaseLoad.cs +++ b/src/NHibernate/Engine/TwoPhaseLoad.cs @@ -118,7 +118,7 @@ internal static void InitializeEntity(object entity, bool readOnly, ISessionImpl object version = Versioning.GetVersion(hydratedState, persister); CacheEntry entry = - new CacheEntry(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity); + CacheEntry.Create(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity); CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); if (cacheBatchingHandler != null && persister.IsBatchLoadable && persister.Cache.IsBatchingPutSupported()) diff --git a/src/NHibernate/Type/AnyType.cs b/src/NHibernate/Type/AnyType.cs index 8e1e824c545..df64defc070 100644 --- a/src/NHibernate/Type/AnyType.cs +++ b/src/NHibernate/Type/AnyType.cs @@ -8,6 +8,7 @@ using NHibernate.SqlTypes; using NHibernate.Util; using System.Collections.Generic; +using System.Runtime.Serialization; namespace NHibernate.Type { @@ -95,15 +96,17 @@ public override object NullSafeGet(DbDataReader rs, string[] names, ISessionImpl public override object Hydrate(DbDataReader rs, string[] names, ISessionImplementor session, object owner) { - string entityName = (string)metaType.NullSafeGet(rs, names[0], session, owner); - object id = identifierType.NullSafeGet(rs, names[1], session, owner); - return new ObjectTypeCacheEntry(entityName, id); + return new ObjectTypeCacheEntry + { + Id = identifierType.NullSafeGet(rs, names[1], session, owner), + EntityName = (string) metaType.NullSafeGet(rs, names[0], session, owner) + }; } public override object ResolveIdentifier(object value, ISessionImplementor session, object owner) { ObjectTypeCacheEntry holder = (ObjectTypeCacheEntry) value; - return ResolveAny(holder.entityName, holder.id, session); + return ResolveAny(holder.EntityName, holder.Id, session); } public override object SemiResolve(object value, ISessionImplementor session, object owner) @@ -165,28 +168,44 @@ public override string ToLoggableString(object value, ISessionFactoryImplementor } [Serializable] + [DataContract] public sealed class ObjectTypeCacheEntry { internal string entityName; internal object id; - internal ObjectTypeCacheEntry(string entityName, object id) + + // 6.0 TODO convert to auto-property + [DataMember] + public string EntityName + { + get => entityName; + set => entityName = value; + } + + // 6.0 TODO convert to auto-property + [DataMember] + public object Id { - this.entityName = entityName; - this.id = id; + get => id; + set => id = value; } } public override object Assemble(object cached, ISessionImplementor session, object owner) { ObjectTypeCacheEntry e = cached as ObjectTypeCacheEntry; - return (e == null) ? null : session.InternalLoad(e.entityName, e.id, false, false); + return (e == null) ? null : session.InternalLoad(e.EntityName, e.Id, false, false); } public override object Disassemble(object value, ISessionImplementor session, object owner) { - return value == null ? null : - new ObjectTypeCacheEntry(session.BestGuessEntityName(value), - ForeignKeys.GetEntityIdentifierIfNotUnsaved(session.BestGuessEntityName(value), value, session)); + return value == null + ? null + : new ObjectTypeCacheEntry + { + EntityName = session.BestGuessEntityName(value), + Id = ForeignKeys.GetEntityIdentifierIfNotUnsaved(session.BestGuessEntityName(value), value, session) + }; } public override object Replace(object original, object current, ISessionImplementor session, object owner, @@ -345,8 +364,8 @@ public override bool IsModified(object old, object current, bool[] checkable, IS ObjectTypeCacheEntry holder = (ObjectTypeCacheEntry)old; bool[] idcheckable = new bool[checkable.Length - 1]; Array.Copy(checkable, 1, idcheckable, 0, idcheckable.Length); - return (checkable[0] && !holder.entityName.Equals(session.BestGuessEntityName(current))) || - identifierType.IsModified(holder.id, Id(current, session), idcheckable, session); + return (checkable[0] && !holder.EntityName.Equals(session.BestGuessEntityName(current))) || + identifierType.IsModified(holder.Id, Id(current, session), idcheckable, session); } public bool[] PropertyNullability