Skip to content
A small framework for managing settings.
C#
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.paket
Gu.Persist.Core.Tests
Gu.Persist.Core
Gu.Persist.Demo
Gu.Persist.Git.Tests
Gu.Persist.Git
Gu.Persist.NewtonsoftJson.Tests
Gu.Persist.NewtonsoftJson
Gu.Persist.RuntimeBinary.Tests
Gu.Persist.RuntimeBinary
Gu.Persist.RuntimeXml.Tests
Gu.Persist.RuntimeXml
Gu.Persist.SystemXml.Tests
Gu.Persist.SystemXml
.gitignore
.tfignore
Gu.Persist.sln
Gu.Persist.sln.DotSettings
Gu.Persist.snk
LICENSE
README.md
RELEASE_NOTES.md
appveyor.yml
azure-pipelines.yml
paket.dependencies
paket.lock

README.md

Gu.Persist

License NuGet NuGet NuGet NuGet NuGet

Build Status

A small framework for reading and saving data.

  • XmlRepository is a baseclass for managing xml files.
  • BinaryRepository is a baseclass for managing binary files.
  • JsonRepository is a baseclass for managing json files.

Table of contents.

Features

  • Transactional atomic saves. Avoids corrupted data on application crash etc.
  • Repository bootstraps itself with settings file in directory.
  • SingletonRepository manages a singleton reference for each file.
  • Creates backups on save. Backup rules configurable via setting.
    • Extension
    • Directory
    • Number of backups
    • Max age backups.
  • Use git for backups.
  • T Clone(T item); deep clone by serializing and then deserializing an instance.
  • bool IsDirty(T item, IEqualityComparer comparer); check if an instance is dirty after last save.
  • EqualityComparers that checks structural equality by serializing and comparing bytes. If performance is an issue overloads with IEqualityComparer are exposed.

Repository

Helper class for reading and saving files. It is meant to be cached as it is expensive to instantiate. There are a number of interfaces with subsets of the functionality. Exposing the raw repository class is probably a bad idea as it has so many overload of everything. When not passing in an explicit RepositorySetting in the constructor the constructor looks on disk if there is a settings file to read and use. If there is no file it creates an instance and saves it for use next time. This makes it configurable without recompiling.

If a setting is passed in the constructor does not look on disk.

The different libraries contains repository implementations for

  • NewtonSoft.Json
  • System.XmlSerializer
  • System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
  • System.Runtime.Serialization.DataContractSerializer

SingletonRepository

Manages singleton instances of things read or written to disk. This is useful for settings etc.

DataRepository

Simple repository for reading & saving data.

Members

  • GetFileInfo, for getting the file the repository uses for reading & saving.
  • Delete for deleting files & backups.
  • Exists for checking if files exists.
  • Read for reading and deserializing the contents of files.
  • ReadAsync for reading and deserializing the contents of files.
  • ReadOrCreate, read the file if it exists, create and instance and save it to disk before returning it if not.
  • Save for saving files.
  • SaveAsync for saving files.
  • CanRename, check for collisions before renaming.
  • Rename, rename files and backups.
  • ClearTrackerCache, clear the IDirtyTracker
  • RemoveFromDirtyTracker, remove an item from IDirtyTracker
  • DeleteBackups, delete backups for a file.

Migration.

For managing versions of files on disk. The migrations can also be used for switching from xml to json for example. Sample for json:

var read = repository.Read<DummySerializable>(new JsonMigration(Version1To2, Version2To3));

JObject Version1To2(JObject jObject)
{
    if (jObject["Version"].Value<int>() == 1)
    {
        jObject["Version"] = 2;
        jObject.RenameProperty("Typo", "Name");
        return jObject;
    }

    return jObject;
}

JObject Version2To3(JObject jObject)
{
    if (jObject["Version"].Value<int>() == 2)
    {
        jObject["Version"] = 3;
        jObject.Add("NewProperty", "default value");
        return jObject;
    }

    return jObject;
}

Save transaction.

Locks all files that will be part of the transaction. Uses atomic writes.

  1. Lock file if exists.
  2. Lock file.delete if it exists.
  3. Create and lock file.tmp if it exists.
  4. Save to file.tmp
  5. Rename file to file.backup if creating backups.
  6. Rename file.tmp-> file

On error everything is reset back to initial state.

Samples

Sample for reading and saving settings using git for backups.

public class Settings
{
    private static readonly DirectoryInfo Directory = new DirectoryInfo("./Settings");

    // Initializes with  ./Settings/RepositorySettings.json is present
    // Creates a git repository for history.
    private readonly SingletonRepository repository = new SingletonRepository(
        CreateDefaultSettings,
        new GitBackuper(Directory.FullName));

    public MySetting ReadFoo()
    {
        // Reads the contents of ./Settings/MySetting.json
        // As we are using a SingletonRepository Read will always return the same instance.
        return this.repository.Read<MySetting>();
    }

    public void Save(MySetting setting)
    {
        // Saves to of ./Settings/MySetting.json
        // Commits changes to git repository.
        this.repository.Save(setting);
    }

    private static RepositorySettings CreateDefaultSettings()
    {
        return new RepositorySettings(
            directory: Directory.FullName,
            jsonSerializerSettings: new JsonSerializerSettings { Formatting = Formatting.Indented },
            isTrackingDirty: true,
            backupSettings: null,
            extension: ".json",
            tempExtension: ".saving");
    }
}

For reading and saving data

public class Data
{
    // Uses %AppData%/ApplicationName.
    // Initializes with  %AppData%/ApplicationName/RepositorySettings.cfg
    private readonly DataRepository repository = new DataRepository();

    public MyData ReadFoo()
    {
        // Reads the contents of %AppData%/ApplicationName/MyData.cfg
        // As we wrap a DataRepository Read will always return a new instance.
        return this.repository.Read<MyData>();
    }

    public void Save(MyData data)
    {
        // Saves to of %AppData%/ApplicationName/MyData.cfg
        // Creates a backup %AppData%/ApplicationName/MyData.bak
        this.repository.Save(data);
    }
}

Interfaces

A number of interfaces exposing subsets of the functionality are provided.

  • IAsyncFileNameRepository
  • IAsyncFileInfoRepository
  • ICloner
  • IDirty
  • IStreamRepository
  • IGenericRepository
  • IGenericAsyncRepository
  • A bunch of StreamRepositories for reading raw streams.
You can’t perform that action at this time.