aboutsummaryrefslogtreecommitdiff
path: root/VNLib.Plugins.Extensions.Data
diff options
context:
space:
mode:
Diffstat (limited to 'VNLib.Plugins.Extensions.Data')
-rw-r--r--VNLib.Plugins.Extensions.Data/Abstractions/IBulkDataStore.cs41
-rw-r--r--VNLib.Plugins.Extensions.Data/Abstractions/IDataStore.cs99
-rw-r--r--VNLib.Plugins.Extensions.Data/Abstractions/IPaginatedDataStore.cs32
-rw-r--r--VNLib.Plugins.Extensions.Data/Abstractions/IUserEntity.cs15
-rw-r--r--VNLib.Plugins.Extensions.Data/DbModelBase.cs24
-rw-r--r--VNLib.Plugins.Extensions.Data/DbStore.cs501
-rw-r--r--VNLib.Plugins.Extensions.Data/Extensions.cs58
-rw-r--r--VNLib.Plugins.Extensions.Data/IDbModel.cs28
-rw-r--r--VNLib.Plugins.Extensions.Data/ProtectedDbStore.cs46
-rw-r--r--VNLib.Plugins.Extensions.Data/ProtectedEntityExtensions.cs62
-rw-r--r--VNLib.Plugins.Extensions.Data/TransactionalDbContext.cs69
-rw-r--r--VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.csproj41
-rw-r--r--VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.xml473
-rw-r--r--VNLib.Plugins.Extensions.Data/l473
14 files changed, 1962 insertions, 0 deletions
diff --git a/VNLib.Plugins.Extensions.Data/Abstractions/IBulkDataStore.cs b/VNLib.Plugins.Extensions.Data/Abstractions/IBulkDataStore.cs
new file mode 100644
index 0000000..3b6ab62
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/Abstractions/IBulkDataStore.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using VNLib.Utils;
+
+namespace VNLib.Plugins.Extensions.Data.Abstractions
+{
+ /// <summary>
+ /// An abstraction that defines a Data-Store that supports
+ /// bulk data operations
+ /// </summary>
+ /// <typeparam name="T">The data-model type</typeparam>
+ public interface IBulkDataStore<T>
+ {
+ /// <summary>
+ /// Deletes a collection of records from the store
+ /// </summary>
+ /// <param name="records">A collection of records to delete</param>
+ ///<returns>A task the resolves the number of entires removed from the store</returns>
+ Task<ERRNO> DeleteBulkAsync(ICollection<T> records);
+ /// <summary>
+ /// Updates a collection of records
+ /// </summary>
+ /// <param name="records">The collection of records to update</param>
+ /// <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ Task<ERRNO> UpdateBulkAsync(ICollection<T> records);
+ /// <summary>
+ /// Creates a bulk collection of records as entries in the store
+ /// </summary>
+ /// <param name="records">The collection of records to add</param>
+ /// <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ Task<ERRNO> CreateBulkAsync(ICollection<T> records);
+ /// <summary>
+ /// Creates or updates individual records from a bulk collection of records
+ /// </summary>
+ /// <param name="records">The collection of records to add</param>
+ /// <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ Task<ERRNO> AddOrUpdateBulkAsync(ICollection<T> records);
+ }
+
+}
diff --git a/VNLib.Plugins.Extensions.Data/Abstractions/IDataStore.cs b/VNLib.Plugins.Extensions.Data/Abstractions/IDataStore.cs
new file mode 100644
index 0000000..f923891
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/Abstractions/IDataStore.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using VNLib.Utils;
+
+namespace VNLib.Plugins.Extensions.Data.Abstractions
+{
+ /// <summary>
+ /// An abstraction that defines a Data-Store and common
+ /// operations that retrieve or manipulate records of data
+ /// </summary>
+ /// <typeparam name="T">The data-model type</typeparam>
+ public interface IDataStore<T>
+ {
+ /// <summary>
+ /// Gets the total number of records in the current store
+ /// </summary>
+ /// <returns>A task that resolves the number of records in the store</returns>
+ Task<long> GetCountAsync();
+ /// <summary>
+ /// Gets the number of records that belong to the specified constraint
+ /// </summary>
+ /// <param name="specifier">A specifier to constrain the reults</param>
+ /// <returns>The number of records that belong to the specifier</returns>
+ Task<long> GetCountAsync(string specifier);
+ /// <summary>
+ /// Gets a record from its key
+ /// </summary>
+ /// <param name="key">The key identifying the unique record</param>
+ /// <returns>A promise that resolves the record identified by the specified key</returns>
+ Task<T?> GetSingleAsync(string key);
+ /// <summary>
+ /// Gets a record from its key
+ /// </summary>
+ /// <param name="specifiers">A variable length specifier arguemnt array for retreiving a single application</param>
+ /// <returns></returns>
+ Task<T?> GetSingleAsync(params string[] specifiers);
+ /// <summary>
+ /// Gets a record from the store with a partial model, intended to complete the model
+ /// </summary>
+ /// <param name="record">The partial model used to query the store</param>
+ /// <returns>A task the resolves the completed data-model</returns>
+ Task<T?> GetSingleAsync(T record);
+ /// <summary>
+ /// Fills a collection with enires retireved from the store using the specifer
+ /// </summary>
+ /// <param name="collection">The collection to add entires to</param>
+ /// <param name="specifier">A specifier argument to constrain results</param>
+ /// <param name="limit">The maximum number of elements to retrieve</param>
+ /// <returns>A Task the resolves to the number of items added to the collection</returns>
+ Task<ERRNO> GetCollectionAsync(ICollection<T> collection, string specifier, int limit);
+ /// <summary>
+ /// Fills a collection with enires retireved from the store using a variable length specifier
+ /// parameter
+ /// </summary>
+ /// <param name="collection">The collection to add entires to</param>
+ /// <param name="limit">The maximum number of elements to retrieve</param>
+ /// <param name="args"></param>
+ /// <returns>A Task the resolves to the number of items added to the collection</returns>
+ Task<ERRNO> GetCollectionAsync(ICollection<T> collection, int limit, params string[] args);
+ /// <summary>
+ /// Updates an entry in the store with the specified record
+ /// </summary>
+ /// <param name="record">The record to update</param>
+ /// <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ Task<ERRNO> UpdateAsync(T record);
+ /// <summary>
+ /// Creates a new entry in the store representing the specified record
+ /// </summary>
+ /// <param name="record">The record to add to the store</param>
+ /// <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ Task<ERRNO> CreateAsync(T record);
+ /// <summary>
+ /// Deletes one or more entrires from the store matching the specified record
+ /// </summary>
+ /// <param name="record">The record to remove from the store</param>
+ /// <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ Task<ERRNO> DeleteAsync(T record);
+ /// <summary>
+ /// Deletes one or more entires from the store matching the specified unique key
+ /// </summary>
+ /// <param name="key">The unique key that identifies the record</param>
+ /// <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ Task<ERRNO> DeleteAsync(string key);
+ /// <summary>
+ /// Deletes one or more entires from the store matching the supplied specifiers
+ /// </summary>
+ /// <param name="specifiers">A variable length array of specifiers used to delete one or more entires</param>
+ /// <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ Task<ERRNO> DeleteAsync(params string[] specifiers);
+ /// <summary>
+ /// Updates an entry in the store if it exists, or creates a new entry if one does not already exist
+ /// </summary>
+ /// <param name="record">The record to add to the store</param>
+ /// <returns>A task the resolves the result of the operation</returns>
+ Task<ERRNO> AddOrUpdateAsync(T record);
+ }
+}
diff --git a/VNLib.Plugins.Extensions.Data/Abstractions/IPaginatedDataStore.cs b/VNLib.Plugins.Extensions.Data/Abstractions/IPaginatedDataStore.cs
new file mode 100644
index 0000000..44c11f8
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/Abstractions/IPaginatedDataStore.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace VNLib.Plugins.Extensions.Data.Abstractions
+{
+ /// <summary>
+ /// Defines a Data-Store that can retirieve and manipulate paginated
+ /// data
+ /// </summary>
+ /// <typeparam name="T">The data-model type</typeparam>
+ public interface IPaginatedDataStore<T>
+ {
+ /// <summary>
+ /// Gets a collection of records using a pagination style query, and adds the records to the collecion
+ /// </summary>
+ /// <param name="collection">The collection to add records to</param>
+ /// <param name="page">Pagination page to get records from</param>
+ /// <param name="limit">The maximum number of items to retrieve from the store</param>
+ /// <returns>A task that resolves the number of items added to the collection</returns>
+ Task<int> GetPageAsync(ICollection<T> collection, int page, int limit);
+ /// <summary>
+ /// Gets a collection of records using a pagination style query with constraint arguments, and adds the records to the collecion
+ /// </summary>
+ /// <param name="collection">The collection to add records to</param>
+ /// <param name="page">Pagination page to get records from</param>
+ /// <param name="limit">The maximum number of items to retrieve from the store</param>
+ /// <param name="constraints">A params array of strings to constrain the result set from the db</param>
+ /// <returns>A task that resolves the number of items added to the collection</returns>
+ Task<int> GetPageAsync(ICollection<T> 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
new file mode 100644
index 0000000..6026f85
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/Abstractions/IUserEntity.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace VNLib.Plugins.Extensions.Data.Abstractions
+{
+ /// <summary>
+ /// Defines an entity base that has an owner, identified by its user-id
+ /// </summary>
+ public interface IUserEntity
+ {
+ /// <summary>
+ /// The user-id of the owner of the entity
+ /// </summary>
+ string? UserId { get; set; }
+ }
+}
diff --git a/VNLib.Plugins.Extensions.Data/DbModelBase.cs b/VNLib.Plugins.Extensions.Data/DbModelBase.cs
new file mode 100644
index 0000000..fc9d1fc
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/DbModelBase.cs
@@ -0,0 +1,24 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+
+namespace VNLib.Plugins.Extensions.Data
+{
+ /// <summary>
+ /// Provides a base for DBSet Records with a timestamp/version
+ /// a unique ID key, and create/modified timestamps
+ /// </summary>
+ public abstract class DbModelBase : IDbModel
+ {
+ ///<inheritdoc/>
+ public abstract string Id { get; set; }
+ ///<inheritdoc/>
+ [Timestamp]
+ [JsonIgnore]
+ public virtual byte[]? Version { get; set; }
+ ///<inheritdoc/>
+ public abstract DateTime Created { get; set; }
+ ///<inheritdoc/>
+ public abstract DateTime LastModified { get; set; }
+ }
+}
diff --git a/VNLib.Plugins.Extensions.Data/DbStore.cs b/VNLib.Plugins.Extensions.Data/DbStore.cs
new file mode 100644
index 0000000..19ada7f
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/DbStore.cs
@@ -0,0 +1,501 @@
+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
+{
+ /// <summary>
+ /// Implements basic data-store functionality with abstract query builders
+ /// </summary>
+ /// <typeparam name="T">A <see cref="DbModelBase"/> implemented type</typeparam>
+ public abstract class DbStore<T> : IDataStore<T>, IPaginatedDataStore<T> where T: class, IDbModel
+ {
+ /// <summary>
+ /// Gets a unique ID for a new record being added to the store
+ /// </summary>
+ public abstract string RecordIdBuilder { get; }
+ /// <summary>
+ /// Gets a new <see cref="TransactionalDbContext"/> ready for use
+ /// </summary>
+ /// <returns></returns>
+ public abstract TransactionalDbContext NewContext();
+
+ /// <summary>
+ /// An object rental for entity collections
+ /// </summary>
+ public ObjectRental<List<T>> ListRental { get; } = ObjectRental.Create<List<T>>(null, static ret => ret.Clear());
+
+ #region Add Or Update
+ ///<inheritdoc/>
+ public virtual async Task<ERRNO> AddOrUpdateAsync(T record)
+ {
+ //Open new db context
+ await using TransactionalDbContext ctx = NewContext();
+ //Open transaction
+ await ctx.OpenTransactionAsync();
+ IQueryable<T> query;
+ if (string.IsNullOrWhiteSpace(record.Id))
+ {
+ //Get the application
+ query = AddOrUpdateQueryBuilder(ctx, record);
+ }
+ else
+ {
+ //Get the application
+ query = (from et in ctx.Set<T>()
+ 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;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<ERRNO> UpdateAsync(T record)
+ {
+ //Open new db context
+ await using TransactionalDbContext ctx = NewContext();
+ //Open transaction
+ await ctx.OpenTransactionAsync();
+ //Get the application
+ IQueryable<T> 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;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<ERRNO> 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;
+ }
+
+ /// <summary>
+ /// 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 <see cref="DbModelBase.Id"/> property
+ /// </summary>
+ /// <param name="context">The active context to query</param>
+ /// <param name="record">The record to search for</param>
+ /// <returns>A query that yields a single record if it exists in the store</returns>
+ protected virtual IQueryable<T> AddOrUpdateQueryBuilder(TransactionalDbContext context, T record)
+ {
+ //default to get single of the specific record
+ return GetSingleQueryBuilder(context, record);
+ }
+ /// <summary>
+ /// Builds a query that attempts to get a single entry from the
+ /// store to update based on the specified record
+ /// </summary>
+ /// <param name="context">The active context to query</param>
+ /// <param name="record">The record to search for</param>
+ /// <returns>A query that yields a single record to update if it exists in the store</returns>
+ protected virtual IQueryable<T> UpdateQueryBuilder(TransactionalDbContext context, T record)
+ {
+ //default to get single of the specific record
+ return GetSingleQueryBuilder(context, record);
+ }
+ /// <summary>
+ /// Updates the current record (if found) to the new record before
+ /// storing the updates.
+ /// </summary>
+ /// <param name="newRecord">The new record to capture data from</param>
+ /// <param name="currentRecord">The current record to be updated</param>
+ protected abstract void OnRecordUpdate(T newRecord, T currentRecord);
+ #endregion
+
+ #region Delete
+ ///<inheritdoc/>
+ public virtual async Task<ERRNO> DeleteAsync(string key)
+ {
+ //Open new db context
+ await using TransactionalDbContext ctx = NewContext();
+ //Open transaction
+ await ctx.OpenTransactionAsync();
+ //Get the template by its id
+ IQueryable<T> query = (from temp in ctx.Set<T>()
+ 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;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<ERRNO> 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<T> 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;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<ERRNO> 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<T> 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;
+ }
+
+ /// <summary>
+ /// Builds a query that results in a single entry to delete from the
+ /// constraint arguments
+ /// </summary>
+ /// <param name="context">The active context</param>
+ /// <param name="constraints">A variable length parameter array of query constraints</param>
+ /// <returns>A query that yields a single record (or no record) to delete</returns>
+ protected virtual IQueryable<T> 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
+ ///<inheritdoc/>
+ public virtual async Task<ERRNO> GetCollectionAsync(ICollection<T> collection, string specifier, int limit)
+ {
+ //Open new db context
+ await using TransactionalDbContext ctx = NewContext();
+ //Open transaction
+ await ctx.OpenTransactionAsync();
+ //Get the single template by its id
+ IAsyncEnumerable<T> entires = GetCollectionQueryBuilder(ctx, specifier).Take(limit).AsAsyncEnumerable();
+ int count = 0;
+ //Enumrate the template and add them to collection
+ await foreach (T entry in entires)
+ {
+ collection.Add(entry);
+ count++;
+ }
+ //close db and transaction
+ await ctx.CommitTransactionAsync();
+ //Return the number of elements add to the collection
+ return count;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<ERRNO> GetCollectionAsync(ICollection<T> collection, int limit, params string[] args)
+ {
+ //Open new db context
+ await using TransactionalDbContext ctx = NewContext();
+ //Open transaction
+ await ctx.OpenTransactionAsync();
+ //Get the single template by its id
+ IAsyncEnumerable<T> entires = GetCollectionQueryBuilder(ctx, args).Take(limit).AsAsyncEnumerable();
+ int count = 0;
+ //Enumrate the template and add them to collection
+ await foreach (T entry in entires)
+ {
+ collection.Add(entry);
+ count++;
+ }
+ //close db and transaction
+ await ctx.CommitTransactionAsync();
+ //Return the number of elements add to the collection
+ return count;
+ }
+
+ /// <summary>
+ /// Builds a query to get a count of records constrained by the specifier
+ /// </summary>
+ /// <param name="context">The active context to run the query on</param>
+ /// <param name="specifier">The specifier constrain</param>
+ /// <returns>A query that can be counted</returns>
+ protected virtual IQueryable<T> GetCollectionQueryBuilder(TransactionalDbContext context, string specifier)
+ {
+ return GetCollectionQueryBuilder(context, new string[] { specifier });
+ }
+
+ /// <summary>
+ /// Builds a query to get a collection of records based on an variable length array of parameters
+ /// </summary>
+ /// <param name="context">The active context to run the query on</param>
+ /// <param name="constraints">An arguments array to constrain the results of the query</param>
+ /// <returns>A query that returns a collection of records from the store</returns>
+ protected abstract IQueryable<T> GetCollectionQueryBuilder(TransactionalDbContext context, params string[] constraints);
+
+ #endregion
+
+ #region Get Count
+ ///<inheritdoc/>
+ public virtual async Task<long> 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<T>().LongCountAsync();
+ //close db and transaction
+ await ctx.CommitTransactionAsync();
+ return count;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<long> 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;
+ }
+
+ /// <summary>
+ /// Builds a query to get a count of records constrained by the specifier
+ /// </summary>
+ /// <param name="context">The active context to run the query on</param>
+ /// <param name="specifier">The specifier constrain</param>
+ /// <returns>A query that can be counted</returns>
+ protected virtual IQueryable<T> GetCountQueryBuilder(TransactionalDbContext context, string specifier)
+ {
+ //Default use the get collection and just call the count method
+ return GetCollectionQueryBuilder(context, specifier);
+ }
+ #endregion
+
+ #region Get Single
+ ///<inheritdoc/>
+ public virtual async Task<T?> 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<T>()
+ where entry.Id == key
+ select entry)
+ .SingleOrDefaultAsync();
+ //close db and transaction
+ await ctx.CommitTransactionAsync();
+ return record;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<T?> 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;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<T?> 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;
+ }
+ /// <summary>
+ /// Builds a query to get a single record from the variable length parameter arguments
+ /// </summary>
+ /// <param name="context">The context to execute query against</param>
+ /// <param name="constraints">Arguments to constrain the results of the query to a single record</param>
+ /// <returns>A query that yields a single record</returns>
+ protected abstract IQueryable<T> GetSingleQueryBuilder(TransactionalDbContext context, params string[] constraints);
+ /// <summary>
+ /// <para>
+ /// Builds a query to get a single record from the specified record.
+ /// </para>
+ /// <para>
+ /// Unless overridden, performs an ID based query for a single entry
+ /// </para>
+ /// </summary>
+ /// <param name="context">The context to execute query against</param>
+ /// <param name="record">A record to referrence the lookup</param>
+ /// <returns>A query that yields a single record</returns>
+ protected virtual IQueryable<T> GetSingleQueryBuilder(TransactionalDbContext context, T record)
+ {
+ return from entry in context.Set<T>()
+ where entry.Id == record.Id
+ select entry;
+ }
+ #endregion
+
+ #region Get Page
+ ///<inheritdoc/>
+ public virtual async Task<int> GetPageAsync(ICollection<T> collection, int page, int limit)
+ {
+ //Open db connection
+ await using TransactionalDbContext ctx = NewContext();
+ //Open transaction
+ await ctx.OpenTransactionAsync();
+ //Get a page offset and a limit for the
+ IAsyncEnumerable<T> records = ctx.Set<T>()
+ .Skip(page * limit)
+ .Take(limit)
+ .AsAsyncEnumerable();
+ int count = 0;
+ //Enumrate the template and add them to collection
+ await foreach (T record in records)
+ {
+ collection.Add(record);
+ count++;
+ }
+ //close db and transaction
+ await ctx.CommitTransactionAsync();
+ //Return the number of elements add to the collection
+ return count;
+ }
+ ///<inheritdoc/>
+ public virtual async Task<int> GetPageAsync(ICollection<T> collection, int page, int limit, params string[] constraints)
+ {
+ //Open new db context
+ await using TransactionalDbContext ctx = NewContext();
+ //Open transaction
+ await ctx.OpenTransactionAsync();
+ //Get the single template by its id
+ IAsyncEnumerable<T> entires = GetPageQueryBuilder(ctx, constraints)
+ .Skip(page * limit)
+ .Take(limit)
+ .AsAsyncEnumerable();
+ int count = 0;
+ //Enumrate the template and add them to collection
+ await foreach (T entry in entires)
+ {
+ collection.Add(entry);
+ count++;
+ }
+ //close db and transaction
+ await ctx.CommitTransactionAsync();
+ //Return the number of elements add to the collection
+ return count;
+ }
+ /// <summary>
+ /// Builds a query to get a collection of records based on an variable length array of parameters
+ /// </summary>
+ /// <param name="context">The active context to run the query on</param>
+ /// <param name="constraints">An arguments array to constrain the results of the query</param>
+ /// <returns>A query that returns a paginated collection of records from the store</returns>
+ protected virtual IQueryable<T> 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
new file mode 100644
index 0000000..3962a6d
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/Extensions.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+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<string, string> 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<string, string> 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<ERRNO> AddBulkAsync<TEntity>(this DbStore<TEntity> store, IEnumerable<TEntity> 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<TEntity> set = database.Set<TEntity>();
+ //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
new file mode 100644
index 0000000..fa05307
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/IDbModel.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace VNLib.Plugins.Extensions.Data
+{
+ /// <summary>
+ /// Represents a basic data model for an EFCore entity
+ /// for support in data-stores
+ /// </summary>
+ public interface IDbModel
+ {
+ /// <summary>
+ /// A unique id for the entity
+ /// </summary>
+ string Id { get; set; }
+ /// <summary>
+ /// The <see cref="DateTime"/> the entity was created in the store
+ /// </summary>
+ DateTime Created { get; set; }
+ /// <summary>
+ /// The <see cref="DateTime"/> the entity was last modified in the store
+ /// </summary>
+ DateTime LastModified { get; set; }
+ /// <summary>
+ /// Entity concurrency token
+ /// </summary>
+ 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
new file mode 100644
index 0000000..00f0bf2
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/ProtectedDbStore.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Linq;
+
+using VNLib.Plugins.Extensions.Data.Abstractions;
+
+namespace VNLib.Plugins.Extensions.Data
+{
+#nullable enable
+ /// <summary>
+ /// A data store that provides unique identities and protections based on an entity that has an owner <see cref="IUserEntity"/>
+ /// </summary>
+ public abstract class ProtectedDbStore<TEntity> : DbStore<TEntity> where TEntity : class, IDbModel, IUserEntity
+ {
+ ///<inheritdoc/>
+ protected override IQueryable<TEntity> GetCollectionQueryBuilder(TransactionalDbContext context, params string[] constraints)
+ {
+ string userId = constraints[0];
+ //Query items for the user and its id
+ return from item in context.Set<TEntity>()
+ where item.UserId == userId
+ orderby item.Created descending
+ select item;
+ }
+
+ /// <summary>
+ /// Gets a single item contrained by a given user-id and item id
+ /// </summary>
+ /// <param name="context"></param>
+ /// <param name="constraints"></param>
+ /// <returns></returns>
+ protected override IQueryable<TEntity> 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<TEntity>()
+ where item.Id == key && item.UserId == userId
+ select item;
+ }
+ ///<inheritdoc/>
+ protected override IQueryable<TEntity> 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
new file mode 100644
index 0000000..6d0dcec
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/ProtectedEntityExtensions.cs
@@ -0,0 +1,62 @@
+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
+ {
+ /// <summary>
+ /// Updates the specified record within the store
+ /// </summary>
+ /// <param name="store"></param>
+ /// <param name="record">The record to update</param>
+ /// <param name="userId">The userid of the record owner</param>
+ /// <returns>A task that evaluates to the number of records modified</returns>
+ public static Task<ERRNO> UpdateAsync<TEntity>(this IDataStore<TEntity> store, TEntity record, string userId) where TEntity : class, IDbModel, IUserEntity
+ {
+ record.UserId = userId;
+ return store.UpdateAsync(record);
+ }
+
+ /// <summary>
+ /// Updates the specified record within the store
+ /// </summary>
+ /// <param name="store"></param>
+ /// <param name="record">The record to update</param>
+ /// <param name="userId">The userid of the record owner</param>
+ /// <returns>A task that evaluates to the number of records modified</returns>
+ public static Task<ERRNO> CreateAsync<TEntity>(this IDataStore<TEntity> store, TEntity record, string userId) where TEntity : class, IDbModel, IUserEntity
+ {
+ record.UserId = userId;
+ return store.CreateAsync(record);
+ }
+
+ /// <summary>
+ /// Gets a single entity from its ID and user-id
+ /// </summary>
+ /// <param name="store"></param>
+ /// <param name="key">The unique id of the entity</param>
+ /// <param name="userId">The user's id that owns the resource</param>
+ /// <returns>A task that resolves the entity or null if not found</returns>
+ public static Task<TEntity?> GetSingleAsync<TEntity>(this IDataStore<TEntity> store, string key, string userId) where TEntity : class, IDbModel, IUserEntity
+ {
+ return store.GetSingleAsync(key, userId);
+ }
+
+ /// <summary>
+ /// Deletes a single entiry by its ID only if it belongs to the speicifed user
+ /// </summary>
+ /// <param name="store"></param>
+ /// <param name="key">The unique id of the entity</param>
+ /// <param name="userId">The user's id that owns the resource</param>
+ /// <returns>A task the resolves the number of eneities deleted (should evaluate to true or false)</returns>
+ public static Task<ERRNO> DeleteAsync<TEntity>(this IDataStore<TEntity> store, string key, string userId) where TEntity : class, IDbModel, IUserEntity
+ {
+ return store.DeleteAsync(key, userId);
+ }
+ }
+}
diff --git a/VNLib.Plugins.Extensions.Data/TransactionalDbContext.cs b/VNLib.Plugins.Extensions.Data/TransactionalDbContext.cs
new file mode 100644
index 0000000..8573c8f
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/TransactionalDbContext.cs
@@ -0,0 +1,69 @@
+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, IDisposable
+ {
+ /// <summary>
+ /// <inheritdoc/>
+ /// </summary>
+ protected TransactionalDbContext()
+ {}
+ /// <summary>
+ /// <inheritdoc/>
+ /// </summary>
+ protected TransactionalDbContext(DbContextOptions options) : base(options)
+ {}
+
+ /// <summary>
+ /// The transaction that was opened on the current context
+ /// </summary>
+ public IDbContextTransaction? Transaction { get; set; }
+ ///<inheritdoc/>
+ public override void Dispose()
+ {
+ //dispose the transaction
+ this.Transaction?.Dispose();
+ base.Dispose();
+ }
+
+ /// <summary>
+ /// Opens a single transaction on the current context. If a transaction is already open,
+ /// it is disposed and a new transaction is begun.
+ /// </summary>
+ public async Task OpenTransactionAsync(CancellationToken cancellationToken = default)
+ {
+ //open a new transaction on the current database
+ this.Transaction = await base.Database.BeginTransactionAsync(cancellationToken);
+ }
+ /// <summary>
+ /// Invokes the <see cref="IDbContextTransaction.Commit"/> on the current context
+ /// </summary>
+ public Task CommitTransactionAsync(CancellationToken token = default)
+ {
+ return Transaction != null ? Transaction.CommitAsync(token) : Task.CompletedTask;
+ }
+ /// <summary>
+ /// Invokes the <see cref="IDbContextTransaction.Rollback"/> on the current context
+ /// </summary>
+ public Task RollbackTransctionAsync(CancellationToken token = default)
+ {
+ return Transaction != null ? Transaction.RollbackAsync(token) : Task.CompletedTask;
+ }
+ ///<inheritdoc/>
+ public override async ValueTask DisposeAsync()
+ {
+ //If transaction has been created, dispose the transaction
+ if(this.Transaction != null)
+ {
+ await this.Transaction.DisposeAsync();
+ }
+ await base.DisposeAsync();
+ GC.SuppressFinalize(this);
+ }
+ }
+} \ 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
new file mode 100644
index 0000000..2a87627
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.csproj
@@ -0,0 +1,41 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <RootNamespace>VNLib.Plugins.Extensions.Data</RootNamespace>
+ <Platforms>AnyCPU;x64</Platforms>
+ </PropertyGroup>
+
+ <!-- Resolve nuget dll files and store them in the output dir -->
+ <PropertyGroup>
+ <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
+ <AssemblyName>VNLib.Plugins.Extensions.Data</AssemblyName>
+ <Authors>Vaughn Nugent</Authors>
+ <Description>Data extensions for VNLib Plugins</Description>
+ <Copyright>Copyright © 2022 Vaughn Nugent</Copyright>
+ <PackageProjectUrl>www.vaughnnugent.com/resources</PackageProjectUrl>
+ <Version>1.0.0.1</Version>
+ <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <DocumentationFile>l</DocumentationFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="ErrorProne.NET.Structs" Version="0.1.2">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.10" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\VNLib\Utils\src\VNLib.Utils.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.xml b/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.xml
new file mode 100644
index 0000000..638637d
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/VNLib.Plugins.Extensions.Data.xml
@@ -0,0 +1,473 @@
+<?xml version="1.0"?>
+<doc>
+ <assembly>
+ <name>VNLib.Plugins.Extensions.Data</name>
+ </assembly>
+ <members>
+ <member name="T:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1">
+ <summary>
+ An abstraction that defines a Data-Store that supports
+ bulk data operations
+ </summary>
+ <typeparam name="T">The data-model type</typeparam>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1.DeleteBulkAsync(System.Collections.Generic.ICollection{`0})">
+ <summary>
+ Deletes a collection of records from the store
+ </summary>
+ <param name="records">A collection of records to delete</param>
+ <returns>A task the resolves the number of entires removed from the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1.UpdateBulkAsync(System.Collections.Generic.ICollection{`0})">
+ <summary>
+ Updates a collection of records
+ </summary>
+ <param name="records">The collection of records to update</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1.CreateBulkAsync(System.Collections.Generic.ICollection{`0})">
+ <summary>
+ Creates a bulk collection of records as entries in the store
+ </summary>
+ <param name="records">The collection of records to add</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1.AddOrUpdateBulkAsync(System.Collections.Generic.ICollection{`0})">
+ <summary>
+ Creates or updates individual records from a bulk collection of records
+ </summary>
+ <param name="records">The collection of records to add</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1">
+ <summary>
+ An abstraction that defines a Data-Store and common
+ operations that retrieve or manipulate records of data
+ </summary>
+ <typeparam name="T">The data-model type</typeparam>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetCountAsync">
+ <summary>
+ Gets the total number of records in the current store
+ </summary>
+ <returns>A task that resolves the number of records in the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetCountAsync(System.String)">
+ <summary>
+ Gets the number of records that belong to the specified constraint
+ </summary>
+ <param name="specifier">A specifier to constrain the reults</param>
+ <returns>The number of records that belong to the specifier</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetSingleAsync(System.String)">
+ <summary>
+ Gets a record from its key
+ </summary>
+ <param name="key">The key identifying the unique record</param>
+ <returns>A promise that resolves the record identified by the specified key</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetSingleAsync(System.String[])">
+ <summary>
+ Gets a record from its key
+ </summary>
+ <param name="specifiers">A variable length specifier arguemnt array for retreiving a single application</param>
+ <returns></returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetSingleAsync(`0)">
+ <summary>
+ Gets a record from the store with a partial model, intended to complete the model
+ </summary>
+ <param name="record">The partial model used to query the store</param>
+ <returns>A task the resolves the completed data-model</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetCollectionAsync(System.Collections.Generic.ICollection{`0},System.String,System.Int32)">
+ <summary>
+ Fills a collection with enires retireved from the store using the specifer
+ </summary>
+ <param name="collection">The collection to add entires to</param>
+ <param name="specifier">A specifier argument to constrain results</param>
+ <param name="limit">The maximum number of elements to retrieve</param>
+ <returns>A Task the resolves to the number of items added to the collection</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetCollectionAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.String[])">
+ <summary>
+ Fills a collection with enires retireved from the store using a variable length specifier
+ parameter
+ </summary>
+ <param name="collection">The collection to add entires to</param>
+ <param name="limit">The maximum number of elements to retrieve</param>
+ <param name="args"></param>
+ <returns>A Task the resolves to the number of items added to the collection</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.UpdateAsync(`0)">
+ <summary>
+ Updates an entry in the store with the specified record
+ </summary>
+ <param name="record">The record to update</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.CreateAsync(`0)">
+ <summary>
+ Creates a new entry in the store representing the specified record
+ </summary>
+ <param name="record">The record to add to the store</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.DeleteAsync(`0)">
+ <summary>
+ Deletes one or more entrires from the store matching the specified record
+ </summary>
+ <param name="record">The record to remove from the store</param>
+ <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.DeleteAsync(System.String)">
+ <summary>
+ Deletes one or more entires from the store matching the specified unique key
+ </summary>
+ <param name="key">The unique key that identifies the record</param>
+ <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.DeleteAsync(System.String[])">
+ <summary>
+ Deletes one or more entires from the store matching the supplied specifiers
+ </summary>
+ <param name="specifiers">A variable length array of specifiers used to delete one or more entires</param>
+ <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.AddOrUpdateAsync(`0)">
+ <summary>
+ Updates an entry in the store if it exists, or creates a new entry if one does not already exist
+ </summary>
+ <param name="record">The record to add to the store</param>
+ <returns>A task the resolves the result of the operation</returns>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.Abstractions.IPaginatedDataStore`1">
+ <summary>
+ Defines a Data-Store that can retirieve and manipulate paginated
+ data
+ </summary>
+ <typeparam name="T">The data-model type</typeparam>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IPaginatedDataStore`1.GetPageAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.Int32)">
+ <summary>
+ Gets a collection of records using a pagination style query, and adds the records to the collecion
+ </summary>
+ <param name="collection">The collection to add records to</param>
+ <param name="page">Pagination page to get records from</param>
+ <param name="limit">The maximum number of items to retrieve from the store</param>
+ <returns>A task that resolves the number of items added to the collection</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IPaginatedDataStore`1.GetPageAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.Int32,System.String[])">
+ <summary>
+ Gets a collection of records using a pagination style query with constraint arguments, and adds the records to the collecion
+ </summary>
+ <param name="collection">The collection to add records to</param>
+ <param name="page">Pagination page to get records from</param>
+ <param name="limit">The maximum number of items to retrieve from the store</param>
+ <param name="constraints">A params array of strings to constrain the result set from the db</param>
+ <returns>A task that resolves the number of items added to the collection</returns>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.Abstractions.IUserEntity">
+ <summary>
+ Defines an entity base that has an owner, identified by its user-id
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.Abstractions.IUserEntity.UserId">
+ <summary>
+ The user-id of the owner of the entity
+ </summary>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.DbModelBase">
+ <summary>
+ Provides a base for DBSet Records with a timestamp/version
+ a unique ID key, and create/modified timestamps
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbModelBase.Id">
+ <inheritdoc/>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbModelBase.Version">
+ <inheritdoc/>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbModelBase.Created">
+ <inheritdoc/>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbModelBase.LastModified">
+ <inheritdoc/>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.DbStore`1">
+ <summary>
+ Implements basic data-store functionality with abstract query builders
+ </summary>
+ <typeparam name="T">A <see cref="T:VNLib.Plugins.Extensions.Data.DbModelBase"/> implemented type</typeparam>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbStore`1.RecordIdBuilder">
+ <summary>
+ Gets a unique ID for a new record being added to the store
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.NewContext">
+ <summary>
+ Gets a new <see cref="T:VNLib.Plugins.Extensions.Data.TransactionalDbContext"/> ready for use
+ </summary>
+ <returns></returns>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbStore`1.ListRental">
+ <summary>
+ An object rental for entity collections
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.AddOrUpdateAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.UpdateAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.CreateAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.AddOrUpdateQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,`0)">
+ <summary>
+ 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 <see cref="P:VNLib.Plugins.Extensions.Data.DbModelBase.Id"/> property
+ </summary>
+ <param name="context">The active context to query</param>
+ <param name="record">The record to search for</param>
+ <returns>A query that yields a single record if it exists in the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.UpdateQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,`0)">
+ <summary>
+ Builds a query that attempts to get a single entry from the
+ store to update based on the specified record
+ </summary>
+ <param name="context">The active context to query</param>
+ <param name="record">The record to search for</param>
+ <returns>A query that yields a single record to update if it exists in the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.OnRecordUpdate(`0,`0)">
+ <summary>
+ Updates the current record (if found) to the new record before
+ storing the updates.
+ </summary>
+ <param name="newRecord">The new record to capture data from</param>
+ <param name="currentRecord">The current record to be updated</param>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.DeleteAsync(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.DeleteAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.DeleteAsync(System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.DeleteQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Builds a query that results in a single entry to delete from the
+ constraint arguments
+ </summary>
+ <param name="context">The active context</param>
+ <param name="constraints">A variable length parameter array of query constraints</param>
+ <returns>A query that yields a single record (or no record) to delete</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCollectionAsync(System.Collections.Generic.ICollection{`0},System.String,System.Int32)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCollectionAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCollectionQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String)">
+ <summary>
+ Builds a query to get a count of records constrained by the specifier
+ </summary>
+ <param name="context">The active context to run the query on</param>
+ <param name="specifier">The specifier constrain</param>
+ <returns>A query that can be counted</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCollectionQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Builds a query to get a collection of records based on an variable length array of parameters
+ </summary>
+ <param name="context">The active context to run the query on</param>
+ <param name="constraints">An arguments array to constrain the results of the query</param>
+ <returns>A query that returns a collection of records from the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCountAsync">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCountAsync(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCountQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String)">
+ <summary>
+ Builds a query to get a count of records constrained by the specifier
+ </summary>
+ <param name="context">The active context to run the query on</param>
+ <param name="specifier">The specifier constrain</param>
+ <returns>A query that can be counted</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleAsync(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleAsync(System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Builds a query to get a single record from the variable length parameter arguments
+ </summary>
+ <param name="context">The context to execute query against</param>
+ <param name="constraints">Arguments to constrain the results of the query to a single record</param>
+ <returns>A query that yields a single record</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,`0)">
+ <summary>
+ <para>
+ Builds a query to get a single record from the specified record.
+ </para>
+ <para>
+ Unless overridden, performs an ID based query for a single entry
+ </para>
+ </summary>
+ <param name="context">The context to execute query against</param>
+ <param name="record">A record to referrence the lookup</param>
+ <returns>A query that yields a single record</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetPageAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.Int32)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetPageAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.Int32,System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetPageQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Builds a query to get a collection of records based on an variable length array of parameters
+ </summary>
+ <param name="context">The active context to run the query on</param>
+ <param name="constraints">An arguments array to constrain the results of the query</param>
+ <returns>A query that returns a paginated collection of records from the store</returns>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.IDbModel">
+ <summary>
+ Represents a basic data model for an EFCore entity
+ for support in data-stores
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.IDbModel.Id">
+ <summary>
+ A unique id for the entity
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.IDbModel.Created">
+ <summary>
+ The <see cref="T:System.DateTimeOffset"/> the entity was created in the store
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.IDbModel.LastModified">
+ <summary>
+ The <see cref="T:System.DateTimeOffset"/> the entity was last modified in the store
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.IDbModel.Version">
+ <summary>
+ Entity concurrency token
+ </summary>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.ProtectedDbStore`1">
+ <summary>
+ A data store that provides unique identities and protections based on an entity that has an owner <see cref="T:VNLib.Plugins.Extensions.Data.Abstractions.IUserEntity"/>
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedDbStore`1.GetCollectionQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedDbStore`1.GetSingleQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Gets a single item contrained by a given user-id and item id
+ </summary>
+ <param name="context"></param>
+ <param name="constraints"></param>
+ <returns></returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedDbStore`1.GetSingleQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedEntityExtensions.UpdateAsync``1(VNLib.Plugins.Extensions.Data.Abstractions.IDataStore{``0},``0,System.String)">
+ <summary>
+ Updates the specified record within the store
+ </summary>
+ <param name="store"></param>
+ <param name="record">The record to update</param>
+ <param name="userId">The userid of the record owner</param>
+ <returns>A task that evaluates to the number of records modified</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedEntityExtensions.CreateAsync``1(VNLib.Plugins.Extensions.Data.Abstractions.IDataStore{``0},``0,System.String)">
+ <summary>
+ Updates the specified record within the store
+ </summary>
+ <param name="store"></param>
+ <param name="record">The record to update</param>
+ <param name="userId">The userid of the record owner</param>
+ <returns>A task that evaluates to the number of records modified</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedEntityExtensions.GetSingleAsync``1(VNLib.Plugins.Extensions.Data.Abstractions.IDataStore{``0},System.String,System.String)">
+ <summary>
+ Gets a single entity from its ID and user-id
+ </summary>
+ <param name="store"></param>
+ <param name="key">The unique id of the entity</param>
+ <param name="userId">The user's id that owns the resource</param>
+ <returns>A task that resolves the entity or null if not found</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedEntityExtensions.DeleteAsync``1(VNLib.Plugins.Extensions.Data.Abstractions.IDataStore{``0},System.String,System.String)">
+ <summary>
+ Deletes a single entiry by its ID only if it belongs to the speicifed user
+ </summary>
+ <param name="store"></param>
+ <param name="key">The unique id of the entity</param>
+ <param name="userId">The user's id that owns the resource</param>
+ <returns>A task the resolves the number of eneities deleted (should evaluate to true or false)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.#ctor">
+ <summary>
+ <inheritdoc/>
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.#ctor(Microsoft.EntityFrameworkCore.DbContextOptions)">
+ <summary>
+ <inheritdoc/>
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.TransactionalDbContext.Transaction">
+ <summary>
+ The transaction that was opened on the current context
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.Dispose">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.OpenTransactionAsync(System.Threading.CancellationToken)">
+ <summary>
+ Opens a single transaction on the current context. If a transaction is already open,
+ it is disposed and a new transaction is begun.
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.CommitTransactionAsync(System.Threading.CancellationToken)">
+ <summary>
+ Invokes the <see cref="M:Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction.Commit"/> on the current context
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.RollbackTransctionAsync(System.Threading.CancellationToken)">
+ <summary>
+ Invokes the <see cref="M:Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction.Rollback"/> on the current context
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.DisposeAsync">
+ <inheritdoc/>
+ </member>
+ </members>
+</doc>
diff --git a/VNLib.Plugins.Extensions.Data/l b/VNLib.Plugins.Extensions.Data/l
new file mode 100644
index 0000000..72817ee
--- /dev/null
+++ b/VNLib.Plugins.Extensions.Data/l
@@ -0,0 +1,473 @@
+<?xml version="1.0"?>
+<doc>
+ <assembly>
+ <name>VNLib.Plugins.Extensions.Data</name>
+ </assembly>
+ <members>
+ <member name="T:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1">
+ <summary>
+ An abstraction that defines a Data-Store that supports
+ bulk data operations
+ </summary>
+ <typeparam name="T">The data-model type</typeparam>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1.DeleteBulkAsync(System.Collections.Generic.ICollection{`0})">
+ <summary>
+ Deletes a collection of records from the store
+ </summary>
+ <param name="records">A collection of records to delete</param>
+ <returns>A task the resolves the number of entires removed from the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1.UpdateBulkAsync(System.Collections.Generic.ICollection{`0})">
+ <summary>
+ Updates a collection of records
+ </summary>
+ <param name="records">The collection of records to update</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1.CreateBulkAsync(System.Collections.Generic.ICollection{`0})">
+ <summary>
+ Creates a bulk collection of records as entries in the store
+ </summary>
+ <param name="records">The collection of records to add</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IBulkDataStore`1.AddOrUpdateBulkAsync(System.Collections.Generic.ICollection{`0})">
+ <summary>
+ Creates or updates individual records from a bulk collection of records
+ </summary>
+ <param name="records">The collection of records to add</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1">
+ <summary>
+ An abstraction that defines a Data-Store and common
+ operations that retrieve or manipulate records of data
+ </summary>
+ <typeparam name="T">The data-model type</typeparam>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetCountAsync">
+ <summary>
+ Gets the total number of records in the current store
+ </summary>
+ <returns>A task that resolves the number of records in the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetCountAsync(System.String)">
+ <summary>
+ Gets the number of records that belong to the specified constraint
+ </summary>
+ <param name="specifier">A specifier to constrain the reults</param>
+ <returns>The number of records that belong to the specifier</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetSingleAsync(System.String)">
+ <summary>
+ Gets a record from its key
+ </summary>
+ <param name="key">The key identifying the unique record</param>
+ <returns>A promise that resolves the record identified by the specified key</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetSingleAsync(System.String[])">
+ <summary>
+ Gets a record from its key
+ </summary>
+ <param name="specifiers">A variable length specifier arguemnt array for retreiving a single application</param>
+ <returns></returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetSingleAsync(`0)">
+ <summary>
+ Gets a record from the store with a partial model, intended to complete the model
+ </summary>
+ <param name="record">The partial model used to query the store</param>
+ <returns>A task the resolves the completed data-model</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetCollectionAsync(System.Collections.Generic.ICollection{`0},System.String,System.Int32)">
+ <summary>
+ Fills a collection with enires retireved from the store using the specifer
+ </summary>
+ <param name="collection">The collection to add entires to</param>
+ <param name="specifier">A specifier argument to constrain results</param>
+ <param name="limit">The maximum number of elements to retrieve</param>
+ <returns>A Task the resolves to the number of items added to the collection</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.GetCollectionAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.String[])">
+ <summary>
+ Fills a collection with enires retireved from the store using a variable length specifier
+ parameter
+ </summary>
+ <param name="collection">The collection to add entires to</param>
+ <param name="limit">The maximum number of elements to retrieve</param>
+ <param name="args"></param>
+ <returns>A Task the resolves to the number of items added to the collection</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.UpdateAsync(`0)">
+ <summary>
+ Updates an entry in the store with the specified record
+ </summary>
+ <param name="record">The record to update</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.CreateAsync(`0)">
+ <summary>
+ Creates a new entry in the store representing the specified record
+ </summary>
+ <param name="record">The record to add to the store</param>
+ <returns>A task the resolves an error code (should evaluate to false on failure, and true on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.DeleteAsync(`0)">
+ <summary>
+ Deletes one or more entrires from the store matching the specified record
+ </summary>
+ <param name="record">The record to remove from the store</param>
+ <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.DeleteAsync(System.String)">
+ <summary>
+ Deletes one or more entires from the store matching the specified unique key
+ </summary>
+ <param name="key">The unique key that identifies the record</param>
+ <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.DeleteAsync(System.String[])">
+ <summary>
+ Deletes one or more entires from the store matching the supplied specifiers
+ </summary>
+ <param name="specifiers">A variable length array of specifiers used to delete one or more entires</param>
+ <returns>A task the resolves the number of records removed(should evaluate to false on failure, and deleted count on success)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IDataStore`1.AddOrUpdateAsync(`0)">
+ <summary>
+ Updates an entry in the store if it exists, or creates a new entry if one does not already exist
+ </summary>
+ <param name="record">The record to add to the store</param>
+ <returns>A task the resolves the result of the operation</returns>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.Abstractions.IPaginatedDataStore`1">
+ <summary>
+ Defines a Data-Store that can retirieve and manipulate paginated
+ data
+ </summary>
+ <typeparam name="T">The data-model type</typeparam>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IPaginatedDataStore`1.GetPageAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.Int32)">
+ <summary>
+ Gets a collection of records using a pagination style query, and adds the records to the collecion
+ </summary>
+ <param name="collection">The collection to add records to</param>
+ <param name="page">Pagination page to get records from</param>
+ <param name="limit">The maximum number of items to retrieve from the store</param>
+ <returns>A task that resolves the number of items added to the collection</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.Abstractions.IPaginatedDataStore`1.GetPageAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.Int32,System.String[])">
+ <summary>
+ Gets a collection of records using a pagination style query with constraint arguments, and adds the records to the collecion
+ </summary>
+ <param name="collection">The collection to add records to</param>
+ <param name="page">Pagination page to get records from</param>
+ <param name="limit">The maximum number of items to retrieve from the store</param>
+ <param name="constraints">A params array of strings to constrain the result set from the db</param>
+ <returns>A task that resolves the number of items added to the collection</returns>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.Abstractions.IUserEntity">
+ <summary>
+ Defines an entity base that has an owner, identified by its user-id
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.Abstractions.IUserEntity.UserId">
+ <summary>
+ The user-id of the owner of the entity
+ </summary>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.DbModelBase">
+ <summary>
+ Provides a base for DBSet Records with a timestamp/version
+ a unique ID key, and create/modified timestamps
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbModelBase.Id">
+ <inheritdoc/>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbModelBase.Version">
+ <inheritdoc/>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbModelBase.Created">
+ <inheritdoc/>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbModelBase.LastModified">
+ <inheritdoc/>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.DbStore`1">
+ <summary>
+ Implements basic data-store functionality with abstract query builders
+ </summary>
+ <typeparam name="T">A <see cref="T:VNLib.Plugins.Extensions.Data.DbModelBase"/> implemented type</typeparam>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbStore`1.RecordIdBuilder">
+ <summary>
+ Gets a unique ID for a new record being added to the store
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.NewContext">
+ <summary>
+ Gets a new <see cref="T:VNLib.Plugins.Extensions.Data.TransactionalDbContext"/> ready for use
+ </summary>
+ <returns></returns>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.DbStore`1.ListRental">
+ <summary>
+ An object rental for entity collections
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.AddOrUpdateAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.UpdateAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.CreateAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.AddOrUpdateQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,`0)">
+ <summary>
+ 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 <see cref="P:VNLib.Plugins.Extensions.Data.DbModelBase.Id"/> property
+ </summary>
+ <param name="context">The active context to query</param>
+ <param name="record">The record to search for</param>
+ <returns>A query that yields a single record if it exists in the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.UpdateQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,`0)">
+ <summary>
+ Builds a query that attempts to get a single entry from the
+ store to update based on the specified record
+ </summary>
+ <param name="context">The active context to query</param>
+ <param name="record">The record to search for</param>
+ <returns>A query that yields a single record to update if it exists in the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.OnRecordUpdate(`0,`0)">
+ <summary>
+ Updates the current record (if found) to the new record before
+ storing the updates.
+ </summary>
+ <param name="newRecord">The new record to capture data from</param>
+ <param name="currentRecord">The current record to be updated</param>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.DeleteAsync(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.DeleteAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.DeleteAsync(System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.DeleteQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Builds a query that results in a single entry to delete from the
+ constraint arguments
+ </summary>
+ <param name="context">The active context</param>
+ <param name="constraints">A variable length parameter array of query constraints</param>
+ <returns>A query that yields a single record (or no record) to delete</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCollectionAsync(System.Collections.Generic.ICollection{`0},System.String,System.Int32)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCollectionAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCollectionQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String)">
+ <summary>
+ Builds a query to get a count of records constrained by the specifier
+ </summary>
+ <param name="context">The active context to run the query on</param>
+ <param name="specifier">The specifier constrain</param>
+ <returns>A query that can be counted</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCollectionQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Builds a query to get a collection of records based on an variable length array of parameters
+ </summary>
+ <param name="context">The active context to run the query on</param>
+ <param name="constraints">An arguments array to constrain the results of the query</param>
+ <returns>A query that returns a collection of records from the store</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCountAsync">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCountAsync(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetCountQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String)">
+ <summary>
+ Builds a query to get a count of records constrained by the specifier
+ </summary>
+ <param name="context">The active context to run the query on</param>
+ <param name="specifier">The specifier constrain</param>
+ <returns>A query that can be counted</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleAsync(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleAsync(`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleAsync(System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Builds a query to get a single record from the variable length parameter arguments
+ </summary>
+ <param name="context">The context to execute query against</param>
+ <param name="constraints">Arguments to constrain the results of the query to a single record</param>
+ <returns>A query that yields a single record</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetSingleQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,`0)">
+ <summary>
+ <para>
+ Builds a query to get a single record from the specified record.
+ </para>
+ <para>
+ Unless overridden, performs an ID based query for a single entry
+ </para>
+ </summary>
+ <param name="context">The context to execute query against</param>
+ <param name="record">A record to referrence the lookup</param>
+ <returns>A query that yields a single record</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetPageAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.Int32)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetPageAsync(System.Collections.Generic.ICollection{`0},System.Int32,System.Int32,System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.DbStore`1.GetPageQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Builds a query to get a collection of records based on an variable length array of parameters
+ </summary>
+ <param name="context">The active context to run the query on</param>
+ <param name="constraints">An arguments array to constrain the results of the query</param>
+ <returns>A query that returns a paginated collection of records from the store</returns>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.IDbModel">
+ <summary>
+ Represents a basic data model for an EFCore entity
+ for support in data-stores
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.IDbModel.Id">
+ <summary>
+ A unique id for the entity
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.IDbModel.Created">
+ <summary>
+ The <see cref="T:System.DateTime"/> the entity was created in the store
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.IDbModel.LastModified">
+ <summary>
+ The <see cref="T:System.DateTime"/> the entity was last modified in the store
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.IDbModel.Version">
+ <summary>
+ Entity concurrency token
+ </summary>
+ </member>
+ <member name="T:VNLib.Plugins.Extensions.Data.ProtectedDbStore`1">
+ <summary>
+ A data store that provides unique identities and protections based on an entity that has an owner <see cref="T:VNLib.Plugins.Extensions.Data.Abstractions.IUserEntity"/>
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedDbStore`1.GetCollectionQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedDbStore`1.GetSingleQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,System.String[])">
+ <summary>
+ Gets a single item contrained by a given user-id and item id
+ </summary>
+ <param name="context"></param>
+ <param name="constraints"></param>
+ <returns></returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedDbStore`1.GetSingleQueryBuilder(VNLib.Plugins.Extensions.Data.TransactionalDbContext,`0)">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedEntityExtensions.UpdateAsync``1(VNLib.Plugins.Extensions.Data.Abstractions.IDataStore{``0},``0,System.String)">
+ <summary>
+ Updates the specified record within the store
+ </summary>
+ <param name="store"></param>
+ <param name="record">The record to update</param>
+ <param name="userId">The userid of the record owner</param>
+ <returns>A task that evaluates to the number of records modified</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedEntityExtensions.CreateAsync``1(VNLib.Plugins.Extensions.Data.Abstractions.IDataStore{``0},``0,System.String)">
+ <summary>
+ Updates the specified record within the store
+ </summary>
+ <param name="store"></param>
+ <param name="record">The record to update</param>
+ <param name="userId">The userid of the record owner</param>
+ <returns>A task that evaluates to the number of records modified</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedEntityExtensions.GetSingleAsync``1(VNLib.Plugins.Extensions.Data.Abstractions.IDataStore{``0},System.String,System.String)">
+ <summary>
+ Gets a single entity from its ID and user-id
+ </summary>
+ <param name="store"></param>
+ <param name="key">The unique id of the entity</param>
+ <param name="userId">The user's id that owns the resource</param>
+ <returns>A task that resolves the entity or null if not found</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.ProtectedEntityExtensions.DeleteAsync``1(VNLib.Plugins.Extensions.Data.Abstractions.IDataStore{``0},System.String,System.String)">
+ <summary>
+ Deletes a single entiry by its ID only if it belongs to the speicifed user
+ </summary>
+ <param name="store"></param>
+ <param name="key">The unique id of the entity</param>
+ <param name="userId">The user's id that owns the resource</param>
+ <returns>A task the resolves the number of eneities deleted (should evaluate to true or false)</returns>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.#ctor">
+ <summary>
+ <inheritdoc/>
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.#ctor(Microsoft.EntityFrameworkCore.DbContextOptions)">
+ <summary>
+ <inheritdoc/>
+ </summary>
+ </member>
+ <member name="P:VNLib.Plugins.Extensions.Data.TransactionalDbContext.Transaction">
+ <summary>
+ The transaction that was opened on the current context
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.Dispose">
+ <inheritdoc/>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.OpenTransactionAsync(System.Threading.CancellationToken)">
+ <summary>
+ Opens a single transaction on the current context. If a transaction is already open,
+ it is disposed and a new transaction is begun.
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.CommitTransactionAsync(System.Threading.CancellationToken)">
+ <summary>
+ Invokes the <see cref="M:Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction.Commit"/> on the current context
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.RollbackTransctionAsync(System.Threading.CancellationToken)">
+ <summary>
+ Invokes the <see cref="M:Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction.Rollback"/> on the current context
+ </summary>
+ </member>
+ <member name="M:VNLib.Plugins.Extensions.Data.TransactionalDbContext.DisposeAsync">
+ <inheritdoc/>
+ </member>
+ </members>
+</doc>