/// <summary> /// Write an object to a target stream using a certain serialiser. /// All related objects along the object graph are properly stored using the <see cref="ISerialisationNotifier"/> contract. /// </summary> /// <param name="obj">The object.</param> /// <param name="target">The target stream.</param> /// <param name="serialiser">The serialiser.</param> /// <param name="autoClose">Optionally indicate if the stream should be automatically closed.</param> /// <param name="verbose">Optionally indicate where the log messages should written to (verbose = Info, otherwise Debug).</param> /// <returns>The number of bytes written (if exposed by the used target stream).</returns> public static long Write(object obj, Stream target, ISerialiser serialiser, bool autoClose = true, bool verbose = true) { if (obj == null) { throw new ArgumentNullException(nameof(obj)); } if (target == null) { throw new ArgumentNullException(nameof(target)); } if (serialiser == null) { throw new ArgumentNullException(nameof(serialiser)); } LoggingUtils.Log(verbose ? Level.Info : Level.Debug, $"Writing {obj.GetType().Name} {obj} of type {obj.GetType()} to target stream {target} using serialiser {serialiser}...", ClazzLogger); Stopwatch stopwatch = Stopwatch.StartNew(); long beforePosition = target.Position; TraverseObjectGraph(obj, new HashSet <object>(), (p, f, o) => (o as ISerialisationNotifier)?.OnSerialising()); serialiser.Write(obj, target); TraverseObjectGraph(obj, new HashSet <object>(), (p, f, o) => (o as ISerialisationNotifier)?.OnSerialised()); target.Flush(); long bytesWritten = target.Position - beforePosition; target.Close(); LoggingUtils.Log(verbose ? Level.Info : Level.Debug, $"Done writing {obj.GetType().Name} {obj} to target stream {target} using serialiser {serialiser}, " + $"wrote {(bytesWritten / 1024.0):#.#}kB, took {stopwatch.ElapsedMilliseconds}ms.", ClazzLogger); return(bytesWritten); }
/// <summary> /// Attempt to read and validate certain object from a binary file, return the original value if unsuccessful. /// </summary> /// <typeparam name="T">The object type.</typeparam> /// <param name="fileName">The file name.</param> /// <param name="originalValue">The original value.</param> /// <param name="verbose">Optionally indicate where the log messages should written to (verbose = Info, otherwise Debug).</param> /// <param name="validationFunction">The optional validation function to validate the read object with (if false, the original value is returned).</param> /// <returns>The read (i.e. existing) if successfully read and validated, otherwise the original value.</returns> public static T ReadBinaryFileIfExists <T>(string fileName, T originalValue, bool verbose = true, Func <T, bool> validationFunction = null) { try { T existing = ReadBinaryFile <T>(fileName, verbose); if (validationFunction == null || validationFunction.Invoke(existing)) { LoggingUtils.Log(verbose ? Level.Info : Level.Debug, $"Read and validation of type {typeof(T)} successful, returning existing value.", ClazzLogger); return(existing); } LoggingUtils.Log(verbose ? Level.Info : Level.Debug, $"Read of type {typeof(T)} successful, validation failed, returning default value.", ClazzLogger); } catch (Exception e) { LoggingUtils.Log(verbose ? Level.Info : Level.Debug, $"Read of type {typeof(T)} failed with \"{e.GetType()}: {e.Message}\", returning default value.", ClazzLogger); } return(originalValue); }
/// <summary> /// Read an object from a target stream using a certain serialiser. /// All related objects along the object graph are properly restored using the <see cref="ISerialisationNotifier"/> contract. /// </summary> /// <param name="target">The target stream.</param> /// <param name="serialiser">The serialiser.</param> /// <param name="verbose">Optionally indicate where the log messages should written to (verbose = Info, otherwise Debug).</param> /// <returns>The read object of the requested type.</returns> public static T Read <T>(Stream target, ISerialiser serialiser, bool verbose = true) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (serialiser == null) { throw new ArgumentNullException(nameof(serialiser)); } LoggingUtils.Log(verbose ? Level.Info : Level.Debug, $"Reading {typeof(T).Name} from target stream {target} using serialiser {serialiser}...", ClazzLogger); Stopwatch stopwatch = Stopwatch.StartNew(); long beforePosition = target.Position; object read = serialiser.Read(target); if (!(read is T)) { throw new SerializationException($"Unable to read {typeof(T).Name} from target {target} using serialiser {serialiser}, read object {read} was not of the requested type."); } TraverseObjectGraph(read, new HashSet <object>(), (parent, field, obj) => { // automatically restore all logger instances if (field.FieldType == typeof(ILog)) { field.SetValue(parent, LogManager.GetLogger(Assembly.GetCallingAssembly(), parent.GetType().Namespace + "." + parent.GetType().Name)); } (obj as ISerialisationNotifier)?.OnDeserialised(); }); LoggingUtils.Log(verbose ? Level.Info : Level.Debug, $"Done reading {typeof(T).Name} {read} from target stream {target} using serialiser {serialiser}, " + $"read {((target.Position - beforePosition) / 1024.0):#.#}kB, took {stopwatch.ElapsedMilliseconds}ms.", ClazzLogger); return((T)read); }