From a46c3bf452d287b50b2e7dd5a24f5995c9fd26f6 Mon Sep 17 00:00:00 2001 From: vnugent Date: Mon, 9 Jan 2023 15:09:13 -0500 Subject: Restructure --- .../Abstractions/IBulkDataStore.cs | 65 --- .../Abstractions/IDataStore.cs | 123 ----- .../Abstractions/IPaginatedDataStore.cs | 56 --- .../Abstractions/IUserEntity.cs | 39 -- VNLib.Plugins.Extensions.Data/DbModelBase.cs | 48 -- VNLib.Plugins.Extensions.Data/DbStore.cs | 507 -------------------- VNLib.Plugins.Extensions.Data/Extensions.cs | 81 ---- VNLib.Plugins.Extensions.Data/IDbModel.cs | 52 -- VNLib.Plugins.Extensions.Data/ProtectedDbStore.cs | 70 --- .../ProtectedEntityExtensions.cs | 86 ---- VNLib.Plugins.Extensions.Data/SQL/DbExtensions.cs | 521 --------------------- .../SQL/EnumerableTable.cs | 118 ----- .../SQL/SqlColumnNameAttribute.cs | 54 --- .../SQL/SqlTableNameAttribute.cs | 40 -- VNLib.Plugins.Extensions.Data/SQL/SqlVariable.cs | 58 --- VNLib.Plugins.Extensions.Data/SQL/TableManager.cs | 66 --- VNLib.Plugins.Extensions.Data/Storage/Blob.cs | 244 ---------- .../Storage/BlobExtensions.cs | 67 --- VNLib.Plugins.Extensions.Data/Storage/BlobStore.cs | 162 ------- .../Storage/LWDecriptorCreationException.cs | 45 -- .../Storage/LWStorageContext.cs | 48 -- .../Storage/LWStorageDescriptor.cs | 230 --------- .../Storage/LWStorageEntry.cs | 44 -- .../Storage/LWStorageManager.cs | 347 -------------- .../Storage/LWStorageRemoveFailedException.cs | 44 -- .../Storage/LWStorageUpdateFailedException.cs | 43 -- .../Storage/UndefinedBlobStateException.cs | 45 -- .../TransactionalDbContext.cs | 99 ---- .../VNLib.Plugins.Extensions.Data.csproj | 54 --- .../VNLib.Plugins.Extensions.Data.xml | 476 ------------------- 30 files changed, 3932 deletions(-) delete mode 100644 VNLib.Plugins.Extensions.Data/Abstractions/IBulkDataStore.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Abstractions/IDataStore.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Abstractions/IPaginatedDataStore.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Abstractions/IUserEntity.cs delete mode 100644 VNLib.Plugins.Extensions.Data/DbModelBase.cs delete mode 100644 VNLib.Plugins.Extensions.Data/DbStore.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Extensions.cs delete mode 100644 VNLib.Plugins.Extensions.Data/IDbModel.cs delete mode 100644 VNLib.Plugins.Extensions.Data/ProtectedDbStore.cs delete mode 100644 VNLib.Plugins.Extensions.Data/ProtectedEntityExtensions.cs delete mode 100644 VNLib.Plugins.Extensions.Data/SQL/DbExtensions.cs delete mode 100644 VNLib.Plugins.Extensions.Data/SQL/EnumerableTable.cs delete mode 100644 VNLib.Plugins.Extensions.Data/SQL/SqlColumnNameAttribute.cs delete mode 100644 VNLib.Plugins.Extensions.Data/SQL/SqlTableNameAttribute.cs delete mode 100644 VNLib.Plugins.Extensions.Data/SQL/SqlVariable.cs delete mode 100644 VNLib.Plugins.Extensions.Data/SQL/TableManager.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/Blob.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/BlobExtensions.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/BlobStore.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/LWDecriptorCreationException.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/LWStorageContext.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/LWStorageDescriptor.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/LWStorageEntry.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/LWStorageManager.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/LWStorageRemoveFailedException.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/LWStorageUpdateFailedException.cs delete mode 100644 VNLib.Plugins.Extensions.Data/Storage/UndefinedBlobStateException.cs delete mode 100644 VNLib.Plugins.Extensions.Data/TransactionalDbContext.cs delete mode 100644 VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.csproj delete mode 100644 VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.xml (limited to 'VNLib.Plugins.Extensions.Data') diff --git a/VNLib.Plugins.Extensions.Data/Abstractions/IBulkDataStore.cs b/VNLib.Plugins.Extensions.Data/Abstractions/IBulkDataStore.cs deleted file mode 100644 index b644ec3..0000000 --- a/VNLib.Plugins.Extensions.Data/Abstractions/IBulkDataStore.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: IBulkDataStore.cs -* -* IBulkDataStore.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System.Collections.Generic; -using System.Threading.Tasks; - -using VNLib.Utils; - -namespace VNLib.Plugins.Extensions.Data.Abstractions -{ - /// - /// An abstraction that defines a Data-Store that supports - /// bulk data operations - /// - /// The data-model type - public interface IBulkDataStore - { - /// - /// Deletes a collection of records from the store - /// - /// A collection of records to delete - ///A task the resolves the number of entires removed from the store - Task DeleteBulkAsync(ICollection records); - /// - /// Updates a collection of records - /// - /// The collection of records to update - /// A task the resolves an error code (should evaluate to false on failure, and true on success) - Task UpdateBulkAsync(ICollection records); - /// - /// Creates a bulk collection of records as entries in the store - /// - /// The collection of records to add - /// A task the resolves an error code (should evaluate to false on failure, and true on success) - Task CreateBulkAsync(ICollection records); - /// - /// Creates or updates individual records from a bulk collection of records - /// - /// The collection of records to add - /// A task the resolves an error code (should evaluate to false on failure, and true on success) - Task AddOrUpdateBulkAsync(ICollection records); - } - -} diff --git a/VNLib.Plugins.Extensions.Data/Abstractions/IDataStore.cs b/VNLib.Plugins.Extensions.Data/Abstractions/IDataStore.cs deleted file mode 100644 index 4ab62bf..0000000 --- a/VNLib.Plugins.Extensions.Data/Abstractions/IDataStore.cs +++ /dev/null @@ -1,123 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: IDataStore.cs -* -* IDataStore.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Threading.Tasks; -using System.Collections.Generic; - -using VNLib.Utils; - -namespace VNLib.Plugins.Extensions.Data.Abstractions -{ - /// - /// An abstraction that defines a Data-Store and common - /// operations that retrieve or manipulate records of data - /// - /// The data-model type - public interface IDataStore - { - /// - /// Gets the total number of records in the current store - /// - /// A task that resolves the number of records in the store - Task GetCountAsync(); - /// - /// Gets the number of records that belong to the specified constraint - /// - /// A specifier to constrain the reults - /// The number of records that belong to the specifier - Task GetCountAsync(string specifier); - /// - /// Gets a record from its key - /// - /// The key identifying the unique record - /// A promise that resolves the record identified by the specified key - Task GetSingleAsync(string key); - /// - /// Gets a record from its key - /// - /// A variable length specifier arguemnt array for retreiving a single application - /// - Task GetSingleAsync(params string[] specifiers); - /// - /// Gets a record from the store with a partial model, intended to complete the model - /// - /// The partial model used to query the store - /// A task the resolves the completed data-model - Task GetSingleAsync(T record); - /// - /// Fills a collection with enires retireved from the store using the specifer - /// - /// The collection to add entires to - /// A specifier argument to constrain results - /// The maximum number of elements to retrieve - /// A Task the resolves to the number of items added to the collection - Task GetCollectionAsync(ICollection collection, string specifier, int limit); - /// - /// Fills a collection with enires retireved from the store using a variable length specifier - /// parameter - /// - /// The collection to add entires to - /// The maximum number of elements to retrieve - /// - /// A Task the resolves to the number of items added to the collection - Task GetCollectionAsync(ICollection collection, int limit, params string[] args); - /// - /// Updates an entry in the store with the specified record - /// - /// The record to update - /// A task the resolves an error code (should evaluate to false on failure, and true on success) - Task UpdateAsync(T record); - /// - /// Creates a new entry in the store representing the specified record - /// - /// The record to add to the store - /// A task the resolves an error code (should evaluate to false on failure, and true on success) - Task CreateAsync(T record); - /// - /// Deletes one or more entrires from the store matching the specified record - /// - /// The record to remove from the store - /// A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success) - Task DeleteAsync(T record); - /// - /// Deletes one or more entires from the store matching the specified unique key - /// - /// The unique key that identifies the record - /// A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success) - Task DeleteAsync(string key); - /// - /// Deletes one or more entires from the store matching the supplied specifiers - /// - /// A variable length array of specifiers used to delete one or more entires - /// A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success) - Task DeleteAsync(params string[] specifiers); - /// - /// Updates an entry in the store if it exists, or creates a new entry if one does not already exist - /// - /// The record to add to the store - /// A task the resolves the result of the operation - Task AddOrUpdateAsync(T record); - } -} diff --git a/VNLib.Plugins.Extensions.Data/Abstractions/IPaginatedDataStore.cs b/VNLib.Plugins.Extensions.Data/Abstractions/IPaginatedDataStore.cs deleted file mode 100644 index 4a9cb89..0000000 --- a/VNLib.Plugins.Extensions.Data/Abstractions/IPaginatedDataStore.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: IPaginatedDataStore.cs -* -* IPaginatedDataStore.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace VNLib.Plugins.Extensions.Data.Abstractions -{ - /// - /// Defines a Data-Store that can retirieve and manipulate paginated - /// data - /// - /// The data-model type - public interface IPaginatedDataStore - { - /// - /// Gets a collection of records using a pagination style query, and adds the records to the collecion - /// - /// The collection to add records to - /// Pagination page to get records from - /// The maximum number of items to retrieve from the store - /// A task that resolves the number of items added to the collection - Task GetPageAsync(ICollection collection, int page, int limit); - /// - /// Gets a collection of records using a pagination style query with constraint arguments, and adds the records to the collecion - /// - /// The collection to add records to - /// Pagination page to get records from - /// The maximum number of items to retrieve from the store - /// A params array of strings to constrain the result set from the db - /// A task that resolves the number of items added to the collection - Task GetPageAsync(ICollection collection, int page, int limit, params string[] constraints); - } - -} diff --git a/VNLib.Plugins.Extensions.Data/Abstractions/IUserEntity.cs b/VNLib.Plugins.Extensions.Data/Abstractions/IUserEntity.cs deleted file mode 100644 index aaca7c0..0000000 --- a/VNLib.Plugins.Extensions.Data/Abstractions/IUserEntity.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: IUserEntity.cs -* -* IUserEntity.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; - -namespace VNLib.Plugins.Extensions.Data.Abstractions -{ - /// - /// Defines an entity base that has an owner, identified by its user-id - /// - public interface IUserEntity - { - /// - /// The user-id of the owner of the entity - /// - string? UserId { get; set; } - } -} diff --git a/VNLib.Plugins.Extensions.Data/DbModelBase.cs b/VNLib.Plugins.Extensions.Data/DbModelBase.cs deleted file mode 100644 index b48231b..0000000 --- a/VNLib.Plugins.Extensions.Data/DbModelBase.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: DbModelBase.cs -* -* DbModelBase.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; - -namespace VNLib.Plugins.Extensions.Data -{ - /// - /// Provides a base for DBSet Records with a timestamp/version - /// a unique ID key, and create/modified timestamps - /// - public abstract class DbModelBase : IDbModel - { - /// - public abstract string Id { get; set; } - /// - [Timestamp] - [JsonIgnore] - public virtual byte[]? Version { get; set; } - /// - public abstract DateTime Created { get; set; } - /// - public abstract DateTime LastModified { get; set; } - } -} diff --git a/VNLib.Plugins.Extensions.Data/DbStore.cs b/VNLib.Plugins.Extensions.Data/DbStore.cs deleted file mode 100644 index 8cf4e2e..0000000 --- a/VNLib.Plugins.Extensions.Data/DbStore.cs +++ /dev/null @@ -1,507 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: DbStore.cs -* -* DbStore.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -using Microsoft.EntityFrameworkCore; - -using VNLib.Utils; -using VNLib.Utils.Memory.Caching; -using VNLib.Plugins.Extensions.Data.Abstractions; - -namespace VNLib.Plugins.Extensions.Data -{ - /// - /// Implements basic data-store functionality with abstract query builders - /// - /// A implemented type - public abstract class DbStore : IDataStore, IPaginatedDataStore where T: class, IDbModel - { - /// - /// Gets a unique ID for a new record being added to the store - /// - public abstract string RecordIdBuilder { get; } - /// - /// Gets a new ready for use - /// - /// - public abstract TransactionalDbContext NewContext(); - - /// - /// An object rental for entity collections - /// - public ObjectRental> ListRental { get; } = ObjectRental.Create>(null, static ret => ret.Clear()); - - #region Add Or Update - /// - public virtual async Task AddOrUpdateAsync(T record) - { - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - IQueryable query; - if (string.IsNullOrWhiteSpace(record.Id)) - { - //Get the application - query = AddOrUpdateQueryBuilder(ctx, record); - } - else - { - //Get the application - query = (from et in ctx.Set() - where et.Id == record.Id - select et); - } - //Using single - T? entry = await query.SingleOrDefaultAsync(); - //Check if creted - if (entry == null) - { - //Create a new template id - record.Id = RecordIdBuilder; - //Set the created/lm times - record.Created = record.LastModified = DateTime.UtcNow; - //Add the new template to the ctx - ctx.Add(record); - } - else - { - OnRecordUpdate(record, entry); - } - //Save changes - ERRNO result = await ctx.SaveChangesAsync(); - if (result) - { - //commit transaction if update was successful - await ctx.CommitTransactionAsync(); - } - return result; - } - /// - public virtual async Task UpdateAsync(T record) - { - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the application - IQueryable query = UpdateQueryBuilder(ctx, record); - //Using single to make sure only one app is in the db (should never be an issue) - T? oldEntry = await query.SingleOrDefaultAsync(); - if (oldEntry == null) - { - return false; - } - //Update the template meta-data - OnRecordUpdate(record, oldEntry); - //Only publish update if changes happened - if (!ctx.ChangeTracker.HasChanges()) - { - //commit transaction if no changes need to be made - await ctx.CommitTransactionAsync(); - return true; - } - //Save changes - ERRNO result = await ctx.SaveChangesAsync(); - if (result) - { - //commit transaction if update was successful - await ctx.CommitTransactionAsync(); - } - return result; - } - /// - public virtual async Task CreateAsync(T record) - { - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Create a new template id - record.Id = RecordIdBuilder; - //Update the created/last modified time of the record - record.Created = record.LastModified = DateTime.UtcNow; - //Add the new template - ctx.Add(record); - //save changes - ERRNO result = await ctx.SaveChangesAsync(); - if (result) - { - //Commit transaction - await ctx.CommitTransactionAsync(); - } - return result; - } - - /// - /// Builds a query that attempts to get a single entry from the - /// store based on the specified record if it does not have a - /// valid property - /// - /// The active context to query - /// The record to search for - /// A query that yields a single record if it exists in the store - protected virtual IQueryable AddOrUpdateQueryBuilder(TransactionalDbContext context, T record) - { - //default to get single of the specific record - return GetSingleQueryBuilder(context, record); - } - /// - /// Builds a query that attempts to get a single entry from the - /// store to update based on the specified record - /// - /// The active context to query - /// The record to search for - /// A query that yields a single record to update if it exists in the store - protected virtual IQueryable UpdateQueryBuilder(TransactionalDbContext context, T record) - { - //default to get single of the specific record - return GetSingleQueryBuilder(context, record); - } - /// - /// Updates the current record (if found) to the new record before - /// storing the updates. - /// - /// The new record to capture data from - /// The current record to be updated - protected abstract void OnRecordUpdate(T newRecord, T currentRecord); - #endregion - - #region Delete - /// - public virtual async Task DeleteAsync(string key) - { - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the template by its id - IQueryable query = (from temp in ctx.Set() - where temp.Id == key - select temp); - T? record = await query.SingleOrDefaultAsync(); - if (record == null) - { - return false; - } - //Add the new application - ctx.Remove(record); - //Save changes - ERRNO result = await ctx.SaveChangesAsync(); - if (result) - { - //Commit transaction - await ctx.CommitTransactionAsync(); - } - return result; - } - /// - public virtual async Task DeleteAsync(T record) - { - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get a query for a a single item - IQueryable query = GetSingleQueryBuilder(ctx, record); - //Get the entry - T? entry = await query.SingleOrDefaultAsync(); - if (entry == null) - { - return false; - } - //Add the new application - ctx.Remove(entry); - //Save changes - ERRNO result = await ctx.SaveChangesAsync(); - if (result) - { - //Commit transaction - await ctx.CommitTransactionAsync(); - } - return result; - } - /// - public virtual async Task DeleteAsync(params string[] specifiers) - { - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the template by its id - IQueryable query = DeleteQueryBuilder(ctx, specifiers); - T? entry = await query.SingleOrDefaultAsync(); - if (entry == null) - { - return false; - } - //Add the new application - ctx.Remove(entry); - //Save changes - ERRNO result = await ctx.SaveChangesAsync(); - if (result) - { - //Commit transaction - await ctx.CommitTransactionAsync(); - } - return result; - } - - /// - /// Builds a query that results in a single entry to delete from the - /// constraint arguments - /// - /// The active context - /// A variable length parameter array of query constraints - /// A query that yields a single record (or no record) to delete - protected virtual IQueryable DeleteQueryBuilder(TransactionalDbContext context, params string[] constraints) - { - //default use the get-single method, as the implementation is usually identical - return GetSingleQueryBuilder(context, constraints); - } - #endregion - - #region Get Collection - /// - public virtual async Task GetCollectionAsync(ICollection collection, string specifier, int limit) - { - int previous = collection.Count; - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the single template by its id - await GetCollectionQueryBuilder(ctx, specifier).Take(limit).Select(static e => e).ForEachAsync(collection.Add); - //close db and transaction - await ctx.CommitTransactionAsync(); - //Return the number of elements add to the collection - return collection.Count - previous; - } - /// - public virtual async Task GetCollectionAsync(ICollection collection, int limit, params string[] args) - { - int previous = collection.Count; - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the single template by its id - await GetCollectionQueryBuilder(ctx, args).Take(limit).Select(static e => e).ForEachAsync(collection.Add); - //close db and transaction - await ctx.CommitTransactionAsync(); - //Return the number of elements add to the collection - return collection.Count - previous; - } - - /// - /// Builds a query to get a count of records constrained by the specifier - /// - /// The active context to run the query on - /// The specifier constrain - /// A query that can be counted - protected virtual IQueryable GetCollectionQueryBuilder(TransactionalDbContext context, string specifier) - { - return GetCollectionQueryBuilder(context, new string[] { specifier }); - } - - /// - /// Builds a query to get a collection of records based on an variable length array of parameters - /// - /// The active context to run the query on - /// An arguments array to constrain the results of the query - /// A query that returns a collection of records from the store - protected abstract IQueryable GetCollectionQueryBuilder(TransactionalDbContext context, params string[] constraints); - - #endregion - - #region Get Count - /// - public virtual async Task GetCountAsync() - { - //Open db connection - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Async get the number of records of the given entity type - long count = await ctx.Set().LongCountAsync(); - //close db and transaction - await ctx.CommitTransactionAsync(); - return count; - } - /// - public virtual async Task GetCountAsync(string specifier) - { - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Async get the number of records of the given entity type - long count = await GetCountQueryBuilder(ctx, specifier).LongCountAsync(); - //close db and transaction - await ctx.CommitTransactionAsync(); - return count; - } - - /// - /// Builds a query to get a count of records constrained by the specifier - /// - /// The active context to run the query on - /// The specifier constrain - /// A query that can be counted - protected virtual IQueryable GetCountQueryBuilder(TransactionalDbContext context, string specifier) - { - //Default use the get collection and just call the count method - return GetCollectionQueryBuilder(context, specifier); - } - #endregion - - #region Get Single - /// - public virtual async Task GetSingleAsync(string key) - { - //Open db connection - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the single template by its id - T? record = await (from entry in ctx.Set() - where entry.Id == key - select entry) - .SingleOrDefaultAsync(); - //close db and transaction - await ctx.CommitTransactionAsync(); - return record; - } - /// - public virtual async Task GetSingleAsync(T record) - { - //Open db connection - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the single template by its id - T? entry = await GetSingleQueryBuilder(ctx, record).SingleOrDefaultAsync(); - //close db and transaction - await ctx.CommitTransactionAsync(); - return record; - } - /// - public virtual async Task GetSingleAsync(params string[] specifiers) - { - //Open db connection - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the single template by its id - T? record = await GetSingleQueryBuilder(ctx, specifiers).SingleOrDefaultAsync(); - //close db and transaction - await ctx.CommitTransactionAsync(); - return record; - } - /// - /// Builds a query to get a single record from the variable length parameter arguments - /// - /// The context to execute query against - /// Arguments to constrain the results of the query to a single record - /// A query that yields a single record - protected abstract IQueryable GetSingleQueryBuilder(TransactionalDbContext context, params string[] constraints); - /// - /// - /// Builds a query to get a single record from the specified record. - /// - /// - /// Unless overridden, performs an ID based query for a single entry - /// - /// - /// The context to execute query against - /// A record to referrence the lookup - /// A query that yields a single record - protected virtual IQueryable GetSingleQueryBuilder(TransactionalDbContext context, T record) - { - return from entry in context.Set() - where entry.Id == record.Id - select entry; - } - #endregion - - #region Get Page - /// - public virtual async Task GetPageAsync(ICollection collection, int page, int limit) - { - //Store preivous count - int previous = collection.Count; - //Open db connection - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get a page offset and a limit for the - await ctx.Set() - .Skip(page * limit) - .Take(limit) - .Select(static p => p) - .ForEachAsync(collection.Add); - - //close db and transaction - await ctx.CommitTransactionAsync(); - //Return the number of records added - return collection.Count - previous; - } - /// - public virtual async Task GetPageAsync(ICollection collection, int page, int limit, params string[] constraints) - { - //Store preivous count - int previous = collection.Count; - //Open new db context - await using TransactionalDbContext ctx = NewContext(); - //Open transaction - await ctx.OpenTransactionAsync(); - //Get the single template by its id - await GetPageQueryBuilder(ctx, constraints) - .Skip(page * limit) - .Take(limit) - .Select(static e => e) - .ForEachAsync(collection.Add); - - //close db and transaction - await ctx.CommitTransactionAsync(); - //Return the number of records added - return collection.Count - previous; - } - /// - /// Builds a query to get a collection of records based on an variable length array of parameters - /// - /// The active context to run the query on - /// An arguments array to constrain the results of the query - /// A query that returns a paginated collection of records from the store - protected virtual IQueryable GetPageQueryBuilder(TransactionalDbContext context, params string[] constraints) - { - //Default to getting the entire collection and just selecting a single page - return GetCollectionQueryBuilder(context, constraints); - } - #endregion - } -} diff --git a/VNLib.Plugins.Extensions.Data/Extensions.cs b/VNLib.Plugins.Extensions.Data/Extensions.cs deleted file mode 100644 index 1e0c8de..0000000 --- a/VNLib.Plugins.Extensions.Data/Extensions.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: Extensions.cs -* -* Extensions.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Linq; -using System.Threading.Tasks; -using System.Collections.Generic; - -using Microsoft.EntityFrameworkCore; - -using VNLib.Utils; -using VNLib.Plugins.Extensions.Data.Abstractions; - -namespace VNLib.Plugins.Extensions.Data -{ - public static class Extensions - { - - public static int GetPageOrDefault(this IReadOnlyDictionary queryArgs, int @default, int minClamp = 0, int maxClamp = int.MaxValue) - { - return queryArgs.TryGetValue("page", out string? pageStr) && int.TryParse(pageStr, out int page) ? Math.Clamp(page, minClamp, maxClamp) : @default; - } - - public static int GetLimitOrDefault(this IReadOnlyDictionary queryArgs, int @default, int minClamp = 0, int maxClamp = int.MaxValue) - { - return queryArgs.TryGetValue("limit", out string? limitStr) && int.TryParse(limitStr, out int limit) ? Math.Clamp(limit, minClamp, maxClamp) : @default; - } - - public static async Task AddBulkAsync(this DbStore store, IEnumerable records, string userId, bool overwriteTime = true) - where TEntity : class, IDbModel, IUserEntity - { - //Open context and transaction - await using TransactionalDbContext database = store.NewContext(); - await database.OpenTransactionAsync(); - //Get the entity set - DbSet set = database.Set(); - //Generate random ids for the feeds and set user-id - foreach (TEntity entity in records) - { - entity.Id = store.RecordIdBuilder; - //Explicitly assign the user-id - entity.UserId = userId; - //If the entity has the default created time, update it, otherwise leave it as is - if (overwriteTime || entity.Created == default) - { - entity.Created = DateTime.UtcNow; - } - //Update last-modified time - entity.LastModified = DateTime.UtcNow; - } - //Add feeds to database - set.AddRange(records); - //Commit changes - ERRNO count = await database.SaveChangesAsync(); - //Commit transaction and exit - await database.CommitTransactionAsync(); - return count; - } - } -} diff --git a/VNLib.Plugins.Extensions.Data/IDbModel.cs b/VNLib.Plugins.Extensions.Data/IDbModel.cs deleted file mode 100644 index 8dbc2e4..0000000 --- a/VNLib.Plugins.Extensions.Data/IDbModel.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: IDbModel.cs -* -* IDbModel.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; - -namespace VNLib.Plugins.Extensions.Data -{ - /// - /// Represents a basic data model for an EFCore entity - /// for support in data-stores - /// - public interface IDbModel - { - /// - /// A unique id for the entity - /// - string Id { get; set; } - /// - /// The the entity was created in the store - /// - DateTime Created { get; set; } - /// - /// The the entity was last modified in the store - /// - DateTime LastModified { get; set; } - /// - /// Entity concurrency token - /// - byte[]? Version { get; set; } - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/ProtectedDbStore.cs b/VNLib.Plugins.Extensions.Data/ProtectedDbStore.cs deleted file mode 100644 index 654d52c..0000000 --- a/VNLib.Plugins.Extensions.Data/ProtectedDbStore.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: ProtectedDbStore.cs -* -* ProtectedDbStore.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Linq; - -using VNLib.Plugins.Extensions.Data.Abstractions; - -namespace VNLib.Plugins.Extensions.Data -{ -#nullable enable - /// - /// A data store that provides unique identities and protections based on an entity that has an owner - /// - public abstract class ProtectedDbStore : DbStore where TEntity : class, IDbModel, IUserEntity - { - /// - protected override IQueryable GetCollectionQueryBuilder(TransactionalDbContext context, params string[] constraints) - { - string userId = constraints[0]; - //Query items for the user and its id - return from item in context.Set() - where item.UserId == userId - orderby item.Created descending - select item; - } - - /// - /// Gets a single item contrained by a given user-id and item id - /// - /// - /// - /// - protected override IQueryable GetSingleQueryBuilder(TransactionalDbContext context, params string[] constraints) - { - string key = constraints[0]; - string userId = constraints[1]; - //Query items for the user and its id - return from item in context.Set() - where item.Id == key && item.UserId == userId - select item; - } - /// - protected override IQueryable GetSingleQueryBuilder(TransactionalDbContext context, TEntity record) - { - return this.GetSingleQueryBuilder(context, record.Id, record.UserId); - } - } -} diff --git a/VNLib.Plugins.Extensions.Data/ProtectedEntityExtensions.cs b/VNLib.Plugins.Extensions.Data/ProtectedEntityExtensions.cs deleted file mode 100644 index 7c4f4a3..0000000 --- a/VNLib.Plugins.Extensions.Data/ProtectedEntityExtensions.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: ProtectedEntityExtensions.cs -* -* ProtectedEntityExtensions.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -using VNLib.Utils; -using VNLib.Plugins.Extensions.Data.Abstractions; - -namespace VNLib.Plugins.Extensions.Data -{ - public static class ProtectedEntityExtensions - { - /// - /// Updates the specified record within the store - /// - /// - /// The record to update - /// The userid of the record owner - /// A task that evaluates to the number of records modified - public static Task UpdateAsync(this IDataStore store, TEntity record, string userId) where TEntity : class, IDbModel, IUserEntity - { - record.UserId = userId; - return store.UpdateAsync(record); - } - - /// - /// Updates the specified record within the store - /// - /// - /// The record to update - /// The userid of the record owner - /// A task that evaluates to the number of records modified - public static Task CreateAsync(this IDataStore store, TEntity record, string userId) where TEntity : class, IDbModel, IUserEntity - { - record.UserId = userId; - return store.CreateAsync(record); - } - - /// - /// Gets a single entity from its ID and user-id - /// - /// - /// The unique id of the entity - /// The user's id that owns the resource - /// A task that resolves the entity or null if not found - public static Task GetSingleAsync(this IDataStore store, string key, string userId) where TEntity : class, IDbModel, IUserEntity - { - return store.GetSingleAsync(key, userId); - } - - /// - /// Deletes a single entiry by its ID only if it belongs to the speicifed user - /// - /// - /// The unique id of the entity - /// The user's id that owns the resource - /// A task the resolves the number of eneities deleted (should evaluate to true or false) - public static Task DeleteAsync(this IDataStore store, string key, string userId) where TEntity : class, IDbModel, IUserEntity - { - return store.DeleteAsync(key, userId); - } - } -} diff --git a/VNLib.Plugins.Extensions.Data/SQL/DbExtensions.cs b/VNLib.Plugins.Extensions.Data/SQL/DbExtensions.cs deleted file mode 100644 index 1f9164e..0000000 --- a/VNLib.Plugins.Extensions.Data/SQL/DbExtensions.cs +++ /dev/null @@ -1,521 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: DbExtensions.cs -* -* DbExtensions.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Data; -using System.Reflection; -using System.Data.Common; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -using VNLib.Utils; -using VNLib.Utils.Memory.Caching; - -namespace VNLib.Plugins.Extensions.Data.SQL -{ - /// - /// Provides basic extension methods for ADO.NET abstract classes - /// for rapid development - /// - public static class DbExtensions - { - /* - * Object rental for propery dictionaries used for custom result objects - */ - private static ObjectRental> DictStore { get; } = ObjectRental.Create>(null, static dict => dict.Clear(), 20); - - - /// - /// Creates a new configured for with the specified value - /// and adds it to the command. - /// - /// - /// The parameter name - /// The value of the parameter - /// The of the column - /// Are null types allowed in the value parameter - /// The created parameter - public static DbParameter AddParameter(this DbCommand cmd, string @name, T @value, DbType @type, bool @nullable = false) - { - //Create the new parameter from command - DbParameter param = cmd.CreateParameter(); - //Set parameter variables - param.ParameterName = name; - param.Value = value; - param.DbType = type; - //Force non null mapping - param.SourceColumnNullMapping = nullable; - //Specify input parameter - param.Direction = ParameterDirection.Input; - //Add param to list - cmd.Parameters.Add(param); - return param; - } - /// - /// Creates a new configured for with the specified value - /// and adds it to the command. - /// - /// - /// The parameter name - /// The value of the parameter - /// The of the column - /// Size of the data value - /// Are null types allowed in the value parameter - /// The created parameter - public static DbParameter AddParameter(this DbCommand cmd, string @name, T @value, DbType @type, int @size, bool @nullable = false) - { - DbParameter param = AddParameter(cmd, name, value, type, nullable); - //Set size parameter - param.Size = size; - return param; - } - /// - /// Creates a new configured for with the specified value - /// and adds it to the command. - /// - /// - /// The parameter name - /// The value of the parameter - /// The of the column - /// Are null types allowed in the value parameter - /// The created parameter - public static DbParameter AddOutParameter(this DbCommand cmd, string @name, T @value, DbType @type, bool @nullable = false) - { - //Create the new parameter from command - DbParameter param = AddParameter(cmd, name, value, type, nullable); - //Specify output parameter - param.Direction = ParameterDirection.Output; - return param; - } - /// - /// Creates a new configured for with the specified value - /// and adds it to the command. - /// - /// - /// The parameter name - /// The value of the parameter - /// The of the column - /// Size of the data value - /// Are null types allowed in the value parameter - /// The created parameter - public static DbParameter AddOutParameter(this DbCommand cmd, string @name, T @value, DbType @type, int @size, bool @nullable = false) - { - DbParameter param = AddOutParameter(cmd, name, value, type, nullable); - //Set size parameter - param.Size = size; - return param; - } - - /// - /// Creates a new for with the specified command - /// - /// - /// The command to run against the connection - /// The initalized - public static DbCommand CreateTextCommand(this DbConnection db, string cmdText) - { - //Create the new command - DbCommand cmd = db.CreateCommand(); - cmd.CommandText = cmdText; - cmd.CommandType = CommandType.Text; //Specify text command - return cmd; - } - /// - /// Creates a new for with the specified procedure name - /// - /// - /// The name of the stored proecedure to execute - /// The initalized - public static DbCommand CreateProcedureCommand(this DbConnection db, string procedureName) - { - //Create the new command - DbCommand cmd = db.CreateCommand(); - cmd.CommandText = procedureName; - cmd.CommandType = CommandType.StoredProcedure; //Specify stored procedure - return cmd; - } - - /// - /// Creates a new for with the specified command - /// on a given transaction - /// - /// - /// The command to run against the connection - /// The transaction to execute on - /// The initalized - public static DbCommand CreateTextCommand(this DbConnection db, string cmdText, DbTransaction transaction) - { - return CreateCommand(db, transaction, CommandType.Text, cmdText); - } - /// - /// Shortcut to create a command on a transaction with the specifed command type and command - /// - /// - /// The transaction to complete the operation on - /// The command type - /// The command to execute - /// The intialized db command - public static DbCommand CreateCommand(this DbConnection db, DbTransaction transaction, CommandType type, string command) - { - //Create the new command - DbCommand cmd = db.CreateCommand(); - cmd.Transaction = transaction; - cmd.CommandText = command; - cmd.CommandType = type; - return cmd; - } - /// - /// Creates a new for with the specified procedure name - /// - /// - /// The name of the stored proecedure to execute - /// The transaction to execute on - /// The initalized - public static DbCommand CreateProcedureCommand(this DbConnection db, string procedureName, DbTransaction transaction) - { - return CreateCommand(db, transaction, CommandType.StoredProcedure, procedureName); - } - - /// - /// Reads all available rows from the reader, adapts columns to public properties with - /// attributes, and adds them to the collection - /// - /// - /// - /// The container to write created objects to - /// The number of objects created and written to the collection - public static int GetAllObjects(this DbDataReader reader, ICollection container) where T : new() - { - //make sure its worth collecting object meta - if (!reader.HasRows) - { - return 0; - } - Type objectType = typeof(T); - //Rent a dict of properties that have the column attribute set so we can load the proper results - Dictionary avialbleProps = DictStore.Rent(); - //Itterate through public properties - foreach (PropertyInfo prop in objectType.GetProperties()) - { - //try to get the column name attribute of the propery - SqlColumnNameAttribute? colAtt = prop.GetCustomAttribute(true); - //Attribute is valid and coumn name is not empty - if (!string.IsNullOrWhiteSpace(colAtt?.ColumnName)) - { - //Store the property for later - avialbleProps[colAtt.ColumnName] = prop; - } - } - //Get the column schema - ReadOnlyCollection columns = reader.GetColumnSchema(); - int count = 0; - //Read - while (reader.Read()) - { - //Create the new object - T ret = new(); - //Iterate through columns - foreach (DbColumn col in columns) - { - //Get the propery if its specified by its column-name attribute - if (avialbleProps.TryGetValue(col.ColumnName, out PropertyInfo? prop)) - { - //make sure the column has a value - if (col.ColumnOrdinal.HasValue) - { - //Get the object - object val = reader.GetValue(col.ColumnOrdinal.Value); - //Set check if the row is DB null, if so set it, otherwise set the value - prop.SetValue(ret, Convert.IsDBNull(val) ? null : val); - } - } - } - //Add the object to the collection - container.Add(ret); - //Increment count - count++; - } - //return dict (if an error occurs, just let the dict go and create a new one next time, no stress setting up a try/finally block) - DictStore.Return(avialbleProps); - return count; - } - /// - /// Reads all available rows from the reader, adapts columns to public properties with - /// attributes, and adds them to the collection - /// - /// - /// - /// The container to write created objects to - /// The number of objects created and written to the collection - public static async ValueTask GetAllObjectsAsync(this DbDataReader reader, ICollection container) where T : new() - { - //make sure its worth collecting object meta - if (!reader.HasRows) - { - return 0; - } - Type objectType = typeof(T); - //Rent a dict of properties that have the column attribute set so we can load the proper results - Dictionary avialbleProps = DictStore.Rent(); - //Itterate through public properties - foreach (PropertyInfo prop in objectType.GetProperties()) - { - //try to get the column name attribute of the propery - SqlColumnNameAttribute? colAtt = prop.GetCustomAttribute(true); - //Attribute is valid and coumn name is not empty - if (!string.IsNullOrWhiteSpace(colAtt?.ColumnName)) - { - //Store the property for later - avialbleProps[colAtt.ColumnName] = prop; - } - } - //Get the column schema - ReadOnlyCollection columns = await reader.GetColumnSchemaAsync(); - int count = 0; - //Read - while (await reader.ReadAsync()) - { - //Create the new object - T ret = new(); - //Iterate through columns - foreach (DbColumn col in columns) - { - //Get the propery if its specified by its column-name attribute - if (avialbleProps.TryGetValue(col.ColumnName, out PropertyInfo? prop)) - { - //make sure the column has a value - if (col.ColumnOrdinal.HasValue) - { - //Get the object - object val = reader.GetValue(col.ColumnOrdinal.Value); - //Set check if the row is DB null, if so set it, otherwise set the value - prop.SetValue(ret, Convert.IsDBNull(val) ? null : val); - } - } - } - //Add the object to the collection - container.Add(ret); - //Increment count - count++; - } - //return dict (if an error occurs, just let the dict go and create a new one next time, no stress setting up a try/finally block) - DictStore.Return(avialbleProps); - return count; - } - /// - /// Reads the first available row from the reader, adapts columns to public properties with - /// - /// - /// - /// The created object, or default if no rows are available - public static T? GetFirstObject(this DbDataReader reader) where T : new() - { - //make sure its worth collecting object meta - if (!reader.HasRows) - { - return default; - } - //Get the object type - Type objectType = typeof(T); - //Get the column schema - ReadOnlyCollection columns = reader.GetColumnSchema(); - //Read - if (reader.Read()) - { - //Rent a dict of properties that have the column attribute set so we can load the proper results - Dictionary availbleProps = DictStore.Rent(); - //Itterate through public properties - foreach (PropertyInfo prop in objectType.GetProperties()) - { - //try to get the column name attribute of the propery - SqlColumnNameAttribute? colAtt = prop.GetCustomAttribute(true); - //Attribute is valid and coumn name is not empty - if (colAtt != null && !string.IsNullOrWhiteSpace(colAtt.ColumnName)) - { - //Store the property for later - availbleProps[colAtt.ColumnName] = prop; - } - } - //Create the new object - T ret = new(); - //Iterate through columns - foreach (DbColumn col in columns) - { - //Get the propery if its specified by its column-name attribute - if (availbleProps.TryGetValue(col.ColumnName, out PropertyInfo? prop) && col.ColumnOrdinal.HasValue) - { - //Get the object - object val = reader.GetValue(col.ColumnOrdinal.Value); - //Set check if the row is DB null, if so set it, otherwise set the value - prop.SetValue(ret, Convert.IsDBNull(val) ? null : val); - } - } - //Return dict, no stress if error occurs, the goal is lower overhead - DictStore.Return(availbleProps); - //Return the new object - return ret; - } - return default; - } - /// - /// Reads the first available row from the reader, adapts columns to public properties with - /// - /// - /// - /// The created object, or default if no rows are available - public static async Task GetFirstObjectAsync(this DbDataReader reader) where T : new() - { - //Read - if (await reader.ReadAsync()) - { - //Get the object type - Type objectType = typeof(T); - //Get the column schema - ReadOnlyCollection columns = await reader.GetColumnSchemaAsync(); - //Rent a dict of properties that have the column attribute set so we can load the proper results - Dictionary availbleProps = DictStore.Rent(); - //Itterate through public properties - foreach (PropertyInfo prop in objectType.GetProperties()) - { - //try to get the column name attribute of the propery - SqlColumnNameAttribute? colAtt = prop.GetCustomAttribute(true); - //Attribute is valid and coumn name is not empty - if (colAtt != null && !string.IsNullOrWhiteSpace(colAtt.ColumnName)) - { - //Store the property for later - availbleProps[colAtt.ColumnName] = prop; - } - } - //Create the new object - T ret = new(); - //Iterate through columns - foreach (DbColumn col in columns) - { - //Get the propery if its specified by its column-name attribute - if (availbleProps.TryGetValue(col.ColumnName, out PropertyInfo? prop) && col.ColumnOrdinal.HasValue) - { - //Get the object - object val = reader.GetValue(col.ColumnOrdinal.Value); - //Set check if the row is DB null, if so set it, otherwise set the value - prop.SetValue(ret, Convert.IsDBNull(val) ? null : val); - } - } - //Return dict, no stress if error occurs, the goal is lower overhead - DictStore.Return(availbleProps); - //Return the new object - return ret; - } - return default; - } - /// - /// Executes a nonquery operation with the specified command using the object properties set with the - /// attributes - /// - /// - /// - /// The object containing the properties to write to command variables - /// The number of rows affected - /// - /// - /// - /// - /// - /// - public static ERRNO ExecuteNonQuery(this DbCommand cmd, T obj) where T : notnull - { - if (obj == null) - { - throw new ArgumentNullException(nameof(obj)); - } - //Get the objec type - Type objtype = typeof(T); - //Itterate through public properties - foreach (PropertyInfo prop in objtype.GetProperties()) - { - //try to get the variable attribute of the propery - SqlVariableAttribute varprops = prop.GetCustomAttribute(true); - //This property is an sql variable, so lets add it - if (varprops == null) - { - continue; - } - //If the command type is text, then make sure the variable is actually in the command, if not, ignore it - if (cmd.CommandType != CommandType.Text || cmd.CommandText.Contains(varprops.VariableName)) - { - //Add the parameter to the command list - cmd.AddParameter(varprops.VariableName, prop.GetValue(obj), varprops.DataType, varprops.Size, varprops.IsNullable).Direction = varprops.Direction; - } - } - //Prepare the sql statement - cmd.Prepare(); - //Exect the query and return the results - return cmd.ExecuteNonQuery(); - } - /// - /// Executes a nonquery operation with the specified command using the object properties set with the - /// attributes - /// - /// - /// - /// The object containing the properties to write to command variables - /// The number of rows affected - /// - /// - /// - /// - /// - /// - public static async Task ExecuteNonQueryAsync(this DbCommand cmd, T obj) where T : notnull - { - if (obj == null) - { - throw new ArgumentNullException(nameof(obj)); - } - //Get the objec type - Type objtype = typeof(T); - //Itterate through public properties - foreach (PropertyInfo prop in objtype.GetProperties()) - { - //try to get the variable attribute of the propery - SqlVariableAttribute? varprops = prop.GetCustomAttribute(true); - //This property is an sql variable, so lets add it - if (varprops == null) - { - continue; - } - //If the command type is text, then make sure the variable is actually in the command, if not, ignore it - if (cmd.CommandType != CommandType.Text || cmd.CommandText.Contains(varprops.VariableName)) - { - //Add the parameter to the command list - cmd.AddParameter(varprops.VariableName, prop.GetValue(obj), varprops.DataType, varprops.Size, varprops.IsNullable).Direction = varprops.Direction; - } - } - //Prepare the sql statement - await cmd.PrepareAsync(); - //Exect the query and return the results - return await cmd.ExecuteNonQueryAsync(); - } - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/SQL/EnumerableTable.cs b/VNLib.Plugins.Extensions.Data/SQL/EnumerableTable.cs deleted file mode 100644 index 23cd889..0000000 --- a/VNLib.Plugins.Extensions.Data/SQL/EnumerableTable.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: EnumerableTable.cs -* -* EnumerableTable.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Data; -using System.Threading; -using System.Data.Common; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace VNLib.Plugins.Extensions.Data.SQL -{ - /// - /// A base class for client side async enumerable SQL queries - /// - /// The entity type - public abstract class EnumerableTable : TableManager, IAsyncEnumerable - { - const string DEFAULT_ENUM_STATMENT = "SELECT *\r\nFROM @table\r\n;"; - - public EnumerableTable(Func factory, string tableName) : base(factory, tableName) - { - //Build the default select all statment - Enumerate = DEFAULT_ENUM_STATMENT.Replace("@table", tableName); - } - public EnumerableTable(Func factory) : base(factory) - { } - - /// - /// The command that will be run against the database to return rows for enumeration - /// - protected string Enumerate { get; set; } - - /// - /// The isolation level to use when creating the transaction during enumerations - /// - protected IsolationLevel TransactionIsolationLevel { get; set; } = IsolationLevel.ReadUncommitted; - - IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) - { - return GetAsyncEnumerator(cancellationToken: cancellationToken); - } - - /// - /// Transforms a row from the into the item type - /// to be returned when yielded. - /// - /// The reader to get the item data from - /// A token to cancel the operation - /// A task that returns the transformed item - /// The position is set before this method is invoked - protected abstract Task GetItemAsync(DbDataReader reader, CancellationToken cancellationToken); - /// - /// Invoked when an item is no longer in the enumerator scope, in the enumeration process. - /// - /// The item to cleanup - /// A token to cancel the operation - /// A ValueTask that represents the cleanup process - protected abstract ValueTask CleanupItemAsync(T item, CancellationToken cancellationToken); - - /// - /// Gets an to enumerate items within the backing store. - /// - /// Cleanup items after each item is enumerated and the enumeration scope has - /// returned to the enumerator - /// A token to cancel the enumeration - /// A to enumerate records within the store - public virtual async IAsyncEnumerator GetAsyncEnumerator(bool closeItems = true, CancellationToken cancellationToken = default) - { - await using DbConnection db = GetConnection(); - await db.OpenAsync(cancellationToken); - await using DbTransaction transaction = await db.BeginTransactionAsync(cancellationToken); - //Start the enumeration command - await using DbCommand cmd = db.CreateTextCommand(Enumerate, transaction); - await cmd.PrepareAsync(cancellationToken); - await using DbDataReader reader = await cmd.ExecuteReaderAsync(cancellationToken); - //loop through results and transform each element - while (reader.Read()) - { - //get the item - T item = await GetItemAsync(reader, cancellationToken); - try - { - yield return item; - } - finally - { - if (closeItems) - { - //Cleanup the item - await CleanupItemAsync(item, cancellationToken); - } - } - } - } - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/SQL/SqlColumnNameAttribute.cs b/VNLib.Plugins.Extensions.Data/SQL/SqlColumnNameAttribute.cs deleted file mode 100644 index c18dab9..0000000 --- a/VNLib.Plugins.Extensions.Data/SQL/SqlColumnNameAttribute.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: SqlColumnNameAttribute.cs -* -* SqlColumnNameAttribute.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; - -namespace VNLib.Plugins.Extensions.Data.SQL -{ - /// - /// Property attribute that specifies the property represents an SQL column in the database - /// - [AttributeUsage(AttributeTargets.Property)] - public sealed class SqlColumnNameAttribute : Attribute - { - public bool Nullable { get; } - public bool Unique { get; } - public bool PrimaryKey { get; } - public string ColumnName { get; } - /// - /// Specifies the property is an SQL column name - /// - /// Name of the SQL column - /// - /// - /// - public SqlColumnNameAttribute(string columnName, bool primaryKey = false, bool nullable = true, bool unique = false) - { - this.ColumnName = columnName; - this.PrimaryKey = primaryKey; - this.Nullable = nullable; - this.Unique = unique; - } - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/SQL/SqlTableNameAttribute.cs b/VNLib.Plugins.Extensions.Data/SQL/SqlTableNameAttribute.cs deleted file mode 100644 index 9c870ea..0000000 --- a/VNLib.Plugins.Extensions.Data/SQL/SqlTableNameAttribute.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: SqlColumnName.cs -* -* SqlColumnName.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; - -namespace VNLib.Plugins.Extensions.Data.SQL -{ - - /// - /// Allows a type to declare itself as a with the specified name - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple =false, Inherited = true)] - public sealed class SqlTableNameAttribute : Attribute - { - public string TableName { get; } - - public SqlTableNameAttribute(string tableName) => TableName = tableName; - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/SQL/SqlVariable.cs b/VNLib.Plugins.Extensions.Data/SQL/SqlVariable.cs deleted file mode 100644 index b18d27b..0000000 --- a/VNLib.Plugins.Extensions.Data/SQL/SqlVariable.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: SqlVariable.cs -* -* SqlVariable.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Data; - -namespace VNLib.Plugins.Extensions.Data.SQL -{ - /// - /// Property attribute that specifies the property is to be used for a given command variable - /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] - public sealed class SqlVariableAttribute : Attribute - { - public string VariableName { get; } - public DbType DataType { get; } - public ParameterDirection Direction { get; } - public int Size { get; } - public bool IsNullable { get; } - /// - /// Specifies the property to be used as an SQL variable - /// - /// Sql statement variable this property will substitute - /// The sql data the property will represent - /// Data direction during execution - /// Column size - /// Is this property allowed to be null - public SqlVariableAttribute(string variableName, DbType dataType, ParameterDirection direction, int size, bool isNullable) - { - this.VariableName = variableName; - this.DataType = dataType; - this.Direction = direction; - this.Size = size; - this.IsNullable = isNullable; - } - } -} diff --git a/VNLib.Plugins.Extensions.Data/SQL/TableManager.cs b/VNLib.Plugins.Extensions.Data/SQL/TableManager.cs deleted file mode 100644 index a7f7873..0000000 --- a/VNLib.Plugins.Extensions.Data/SQL/TableManager.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: TableManager.cs -* -* TableManager.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Data.Common; - -namespace VNLib.Plugins.Extensions.Data.SQL -{ - /// - /// A class that contains basic structures for interacting with an SQL driven database - /// - public abstract class TableManager - { - private readonly Func Factory; - protected string Insert { get; set; } - protected string Select { get; set; } - protected string Update { get; set; } - protected string Delete { get; set; } - - /// - /// The name of the table specified during initialized - /// - protected string TableName { get; } - - protected TableManager(Func factory, string tableName) - { - this.Factory = factory ?? throw new ArgumentNullException(nameof(factory)); - this.TableName = !string.IsNullOrWhiteSpace(tableName) ? tableName : throw new ArgumentNullException(nameof(tableName)); - } - - protected TableManager(Func factory) - { - this.Factory = factory ?? throw new ArgumentNullException(nameof(factory)); - this.TableName = ""; - } - /// - /// Opens a new by invoking the factory callback method - /// - /// The open connection - protected DbConnection GetConnection() - { - return Factory(); - } - } -} diff --git a/VNLib.Plugins.Extensions.Data/Storage/Blob.cs b/VNLib.Plugins.Extensions.Data/Storage/Blob.cs deleted file mode 100644 index ab18eeb..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/Blob.cs +++ /dev/null @@ -1,244 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: Blob.cs -* -* Blob.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.IO; -using System.Threading.Tasks; -using System.Runtime.Versioning; - -using VNLib.Utils; -using VNLib.Utils.IO; -using VNLib.Utils.Async; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - /// - /// Represents a stream of arbitrary binary data - /// - public class Blob : BackingStream, IObjectStorage, IAsyncExclusiveResource - { - protected readonly LWStorageDescriptor Descriptor; - - /// - /// The current blob's unique ID - /// - public string BlobId => Descriptor.DescriptorID; - /// - /// A value indicating if the has been modified - /// - public bool Modified { get; protected set; } - /// - /// A valid indicating if the blob was flagged for deletiong - /// - public bool Deleted { get; protected set; } - - /// - /// The name of the file (does not change the actual file system name) - /// - public string Name - { - get => Descriptor.GetName(); - set => Descriptor.SetName(value); - } - /// - /// The UTC time the was last modified - /// - public DateTimeOffset LastWriteTimeUtc => Descriptor.LastModified; - /// - /// The UTC time the was created - /// - public DateTimeOffset CreationTimeUtc => Descriptor.Created; - - internal Blob(LWStorageDescriptor descriptor, in FileStream file) - { - this.Descriptor = descriptor; - base.BaseStream = file; - } - - /// - /// Prevents other processes from reading from or writing to the - /// - /// The begining position of the range to lock - /// The range to be locked - /// - /// - /// - [UnsupportedOSPlatform("ios")] - [UnsupportedOSPlatform("macos")] - [UnsupportedOSPlatform("tvos")] - public void Lock(long position, long length) => BaseStream.Lock(position, length); - /// - /// Prevents other processes from reading from or writing to the - /// - /// - /// - /// - [UnsupportedOSPlatform("ios")] - [UnsupportedOSPlatform("macos")] - [UnsupportedOSPlatform("tvos")] - public void Lock() => BaseStream.Lock(0, BaseStream.Length); - /// - /// Allows access by other processes to all or part of the that was previously locked - /// - /// The begining position of the range to unlock - /// The range to be unlocked - /// - [UnsupportedOSPlatform("ios")] - [UnsupportedOSPlatform("macos")] - [UnsupportedOSPlatform("tvos")] - public void Unlock(long position, long length) => BaseStream.Unlock(position, length); - /// - /// Allows access by other processes to the entire - /// - /// - [UnsupportedOSPlatform("ios")] - [UnsupportedOSPlatform("macos")] - [UnsupportedOSPlatform("tvos")] - public void Unlock() => BaseStream.Unlock(0, BaseStream.Length); - /// - public override void SetLength(long value) - { - base.SetLength(value); - //Set modified flag - Modified |= true; - } - - /* - * Capture on-write calls to set the modified flag - */ - /// - protected override void OnWrite(int count) => Modified |= true; - - T IObjectStorage.GetObject(string key) => ((IObjectStorage)Descriptor).GetObject(key); - void IObjectStorage.SetObject(string key, T obj) => ((IObjectStorage)Descriptor).SetObject(key, obj); - - public string this[string index] - { - get => Descriptor[index]; - set => Descriptor[index] = value; - } - - - /// - /// Marks the file for deletion and will be deleted when the is disposed - /// - public void Delete() - { - //Set deleted flag - Deleted |= true; - Descriptor.Delete(); - } - /// - public bool IsReleased => Descriptor.IsReleased; - - - /// - /// - /// If the was opened with writing enabled, - /// and file was modified, changes are flushed to the backing store - /// and the stream is set to readonly. - /// - /// - /// If calls to this method succeed the stream is placed into a read-only mode - /// which will cause any calls to Write to throw a - /// - /// - /// A that may be awaited until the operation completes - /// - /// This method may be called to avoid flushing changes to the backing store - /// when the is disposed (i.e. lifetime is manged outside of the desired scope) - /// - /// - /// - /// - public async ValueTask FlushChangesAndSetReadonlyAsync() - { - if (Deleted) - { - throw new InvalidOperationException("The blob has been deleted and must be closed!"); - } - if (Modified) - { - //flush the base stream - await BaseStream.FlushAsync(); - //Update the file length in the store - Descriptor.SetLength(BaseStream.Length); - } - //flush changes, this will cause the dispose method to complete synchronously when closing - await Descriptor.WritePendingChangesAsync(); - //Clear modified flag - Modified = false; - //Set to readonly mode - base.ForceReadOnly = true; - } - - - /* - * Override the dispose async to manually dispose the - * base stream and avoid the syncrhonous (OnClose) - * method and allow awaiting the descriptor release - */ - /// - public override async ValueTask DisposeAsync() - { - await ReleaseAsync(); - GC.SuppressFinalize(this); - } - /// - public async ValueTask ReleaseAsync() - { - try - { - //Check for deleted - if (Deleted) - { - //Dispose the base stream explicitly - await BaseStream.DisposeAsync(); - //Try to delete the file - File.Delete(BaseStream.Name); - } - //Check to see if the file was modified - else if (Modified) - { - //Set the file size in bytes - Descriptor.SetLength(BaseStream.Length); - } - } - catch - { - //Set the error flag - Descriptor.IsError(true); - //propagate the exception - throw; - } - finally - { - //Dispose the stream - await BaseStream.DisposeAsync(); - //Release the descriptor - await Descriptor.ReleaseAsync(); - } - } - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/BlobExtensions.cs b/VNLib.Plugins.Extensions.Data/Storage/BlobExtensions.cs deleted file mode 100644 index 468a66d..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/BlobExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: BlobExtensions.cs -* -* BlobExtensions.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; - -using VNLib.Utils; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - public static class BlobExtensions - { - public const string USER_ID_ENTRY = "__.uid"; - public const string VERSION_ENTRY = "__.vers"; - - private const string FILE_SIZE = "__.size"; - private const string FILE_NAME = "__.name"; - private const string ERROR_FLAG = "__.err"; - - public static string GetUserId(this Blob blob) => blob[USER_ID_ENTRY]; - /// - /// Gets the stored in the current - /// - /// The sored version if previously set, thows otherwise - /// - public static Version GetVersion(this Blob blob) => Version.Parse(blob[VERSION_ENTRY]); - /// - /// Sets a for the current - /// - /// - /// The of the - public static void SetVersion(this Blob blob, Version version) => blob[VERSION_ENTRY] = version.ToString(); - - /// - /// Gets a value indicating if the last operation left the in an undefined state - /// - /// True if the state is undefined, false otherwise - public static bool IsError(this Blob blob) => bool.TrueString.Equals(blob[ERROR_FLAG]); - internal static void IsError(this LWStorageDescriptor blob, bool value) => blob[ERROR_FLAG] = value ? bool.TrueString : null; - - internal static long GetLength(this LWStorageDescriptor blob) => (blob as IObjectStorage).GetObject(FILE_SIZE); - internal static void SetLength(this LWStorageDescriptor blob, long length) => (blob as IObjectStorage).SetObject(FILE_SIZE, length); - - internal static string GetName(this LWStorageDescriptor blob) => blob[FILE_NAME]; - internal static string SetName(this LWStorageDescriptor blob, string filename) => blob[FILE_NAME] = filename; - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/BlobStore.cs b/VNLib.Plugins.Extensions.Data/Storage/BlobStore.cs deleted file mode 100644 index 6897516..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/BlobStore.cs +++ /dev/null @@ -1,162 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: BlobStore.cs -* -* BlobStore.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.IO; -using System.Security.Cryptography; -using System.Threading.Tasks; - -using VNLib.Utils.Extensions; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - - /// - /// Stores s to the local file system backed with a single table - /// that tracks changes - /// - public class BlobStore - { - /// - /// The root directory all blob files are stored - /// - public DirectoryInfo RootDir { get; } - /// - /// The backing store for blob meta-data - /// - protected LWStorageManager BlobTable { get; } - /// - /// Creates a new that accesses files - /// within the specified root directory. - /// - /// The root directory containing the blob file contents - /// The db backing store - public BlobStore(DirectoryInfo rootDir, LWStorageManager blobStoreMan) - { - RootDir = rootDir; - BlobTable = blobStoreMan; - } - - private string GetPath(string fileId) => Path.Combine(RootDir.FullName, fileId); - - /* - * Creates a repeatable unique identifier for the file - * name and allows for lookups - */ - internal static string CreateFileHash(string fileName) - { - throw new NotImplementedException(); - //return ManagedHash.ComputeBase64Hash(fileName, HashAlg.SHA1); - } - - /// - /// Opens an existing from the current store - /// - /// The id of the file being requested - /// Access level of the file - /// The sharing option of the underlying file - /// The size of the file buffer - /// If found, the requested , null otherwise. Throws exceptions if the file is opened in a non-sharable state - /// - /// - /// - /// - /// - public virtual async Task OpenBlobAsync(string fileId, FileAccess access, FileShare share, int bufferSize = 4096) - { - //Get the file's data descriptor - LWStorageDescriptor fileDescriptor = await BlobTable.GetDescriptorFromIDAsync(fileId); - //return null if not found - if (fileDescriptor == null) - { - return null; - } - try - { - string fsSafeName = GetPath(fileDescriptor.DescriptorID); - //try to open the file - FileStream file = new(fsSafeName, FileMode.Open, access, share, bufferSize, FileOptions.Asynchronous); - //Create the new blob - return new Blob(fileDescriptor, file); - } - catch (FileNotFoundException) - { - //If the file was not found but the descriptor was, delete the descriptor from the db - fileDescriptor.Delete(); - //Flush changes - await fileDescriptor.ReleaseAsync(); - //return null since this is a desync issue and the file technically does not exist - return null; - } - catch - { - //Release the descriptor and pass the exception - await fileDescriptor.ReleaseAsync(); - throw; - } - } - - /// - /// Creates a new for the specified file sharing permissions - /// - /// The name of the original file - /// The blob sharing permissions - /// - /// The newly created - /// - /// - /// - /// - public virtual async Task CreateBlobAsync(string name, FileShare share = FileShare.None, int bufferSize = 4096) - { - //hash the file name to create a unique id for the file name - LWStorageDescriptor newFile = await BlobTable.CreateDescriptorAsync(CreateFileHash(name)); - //if the descriptor was not created, return null - if (newFile == null) - { - return null; - } - try - { - string fsSafeName = GetPath(newFile.DescriptorID); - //Open/create the new file - FileStream file = new(fsSafeName, FileMode.OpenOrCreate, FileAccess.ReadWrite, share, bufferSize, FileOptions.Asynchronous); - //If the file already exists, make sure its zero'd - file.SetLength(0); - //Save the original name of the file - newFile.SetName(name); - //Create and return the new blob - return new Blob(newFile, file); - } - catch - { - //If an exception occurs, remove the descritor from the db - newFile.Delete(); - await newFile.ReleaseAsync(); - //Pass exception - throw; - } - } - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/LWDecriptorCreationException.cs b/VNLib.Plugins.Extensions.Data/Storage/LWDecriptorCreationException.cs deleted file mode 100644 index db0dbbb..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/LWDecriptorCreationException.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: LWDecriptorCreationException.cs -* -* LWDecriptorCreationException.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - /// - /// Raised when an operation to create a new - /// fails - /// - public class LWDescriptorCreationException : Exception - { - /// - public LWDescriptorCreationException() - {} - /// - public LWDescriptorCreationException(string? message) : base(message) - {} - /// - public LWDescriptorCreationException(string? message, Exception? innerException) : base(message, innerException) - {} - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/LWStorageContext.cs b/VNLib.Plugins.Extensions.Data/Storage/LWStorageContext.cs deleted file mode 100644 index d7f6e29..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/LWStorageContext.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: LWStorageContext.cs -* -* LWStorageContext.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using Microsoft.EntityFrameworkCore; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ -#nullable disable - internal sealed class LWStorageContext : TransactionalDbContext - { - private readonly string TableName; - public DbSet Descriptors { get; set; } - - public LWStorageContext(DbContextOptions options, string tableName) - :base(options) - { - TableName = tableName; - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - //Set table name - modelBuilder.Entity() - .ToTable(TableName); - } - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/LWStorageDescriptor.cs b/VNLib.Plugins.Extensions.Data/Storage/LWStorageDescriptor.cs deleted file mode 100644 index 72665f3..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/LWStorageDescriptor.cs +++ /dev/null @@ -1,230 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: LWStorageDescriptor.cs -* -* LWStorageDescriptor.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.IO; -using System.Text.Json; -using System.Collections; -using System.IO.Compression; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -using VNLib.Utils; -using VNLib.Utils.Async; -using VNLib.Utils.Extensions; -using VNLib.Utils.Memory; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - /// - /// Represents an open storage object, that when released or disposed, will flush its changes to the underlying table - /// for which this descriptor represents - /// - public sealed class LWStorageDescriptor : AsyncUpdatableResource, IObjectStorage, IEnumerable>, IIndexable - { - - private static readonly JsonSerializerOptions SerializerOptions = new() - { - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - NumberHandling = JsonNumberHandling.Strict, - ReadCommentHandling = JsonCommentHandling.Disallow, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IgnoreReadOnlyFields = true, - DefaultBufferSize = Environment.SystemPageSize, - }; - - - internal LWStorageEntry Entry { get; } - - private readonly Lazy> StringStorage; - - /// - /// The currnt descriptor's identifier string within its backing table. Usually the primary key. - /// - public string DescriptorID => Entry.Id; - /// - /// The identifier of the user for which this descriptor belongs to - /// - public string UserID => Entry.UserId!; - /// - /// The when the descriptor was created - /// - public DateTimeOffset Created => Entry.Created; - /// - /// The last time this descriptor was updated - /// - public DateTimeOffset LastModified => Entry.LastModified; - - /// - protected override AsyncUpdateCallback UpdateCb { get; } - /// - protected override AsyncDeleteCallback DeleteCb { get; } - /// - protected override JsonSerializerOptions JSO => SerializerOptions; - - internal LWStorageDescriptor(LWStorageManager manager, LWStorageEntry entry) - { - Entry = entry; - UpdateCb = manager.UpdateDescriptorAsync; - DeleteCb = manager.RemoveDescriptorAsync; - StringStorage = new(OnStringStoreLoad); - } - - internal Dictionary OnStringStoreLoad() - { - if(Entry.Data == null || Entry.Data.Length == 0) - { - return new(StringComparer.OrdinalIgnoreCase); - } - else - { - //Calc and alloc decode buffer - int bufferSize = (int)(Entry.Data.Length * 1.75); - using UnsafeMemoryHandle decodeBuffer = Memory.UnsafeAlloc(bufferSize); - - //Decode and deserialize the data - return BrotliDecoder.TryDecompress(Entry.Data, decodeBuffer, out int written) - ? JsonSerializer.Deserialize>(Entry.Data, SerializerOptions) ?? new(StringComparer.OrdinalIgnoreCase) - : throw new InvalidDataException("Failed to decompress data"); - } - } - - /// - /// - /// - /// - /// - public T? GetObject(string key) - { - Check(); - //De-serialize and return object - return StringStorage.Value.TryGetValue(key, out string? val) ? val.AsJsonObject(SerializerOptions) : default; - } - - /// - /// - /// - public void SetObject(string key, T obj) - { - //Remove the object from storage if its null - if (obj == null) - { - SetStringValue(key, null); - } - else - { - //Serialize the object to a string - string value = obj.ToJsonString(SerializerOptions)!; - //Attempt to store string in storage - SetStringValue(key, value); - } - } - - - /// - /// Gets a string value from string storage matching a given key - /// - /// Key for storage - /// Value associaetd with key if exists, otherwise - /// If key is null - /// - public string GetStringValue(string key) - { - Check(); - return StringStorage.Value.TryGetValue(key, out string? val) ? val : string.Empty; - } - - /// - /// Creates, overwrites, or removes a string value identified by key. - /// - /// Entry key - /// String to store or overwrite, set to null or string.Empty to remove a property - /// - /// If key is null - public void SetStringValue(string key, string? value) - { - if (string.IsNullOrEmpty(key)) - { - throw new ArgumentNullException(nameof(key)); - } - Check(); - //If the value is null, see if the the properties are null - if (string.IsNullOrWhiteSpace(value)) - { - //If the value is null and properies exist, remove the entry - StringStorage.Value.Remove(key); - Modified |= true; - } - else - { - //Set the value - StringStorage.Value[key] = value; - //Set modified flag - Modified |= true; - } - } - - /// - /// Gets or sets a string value from string storage matching a given key - /// - /// Key for storage - /// Value associaetd with key if exists, otherwise - /// - /// If key is null - public string this[string key] - { - get => GetStringValue(key); - set => SetStringValue(key, value); - } - - /// - /// Flushes all pending changes to the backing store asynchronously - /// - /// - public ValueTask WritePendingChangesAsync() - { - Check(); - return Modified ? (new(FlushPendingChangesAsync())) : ValueTask.CompletedTask; - } - - /// - public override async ValueTask ReleaseAsync() - { - await base.ReleaseAsync(); - //Cleanup dict on exit - if (StringStorage.IsValueCreated) - { - StringStorage.Value.Clear(); - } - } - - /// - public IEnumerator> GetEnumerator() => StringStorage.Value.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - /// - protected override object GetResource() => StringStorage.Value; - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/LWStorageEntry.cs b/VNLib.Plugins.Extensions.Data/Storage/LWStorageEntry.cs deleted file mode 100644 index 5c5da61..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/LWStorageEntry.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: LwStorageEntry.cs -* -* LwStorageEntry.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; - -using VNLib.Plugins.Extensions.Data.Abstractions; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - - internal sealed class LWStorageEntry : DbModelBase, IUserEntity - { - public override string Id { get; set; } - - public override DateTime Created { get; set; } - - public override DateTime LastModified { get; set; } - - public string? UserId { get; set; } - - public byte[]? Data { get; set; } - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/LWStorageManager.cs b/VNLib.Plugins.Extensions.Data/Storage/LWStorageManager.cs deleted file mode 100644 index 027fa90..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/LWStorageManager.cs +++ /dev/null @@ -1,347 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: LWStorageManager.cs -* -* LWStorageManager.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.IO; -using System.Data; -using System.Linq; -using System.Threading; -using System.IO.Compression; -using System.Threading.Tasks; - -using Microsoft.EntityFrameworkCore; - -using VNLib.Utils; -using VNLib.Utils.IO; -using VNLib.Utils.Memory; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - - /// - /// Provides single table database object storage services - /// - public sealed class LWStorageManager - { - /// - /// The generator function that is invoked when a new is to - /// be created without an explicit id - /// - public Func NewDescriptorIdGenerator { get; init; } = static () => Guid.NewGuid().ToString("N"); - - private readonly DbContextOptions DbOptions; - private readonly string TableName; - - private LWStorageContext GetContext() => new(DbOptions, TableName); - - /// - /// Creates a new with - /// - /// The db context options to create database connections with - /// The name of the table to operate on - /// - public LWStorageManager(DbContextOptions options, string tableName) - { - DbOptions = options ?? throw new ArgumentNullException(nameof(options)); - TableName = tableName ?? throw new ArgumentNullException(nameof(tableName)); - } - - /// - /// Creates a new fror a given user - /// - /// Id of user - /// An override to specify the new descriptor's id - /// A token to cancel the operation - /// A new if successfully created, null otherwise - /// - /// - public async Task CreateDescriptorAsync(string userId, string? descriptorIdOverride = null, CancellationToken cancellation = default) - { - if (string.IsNullOrWhiteSpace(userId)) - { - throw new ArgumentNullException(nameof(userId)); - } - - //If no override id was specified, generate a new one - descriptorIdOverride ??= NewDescriptorIdGenerator(); - - DateTime createdOrModifedTime = DateTime.UtcNow; - - await using LWStorageContext ctx = GetContext(); - await ctx.OpenTransactionAsync(cancellation); - - //Make sure the descriptor doesnt exist only by its descriptor id - if (await ctx.Descriptors.AnyAsync(d => d.Id == descriptorIdOverride, cancellation)) - { - throw new LWDescriptorCreationException($"A descriptor with id {descriptorIdOverride} already exists"); - } - - //Cache time - DateTime now = DateTime.UtcNow; - - //Create the new descriptor - LWStorageEntry entry = new() - { - Created = now, - LastModified = now, - Id = descriptorIdOverride, - UserId = userId, - }; - - //Add and save changes - ctx.Descriptors.Add(entry); - - ERRNO result = await ctx.SaveChangesAsync(cancellation); - - if (!result) - { - //Rollback and raise exception - await ctx.RollbackTransctionAsync(cancellation); - throw new LWDescriptorCreationException("Failed to create descriptor, because changes could not be saved"); - } - else - { - //Commit transaction and return the new descriptor - await ctx.CommitTransactionAsync(cancellation); - return new LWStorageDescriptor(this, entry); - } - } - - /// - /// Attempts to retrieve for a given user-id. The caller is responsible for - /// consitancy state of the descriptor - /// - /// User's id - /// A token to cancel the operation - /// The descriptor belonging to the user, or null if not found or error occurs - /// - public async Task GetDescriptorFromUIDAsync(string userid, CancellationToken cancellation = default) - { - //Allow null/empty entrys to just return null - if (string.IsNullOrWhiteSpace(userid)) - { - throw new ArgumentNullException(nameof(userid)); - } - - //Init db - await using LWStorageContext db = GetContext(); - //Begin transaction - await db.OpenTransactionAsync(cancellation); - //Get entry - LWStorageEntry? entry = await (from s in db.Descriptors - where s.UserId == userid - select s) - .SingleOrDefaultAsync(cancellation); - - //Close transactions and return - if (entry == null) - { - await db.RollbackTransctionAsync(cancellation); - return null; - } - else - { - await db.CommitTransactionAsync(cancellation); - return new (this, entry); - } - } - - /// - /// Attempts to retrieve the for the given descriptor id. The caller is responsible for - /// consitancy state of the descriptor - /// - /// Unique identifier for the descriptor - /// A token to cancel the opreeaiton - /// The descriptor belonging to the user, or null if not found or error occurs - /// - public async Task GetDescriptorFromIDAsync(string descriptorId, CancellationToken cancellation = default) - { - //Allow null/empty entrys to just return null - if (string.IsNullOrWhiteSpace(descriptorId)) - { - throw new ArgumentNullException(nameof(descriptorId)); - } - - //Init db - await using LWStorageContext db = GetContext(); - //Begin transaction - await db.OpenTransactionAsync(cancellation); - //Get entry - LWStorageEntry? entry = await (from s in db.Descriptors - where s.Id == descriptorId - select s) - .SingleOrDefaultAsync(cancellation); - - //Close transactions and return - if (entry == null) - { - await db.RollbackTransctionAsync(cancellation); - return null; - } - else - { - await db.CommitTransactionAsync(cancellation); - return new (this, entry); - } - } - - /// - /// Cleanup entries before the specified . Entires are store in UTC time - /// - /// Time before to compare against - /// A token to cancel the operation - /// The number of entires cleanedS - public Task CleanupTableAsync(TimeSpan compareTime, CancellationToken cancellation = default) => CleanupTableAsync(DateTime.UtcNow.Subtract(compareTime), cancellation); - - /// - /// Cleanup entries before the specified . Entires are store in UTC time - /// - /// UTC time to compare entires against - /// A token to cancel the operation - /// The number of entires cleaned - public async Task CleanupTableAsync(DateTime compareTime, CancellationToken cancellation = default) - { - //Init db - await using LWStorageContext db = GetContext(); - //Begin transaction - await db.OpenTransactionAsync(cancellation); - - //Get all expired entires - LWStorageEntry[] expired = await (from s in db.Descriptors - where s.Created < compareTime - select s) - .ToArrayAsync(cancellation); - - //Delete - db.Descriptors.RemoveRange(expired); - - //Save changes - ERRNO count = await db.SaveChangesAsync(cancellation); - - //Commit transaction - await db.CommitTransactionAsync(cancellation); - - return count; - } - - /// - /// Updates a descriptor's data field - /// - /// Descriptor to update - /// Data string to store to descriptor record - /// - internal async Task UpdateDescriptorAsync(object descriptorObj, Stream data) - { - LWStorageEntry entry = (descriptorObj as LWStorageDescriptor)!.Entry; - ERRNO result = 0; - try - { - await using LWStorageContext ctx = GetContext(); - await ctx.OpenTransactionAsync(CancellationToken.None); - - //Begin tracking - ctx.Descriptors.Attach(entry); - - //Convert stream to vnstream - VnMemoryStream vms = (VnMemoryStream)data; - using (IMemoryHandle encBuffer = Memory.SafeAlloc((int)vms.Length)) - { - //try to compress - if(!BrotliEncoder.TryCompress(vms.AsSpan(), encBuffer.Span, out int compressed)) - { - throw new InvalidDataException("Failed to compress the descriptor data"); - } - //Set the data - entry.Data = encBuffer.Span.ToArray(); - } - //Update modified time - entry.LastModified = DateTime.UtcNow; - - //Save changes - result = await ctx.SaveChangesAsync(CancellationToken.None); - - //Commit or rollback - if (result) - { - await ctx.CommitTransactionAsync(CancellationToken.None); - } - else - { - await ctx.RollbackTransctionAsync(CancellationToken.None); - } - } - catch (Exception ex) - { - throw new LWStorageUpdateFailedException("", ex); - } - //If the result is 0 then the update failed - if (!result) - { - throw new LWStorageUpdateFailedException($"Descriptor {entry.Id} failed to update"); - } - } - - /// - /// Function to remove the specified descriptor - /// - /// The active descriptor to remove from the database - /// - internal async Task RemoveDescriptorAsync(object descriptorObj) - { - LWStorageEntry descriptor = (descriptorObj as LWStorageDescriptor)!.Entry; - ERRNO result; - try - { - //Init db - await using LWStorageContext db = GetContext(); - //Begin transaction - await db.OpenTransactionAsync(); - - //Delete the user from the database - db.Descriptors.Remove(descriptor); - - //Save changes and commit if successful - result = await db.SaveChangesAsync(); - - if (result) - { - await db.CommitTransactionAsync(); - } - else - { - await db.RollbackTransctionAsync(); - } - } - catch (Exception ex) - { - throw new LWStorageRemoveFailedException("", ex); - } - if (!result) - { - throw new LWStorageRemoveFailedException("Failed to delete the user account because of a database failure, the user may already be deleted"); - } - } - - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/LWStorageRemoveFailedException.cs b/VNLib.Plugins.Extensions.Data/Storage/LWStorageRemoveFailedException.cs deleted file mode 100644 index 806912c..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/LWStorageRemoveFailedException.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: LWStorageRemoveFailedException.cs -* -* LWStorageRemoveFailedException.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using VNLib.Utils.Resources; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - /// - /// The exception raised when an open removal operation fails. The - /// property may contain any nested exceptions that caused the removal to fail. - /// - public class LWStorageRemoveFailedException : ResourceDeleteFailedException - { - internal LWStorageRemoveFailedException(string error, Exception inner) : base(error, inner) { } - - public LWStorageRemoveFailedException() - {} - - public LWStorageRemoveFailedException(string message) : base(message) - {} - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/LWStorageUpdateFailedException.cs b/VNLib.Plugins.Extensions.Data/Storage/LWStorageUpdateFailedException.cs deleted file mode 100644 index fe555bf..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/LWStorageUpdateFailedException.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: LWStorageUpdateFailedException.cs -* -* LWStorageUpdateFailedException.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using VNLib.Utils.Resources; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - /// - /// The exception raised when an open update operation fails. The - /// property may contain any nested exceptions that caused the update to fail. - /// - public class LWStorageUpdateFailedException : ResourceUpdateFailedException - { - internal LWStorageUpdateFailedException(string error, Exception inner) : base(error, inner) { } - - public LWStorageUpdateFailedException() - {} - public LWStorageUpdateFailedException(string message) : base(message) - {} - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/Storage/UndefinedBlobStateException.cs b/VNLib.Plugins.Extensions.Data/Storage/UndefinedBlobStateException.cs deleted file mode 100644 index e845372..0000000 --- a/VNLib.Plugins.Extensions.Data/Storage/UndefinedBlobStateException.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: UndefinedBlobStateException.cs -* -* UndefinedBlobStateException.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Runtime.Serialization; - -namespace VNLib.Plugins.Extensions.Data.Storage -{ - /// - /// Raised to signal that the requested was left in an undefined state - /// when previously accessed - /// - public class UndefinedBlobStateException : Exception - { - public UndefinedBlobStateException() - {} - public UndefinedBlobStateException(string message) : base(message) - {} - public UndefinedBlobStateException(string message, Exception innerException) : base(message, innerException) - {} - protected UndefinedBlobStateException(SerializationInfo info, StreamingContext context) : base(info, context) - {} - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/TransactionalDbContext.cs b/VNLib.Plugins.Extensions.Data/TransactionalDbContext.cs deleted file mode 100644 index 6b835c5..0000000 --- a/VNLib.Plugins.Extensions.Data/TransactionalDbContext.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Data -* File: TransactionalDbContext.cs -* -* TransactionalDbContext.cs is part of VNLib.Plugins.Extensions.Data which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Data is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Data is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Plugins.Extensions.Data. If not, see http://www.gnu.org/licenses/. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; - -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; - -namespace VNLib.Plugins.Extensions.Data -{ - public abstract class TransactionalDbContext : DbContext, IAsyncDisposable - { - /// - /// - /// - protected TransactionalDbContext() - {} - /// - /// - /// - protected TransactionalDbContext(DbContextOptions options) : base(options) - {} - - /// - /// The transaction that was opened on the current context - /// - public IDbContextTransaction? Transaction { get; set; } - - -#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize, ignore because base.Dispose() is called - /// - public sealed override void Dispose() - { - //dispose the transaction - Transaction?.Dispose(); - base.Dispose(); - } - - /// - public override async ValueTask DisposeAsync() - { - //If transaction has been created, dispose the transaction - if (Transaction != null) - { - await Transaction.DisposeAsync(); - } - await base.DisposeAsync(); - } -#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize - - /// - /// Opens a single transaction on the current context. If a transaction is already open, - /// it is disposed and a new transaction is begun. - /// - public async Task OpenTransactionAsync(CancellationToken cancellationToken = default) - { - //open a new transaction on the current database - this.Transaction = await base.Database.BeginTransactionAsync(cancellationToken); - } - /// - /// Invokes the on the current context - /// - public Task CommitTransactionAsync(CancellationToken token = default) - { - return Transaction != null ? Transaction.CommitAsync(token) : Task.CompletedTask; - } - /// - /// Invokes the on the current context - /// - public Task RollbackTransctionAsync(CancellationToken token = default) - { - return Transaction != null ? Transaction.RollbackAsync(token) : Task.CompletedTask; - } - - } -} \ No newline at end of file diff --git a/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.csproj b/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.csproj deleted file mode 100644 index b229e64..0000000 --- a/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.csproj +++ /dev/null @@ -1,54 +0,0 @@ - - - - net6.0 - VNLib.Plugins.Extensions.Data - - - - - - true - VNLib.Plugins.Extensions.Data - Vaughn Nugent - Data extensions for VNLib Plugins - Copyright © 2022 Vaughn Nugent - https://www.vaughnnugent.com/resources - 1.0.1.1 - enable - True - \\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk - - - - true - True - latest-all - - - - False - - - - False - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - diff --git a/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.xml b/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.xml deleted file mode 100644 index 2f7736e..0000000 --- a/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.xml +++ /dev/null @@ -1,476 +0,0 @@ - - - - - VNLib.Plugins.Extensions.Data - - - - - An abstraction that defines a Data-Store that supports - bulk data operations - - The data-model type - - - - Deletes a collection of records from the store - - A collection of records to delete - A task the resolves the number of entires removed from the store - - - - Updates a collection of records - - The collection of records to update - A task the resolves an error code (should evaluate to false on failure, and true on success) - - - - Creates a bulk collection of records as entries in the store - - The collection of records to add - A task the resolves an error code (should evaluate to false on failure, and true on success) - - - - Creates or updates individual records from a bulk collection of records - - The collection of records to add - A task the resolves an error code (should evaluate to false on failure, and true on success) - - - - An abstraction that defines a Data-Store and common - operations that retrieve or manipulate records of data - - The data-model type - - - - Gets the total number of records in the current store - - A task that resolves the number of records in the store - - - - Gets the number of records that belong to the specified constraint - - A specifier to constrain the reults - The number of records that belong to the specifier - - - - Gets a record from its key - - The key identifying the unique record - A promise that resolves the record identified by the specified key - - - - Gets a record from its key - - A variable length specifier arguemnt array for retreiving a single application - - - - - Gets a record from the store with a partial model, intended to complete the model - - The partial model used to query the store - A task the resolves the completed data-model - - - - Fills a collection with enires retireved from the store using the specifer - - The collection to add entires to - A specifier argument to constrain results - The maximum number of elements to retrieve - A Task the resolves to the number of items added to the collection - - - - Fills a collection with enires retireved from the store using a variable length specifier - parameter - - The collection to add entires to - The maximum number of elements to retrieve - - A Task the resolves to the number of items added to the collection - - - - Updates an entry in the store with the specified record - - The record to update - A task the resolves an error code (should evaluate to false on failure, and true on success) - - - - Creates a new entry in the store representing the specified record - - The record to add to the store - A task the resolves an error code (should evaluate to false on failure, and true on success) - - - - Deletes one or more entrires from the store matching the specified record - - The record to remove from the store - A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success) - - - - Deletes one or more entires from the store matching the specified unique key - - The unique key that identifies the record - A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success) - - - - Deletes one or more entires from the store matching the supplied specifiers - - A variable length array of specifiers used to delete one or more entires - A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success) - - - - Updates an entry in the store if it exists, or creates a new entry if one does not already exist - - The record to add to the store - A task the resolves the result of the operation - - - - Defines a Data-Store that can retirieve and manipulate paginated - data - - The data-model type - - - - Gets a collection of records using a pagination style query, and adds the records to the collecion - - The collection to add records to - Pagination page to get records from - The maximum number of items to retrieve from the store - A task that resolves the number of items added to the collection - - - - Gets a collection of records using a pagination style query with constraint arguments, and adds the records to the collecion - - The collection to add records to - Pagination page to get records from - The maximum number of items to retrieve from the store - A params array of strings to constrain the result set from the db - A task that resolves the number of items added to the collection - - - - Defines an entity base that has an owner, identified by its user-id - - - - - The user-id of the owner of the entity - - - - - Provides a base for DBSet Records with a timestamp/version - a unique ID key, and create/modified timestamps - - - - - - - - - - - - - - - - - Implements basic data-store functionality with abstract query builders - - A implemented type - - - - Gets a unique ID for a new record being added to the store - - - - - Gets a new ready for use - - - - - - An object rental for entity collections - - - - - - - - - - - - - - Builds a query that attempts to get a single entry from the - store based on the specified record if it does not have a - valid property - - The active context to query - The record to search for - A query that yields a single record if it exists in the store - - - - Builds a query that attempts to get a single entry from the - store to update based on the specified record - - The active context to query - The record to search for - A query that yields a single record to update if it exists in the store - - - - Updates the current record (if found) to the new record before - storing the updates. - - The new record to capture data from - The current record to be updated - - - - - - - - - - - - - Builds a query that results in a single entry to delete from the - constraint arguments - - The active context - A variable length parameter array of query constraints - A query that yields a single record (or no record) to delete - - - - - - - - - - Builds a query to get a count of records constrained by the specifier - - The active context to run the query on - The specifier constrain - A query that can be counted - - - - Builds a query to get a collection of records based on an variable length array of parameters - - The active context to run the query on - An arguments array to constrain the results of the query - A query that returns a collection of records from the store - - - - - - - - - - Builds a query to get a count of records constrained by the specifier - - The active context to run the query on - The specifier constrain - A query that can be counted - - - - - - - - - - - - - Builds a query to get a single record from the variable length parameter arguments - - The context to execute query against - Arguments to constrain the results of the query to a single record - A query that yields a single record - - - - - Builds a query to get a single record from the specified record. - - - Unless overridden, performs an ID based query for a single entry - - - The context to execute query against - A record to referrence the lookup - A query that yields a single record - - - - - - - - - - Builds a query to get a collection of records based on an variable length array of parameters - - The active context to run the query on - An arguments array to constrain the results of the query - A query that returns a paginated collection of records from the store - - - - Represents a basic data model for an EFCore entity - for support in data-stores - - - - - A unique id for the entity - - - - - The the entity was created in the store - - - - - The the entity was last modified in the store - - - - - Entity concurrency token - - - - - A data store that provides unique identities and protections based on an entity that has an owner - - - - - - - - Gets a single item contrained by a given user-id and item id - - - - - - - - - - - Updates the specified record within the store - - - The record to update - The userid of the record owner - A task that evaluates to the number of records modified - - - - Updates the specified record within the store - - - The record to update - The userid of the record owner - A task that evaluates to the number of records modified - - - - Gets a single entity from its ID and user-id - - - The unique id of the entity - The user's id that owns the resource - A task that resolves the entity or null if not found - - - - Deletes a single entiry by its ID only if it belongs to the speicifed user - - - The unique id of the entity - The user's id that owns the resource - A task the resolves the number of eneities deleted (should evaluate to true or false) - - - - - - - - - - - - - - The transaction that was opened on the current context - - - - - - - - Opens a single transaction on the current context. If a transaction is already open, - it is disposed and a new transaction is begun. - - - - - Invokes the on the current context - - - - - Invokes the on the current context - - - - - - - -- cgit