{"id":21283958,"url":"https://github.com/coronabytes/dotnet-arangodb-extensions","last_synced_at":"2025-07-11T11:31:38.304Z","repository":{"id":65357664,"uuid":"291389396","full_name":"coronabytes/dotnet-arangodb-extensions","owner":"coronabytes","description":"Serilog, DevExtreme and DataProtection extensions for .NET ArangoDB driver","archived":false,"fork":false,"pushed_at":"2024-04-26T23:40:46.000Z","size":587,"stargazers_count":11,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-28T22:06:03.306Z","etag":null,"topics":["arangodb","arangodb-client","aspnetcore","dataprotection","devextreme-query","netcore","serilog","serilog-sink"],"latest_commit_sha":null,"homepage":"","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/coronabytes.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":"2020-08-30T02:57:41.000Z","updated_at":"2024-04-26T23:38:57.000Z","dependencies_parsed_at":"2024-11-13T00:25:34.941Z","dependency_job_id":"6acb6b79-2acf-41c1-892c-8f2b04b8d38d","html_url":"https://github.com/coronabytes/dotnet-arangodb-extensions","commit_stats":null,"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fdotnet-arangodb-extensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fdotnet-arangodb-extensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fdotnet-arangodb-extensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fdotnet-arangodb-extensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coronabytes","download_url":"https://codeload.github.com/coronabytes/dotnet-arangodb-extensions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225715875,"owners_count":17512912,"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":["arangodb","arangodb-client","aspnetcore","dataprotection","devextreme-query","netcore","serilog","serilog-sink"],"created_at":"2024-11-21T11:13:17.997Z","updated_at":"2025-07-11T11:31:38.291Z","avatar_url":"https://github.com/coronabytes.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build](https://github.com/coronabytes/dotnet-arangodb-extensions/actions/workflows/build.yml/badge.svg)](https://github.com/coronabytes/dotnet-arangodb-extensions/actions/workflows/build.yml)\n\n# Extensions for .NET ArangoDB Driver\nSee [dotnet-arangodb](https://github.com/coronabytes/dotnet-arangodb)\n\n| Extension   | Nuget        | Command |\n| :---        | :---         | :---    |\n| [Core.Arango.Migration](https://www.nuget.org/packages/Core.Arango.Migration) | ![Nuget](https://img.shields.io/nuget/v/Core.Arango.Migration) ![Nuget](https://img.shields.io/nuget/dt/Core.Arango.Migration) | dotnet add package Core.Arango.Migration  |\n| [Core.Arango.DataProtection](https://www.nuget.org/packages/Core.Arango.DataProtection) | ![Nuget](https://img.shields.io/nuget/v/Core.Arango.DataProtection) ![Nuget](https://img.shields.io/nuget/dt/Core.Arango.DataProtection) | dotnet add package Core.Arango.DataProtection |\n| [Core.Arango.DevExtreme](https://www.nuget.org/packages/Core.Arango.DevExtreme) | ![Nuget](https://img.shields.io/nuget/v/Core.Arango.DevExtreme) ![Nuget](https://img.shields.io/nuget/dt/Core.Arango.DevExtreme) | dotnet add package Core.Arango.DevExtreme |\n| [Core.Arango.Serilog](https://www.nuget.org/packages/Core.Arango.Serilog) | ![Nuget](https://img.shields.io/nuget/v/Core.Arango.Serilog) ![Nuget](https://img.shields.io/nuget/dt/Core.Arango.Serilog) | dotnet add package Core.Arango.Serilog |\n\n# Migration\n- Ensures the Arango structure / model is up-to-date\n- Synchronises collection, index, graph, analyzer, views and custom functions from code model to arango db\n  - objects are compared and if they differ they will be dropped and recreated\n  - objects cannot be renamed with this method as they are matched by name and not by id\n  - collections cannot be updated (data-loss) with this method, only new ones can be created and old ones dropped\n- Database export and import support with zip-archives\n- Full and partial updates\n- Optional history collection for advanced migration scenarios, like running transformation queries\n- Still under heavy development, might toss some functions around\n\n## Extract structure to code model\n```csharp\nvar migrationService = new ArangoMigrator(Arango);\n\nvar structure = await migrationService.GetStructureAsync(\"source-database\");\n```\n\n## Apply structure from code model (dry run)\n```csharp\nvar migrationService = new ArangoMigrator(Arango);\n\nawait migrationService.ApplyStructureAsync(\"target-database\", structure, new ArangoMigrationOptions\n{\n    DryRun = true,\n    Notify = n =\u003e\n    {\n        if (n.State != ArangoMigrationState.Identical)\n\t    _output.WriteLine($\"{n.State} {n.Object} {n.Name}\");\n    }\n});\n```\n\n## Apply structure from code model \n```csharp\nvar migrationService = new ArangoMigrator(Arango);\n\nawait migrationService.ApplyStructureAsync(\"target-database\", new ArangoStructure\n{\n    Collections = new List\u003cArangoCollectionIndices\u003e\n    {\n\t    new ()\n\t    {\n\t\tCollection = new ArangoCollection\n\t\t{\n\t\t    Name = \"Project\",\n\t\t    Type = ArangoCollectionType.Document\n\t\t},\n\t\tIndices = new List\u003cArangoIndex\u003e\n\t\t{\n\t\t    new ()\n\t\t    {\n\t\t\tName = \"IDX_ParentKey\",\n\t\t\tFields = new List\u003cstring\u003e {\"ParentKey\"},\n\t\t\tType = ArangoIndexType.Hash\n\t\t    }\n\t\t}\n\t    },\n\t    new ()\n\t    {\n\t\tCollection = new ArangoCollection\n\t\t{\n\t\t    Name = \"Activity\",\n\t\t    Type = ArangoCollectionType.Document\n\t\t},\n\t\tIndices = new List\u003cArangoIndex\u003e\n\t\t{\n\t\t    new ()\n\t\t    {\n\t\t\tName = \"IDX_ProjectKey\",\n\t\t\tFields = new List\u003cstring\u003e {\"ProjectKey\"},\n\t\t\tType = ArangoIndexType.Hash\n\t\t    }\n\t\t}\n\t    }\n    }\n});\n```\n\n## Export database with structure and data to zip archive\n```csharp\nvar migrationService = new ArangoMigrator(Arango);\n\nawait using var fs = File.Create(\"export.zip\", 1024 * 1024);\nawait migrationService.ExportAsync(\"source-database\", fs, ArangoMigrationScope.Data | ArangoMigrationScope.Structure);\n```\n\n## Import database with structure and data from zip archive\n```csharp\nvar migrationService = new ArangoMigrator(Arango);\n\nawait using var fs = File.OpenRead(\"export.zip\");\nawait migrationService.ImportAsync(\"target-database\", fs, ArangoMigrationScope.Data | ArangoMigrationScope.Structure);\n```\n\n## Advanced migrations with history collection\n```csharp\nvar migrator = new ArangoMigrator(Arango);\nmigrator.HistoryCollection = \"MigrationHistory\";\n\n// load all migrations from assembly\nmigrator.AddMigrations(typeof(Program).Assembly);\n\n// apply all migrations up to latest\nawait migrator.UpgradeAsync(\"target-database\");\n\n// sample migration / downgrades not yet supported\npublic class M20210401_001 : IArangoMigration\n{\n    public long Id =\u003e 20210401_001; // sortable unique id\n    public string Name =\u003e \"Initial\";\n    \n    public async Task Up(IArangoMigrator migrator, ArangoHandle handle)\n    {\n        await migrator.ApplyStructureAsync(...);\t\n\tawait migrator.Context.Query.ExecuteAsync(...);\n    }\n\n    public Task Down(IArangoMigrator migrator, ArangoHandle handle)\n    {\n        throw new NotImplementedException();\n    }\n}\n```\n\n# DataProtection\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddSingleton(new ArangoContext(Configuration.GetConnectionString(\"Arango\")));\n\n    var dataProtection = services.AddDataProtection()\n        .SetApplicationName(\"App\")\n        .SetDefaultKeyLifetime(TimeSpan.FromDays(30));\n    dataProtection.PersistKeysToArangoDB(database: \"dataprotection\", collection: \"keys\");\n}\n```\n\n# DevExtreme\n- Translates DevExtreme queries to AQL with filtering, sorting, grouping and summaries on a 'best effort basis'\n- Parameters are escaped with bindvars\n- Property names \n  - need to match ^[A-Za-z_][A-Za-z0-9\\\\.]*$\n  - need to be within 128 characters\n  - can be customized with ValidPropertyName() and PropertyTransform()\n- Developer retains full control over the projection - full document by default\n- Check safety limits in settings if your query fails\n- Support for ArangoSearch is coming soon\n  - Not so soon...\n- Groupings by foreign key can be enriched with displayValue using GroupLookups()\n```csharp\n\nprivate static readonly ArangoTransformSettings Transform = new ArangoTransformSettings\n{\n    IteratorVar = \"x\",\n    Key = \"key\",\n    Filter = \"x.Active == true\",\n    RestrictGroups = new HashSet\u003cstring\u003e\n\t{\n\t\t\"ProjectKey\", \"UserKey\"\n\t}, // null allows all groupings (not recommended) / empty hashset disables grouping\n\tGroupLookups = new Dictionary\u003cstring, string\u003e\n\t{\n\t\t[\"ProjectKey\"] = \"DOCUMENT(AProject, ProjectKey).Name\",\n\t\t[\"UserKey\"] = \"DOCUMENT(AUser, UserKey).Name\"\n\t}\n};\n\n[HttpGet(\"dx-query\")]\npublic async Task\u003cActionResult\u003cDxLoadResult\u003e\u003e DxQuery([FromQuery] DataSourceLoadOptions loadOptions)\n{\n    var arangoTransform = new ArangoTransform(loadOptions, Transform);\n\n    if (!arangoTransform.Transform(out var error))\n        return BadRequest(error);\n\n    return await arangoTransform.ExecuteAsync\u003cSomeEntity\u003e(arango, \"database\", \"collection\");\n}\n```\n\n# Serilog\n```csharp\nbuilder.Host.UseSerilog(\n    (c, log) =\u003e\n    {\n        var arango = builder.Configuration.GetConnectionString(\"Arango\");\n        var sink = new ArangoSerilogSink(new ArangoContext(arango), \n            database: \"logs\", \n            logs: \"logs\", \n            renderMessage: ArangoSerilogSink.LoggingRenderStrategy.RenderMessage | ArangoSerilogSink.LoggingRenderStrategy.StoreTemplate , \n            indexLevel: true,\n            indexTimestamp: true,\n            indexTemplate: true);\n\n        log.Enrich.FromLogContext();\n        log.WriteTo.Console(theme: AnsiConsoleTheme.Code);\n        log.WriteTo.Sink(new PeriodicBatchingSink(sink, new PeriodicBatchingSinkOptions\n        {\n            BatchSizeLimit = 1000,\n            QueueLimit = 100000,\n            Period = TimeSpan.FromSeconds(2),\n            EagerlyEmitFirstEvent = true\n        }));\n    });\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoronabytes%2Fdotnet-arangodb-extensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoronabytes%2Fdotnet-arangodb-extensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoronabytes%2Fdotnet-arangodb-extensions/lists"}