{"id":21129509,"url":"https://github.com/guorg/gu.persist","last_synced_at":"2025-07-09T00:32:17.216Z","repository":{"id":29864380,"uuid":"33409459","full_name":"GuOrg/Gu.Persist","owner":"GuOrg","description":"A small framework for managing settings.","archived":false,"fork":false,"pushed_at":"2023-12-25T16:13:59.000Z","size":10942,"stargazers_count":10,"open_issues_count":16,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-05-28T11:14:12.368Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GuOrg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-04-04T15:07:54.000Z","updated_at":"2024-06-19T04:08:54.701Z","dependencies_parsed_at":"2023-12-14T10:57:10.187Z","dependency_job_id":"7188e9c6-b32b-4b9d-a98d-b35e70634309","html_url":"https://github.com/GuOrg/Gu.Persist","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuOrg%2FGu.Persist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuOrg%2FGu.Persist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuOrg%2FGu.Persist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuOrg%2FGu.Persist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GuOrg","download_url":"https://codeload.github.com/GuOrg/Gu.Persist/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225473304,"owners_count":17479761,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-20T05:24:24.635Z","updated_at":"2024-11-20T05:24:25.389Z","avatar_url":"https://github.com/GuOrg.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gu.Persist\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![NuGet](https://img.shields.io/nuget/v/Gu.Persist.NewtonsoftJson.svg)](https://www.nuget.org/packages/Gu.Persist.NewtonsoftJson/)\n[![NuGet](https://img.shields.io/nuget/v/Gu.Persist.SystemXml.svg)](https://www.nuget.org/packages/Gu.Persist.SystemXml/)\n[![NuGet](https://img.shields.io/nuget/v/Gu.Persist.RuntimeBinary.svg)](https://www.nuget.org/packages/Gu.Persist.RuntimeBinary/)\n[![NuGet](https://img.shields.io/nuget/v/Gu.Persist.RuntimeXml.svg)](https://www.nuget.org/packages/Gu.Persist.RuntimeXml/)\n[![NuGet](https://img.shields.io/nuget/v/Gu.Persist.Git.svg)](https://www.nuget.org/packages/Gu.Persist.Git/)\n[![Build status](https://ci.appveyor.com/api/projects/status/347rs0n3van46k50/branch/master?svg=true)](https://ci.appveyor.com/project/JohanLarsson/gu-persist/branch/master)\n[![Build Status](https://dev.azure.com/guorg/Gu.Persist/_apis/build/status/GuOrg.Gu.Persist?branchName=master)](https://dev.azure.com/guorg/Gu.Persist/_build/latest?definitionId=4\u0026branchName=master)\n\nLibrary for reading and saving settings and data.\n\n- XmlRepository is a baseclass for managing xml files.\n- BinaryRepository is a baseclass for managing binary files.\n- JsonRepository is a baseclass for managing json files.\n\n# Table of contents.\n  - [Features](#features)\n  - [Repository](#repository)\n    - [SingletonRepository](#singletonrepository)\n    - [DataRepository](#datarepository)\n    - [Interfaces](#interfaces)\n      - [Members](#members)\n    - [Migration.](#migration)\n    - [Save transaction.](#save-transaction)\n    - [Sample wrapper](#sample-wrapper)\n    - [Sample using git for backups.](#sample-using-git-for-backups)\n\n## Features\n\n- Transactional atomic saves. Avoids corrupted data on application crash etc.\n- Repository bootstraps itself with settings file in directory.\n- SingletonRepository manages a singleton reference for each file.\n- Creates backups on save. Backup rules configurable via setting.\n    - Extension\n    - Directory\n    - Number of backups\n    - Max age backups.\n- Use git for backups.\n- T Clone\u003cT\u003e(T item); deep clone by serializing and then deserializing an instance.\n- bool IsDirty\u003cT\u003e(T item, IEqualityComparer\u003cT\u003e comparer); check if an instance is dirty after last save.\n- EqualityComparers that checks structural equality by serializing and comparing bytes. If performance is an issue overloads with IEqualityComparer\u003cT\u003e are exposed.\n\n## Repository\n\nHelper class for reading and saving files. It is meant to be cached as it is expensive to instantiate.\nThere 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.\nWhen 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.\nThis makes it configurable without recompiling.\n\nIf a setting is passed in the constructor does not look on disk.\n\nThe different libraries contains repository implementations for \n- `NewtonSoft.Json`\n- `System.XmlSerializer`\n- `System.Runtime.Serialization.Formatters.Binary.BinaryFormatter`\n- `System.Runtime.Serialization.DataContractSerializer`\n\n### SingletonRepository\n\nManages singleton instances of things read or written to disk. This is useful for settings etc.\n\n### DataRepository\n\nSimple repository for reading \u0026 saving data.\n\n#### Members\n\n- GetFileInfo, for getting the file the repository uses for reading \u0026 saving.\n- Delete for deleting files \u0026 backups.\n- Exists for checking if files exists.\n- Read for reading and deserializing the contents of files.\n- ReadAsync for reading and deserializing the contents of files.\n- ReadOrCreate, read the file if it exists, create and instance and save it to disk before returning it if not.\n- Save for saving files.\n- SaveAsync for saving files.\n- CanRename, check for collisions before renaming.\n- Rename, rename files and backups.\n- ClearTrackerCache, clear the `IDirtyTracker`\n- RemoveFromDirtyTracker, remove an item from `IDirtyTracker`\n- DeleteBackups, delete backups for a file.\n\n### Migration.\n\nFor managing versions of files on disk.\nThe migrations can also be used for switching from xml to json for example.\nSample for json:\n\n```cs\nvar read = repository.Read\u003cDummySerializable\u003e(new JsonMigration(Version1To2, Version2To3));\n\nJObject Version1To2(JObject jObject)\n{\n    if (jObject[\"Version\"].Value\u003cint\u003e() == 1)\n    {\n        jObject[\"Version\"] = 2;\n        jObject.RenameProperty(\"Typo\", \"Name\");\n        return jObject;\n    }\n\n    return jObject;\n}\n\nJObject Version2To3(JObject jObject)\n{\n    if (jObject[\"Version\"].Value\u003cint\u003e() == 2)\n    {\n        jObject[\"Version\"] = 3;\n        jObject.Add(\"NewProperty\", \"default value\");\n        return jObject;\n    }\n\n    return jObject;\n}\n```\n\n### Save transaction.\n\nLocks all files that will be part of the transaction. Uses atomic writes.\n\n1. Lock `file` if exists.\n2. Lock `file.delete` if it exists.\n3. Create and lock `file.tmp` if it exists.\n4. Save to `file.tmp`\n5. Rename `file` to `file.backup if `creating backups.\n6. Rename `file.tmp`-\u003e `file`\n\nOn error everything is reset back to initial state.\n\n### Samples\n\n### Sample for reading and saving settings using git for backups.\n\n```C#\npublic class Settings\n{\n    private static readonly DirectoryInfo Directory = new DirectoryInfo(\"./Settings\");\n\n    // Initializes with  ./Settings/RepositorySettings.json is present\n    // Creates a git repository for history.\n    private readonly SingletonRepository repository = new SingletonRepository(\n        CreateDefaultSettings,\n        new GitBackuper(Directory.FullName));\n\n    public MySetting ReadFoo()\n    {\n        // Reads the contents of ./Settings/MySetting.json\n        // As we are using a SingletonRepository Read will always return the same instance.\n        return this.repository.Read\u003cMySetting\u003e();\n    }\n\n    public void Save(MySetting setting)\n    {\n        // Saves to of ./Settings/MySetting.json\n        // Commits changes to git repository.\n        this.repository.Save(setting);\n    }\n\n    private static RepositorySettings CreateDefaultSettings()\n    {\n        return new RepositorySettings(\n            directory: Directory.FullName,\n            jsonSerializerSettings: new JsonSerializerSettings { Formatting = Formatting.Indented },\n            isTrackingDirty: true,\n            backupSettings: null,\n            extension: \".json\",\n            tempExtension: \".saving\");\n    }\n}\n```\n\n### For reading and saving data\n\n```C#\npublic class Data\n{\n    // Uses %AppData%/ApplicationName.\n    // Initializes with  %AppData%/ApplicationName/RepositorySettings.cfg\n    private readonly DataRepository repository = new DataRepository();\n\n    public MyData ReadFoo()\n    {\n        // Reads the contents of %AppData%/ApplicationName/MyData.cfg\n        // As we wrap a DataRepository Read will always return a new instance.\n        return this.repository.Read\u003cMyData\u003e();\n    }\n\n    public void Save(MyData data)\n    {\n        // Saves to of %AppData%/ApplicationName/MyData.cfg\n        // Creates a backup %AppData%/ApplicationName/MyData.bak\n        this.repository.Save(data);\n    }\n}\n```\n\n### Interfaces\nA number of interfaces exposing subsets of the functionality are provided.\n\n- `IAsyncFileNameRepository` \n- `IAsyncFileInfoRepository`\n- `ICloner`\n- `IDirty`\n- `IStreamRepository`\n- `IGenericRepository`\n- `IGenericAsyncRepository`\n- A bunch of StreamRepositories for reading raw streams.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguorg%2Fgu.persist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguorg%2Fgu.persist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguorg%2Fgu.persist/lists"}