From 3dbe2f369f89ef527d0c0add53534d4d41ee237b Mon Sep 17 00:00:00 2001 From: Kyle Lauffer Date: Fri, 18 Mar 2022 17:09:18 -0400 Subject: [PATCH 1/2] Issue #34 and #17 Adding Async methods for BulkCreateAsync --- src/Core/Interfaces/Data/IRepositoryAsync.cs | 286 +++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 src/Core/Interfaces/Data/IRepositoryAsync.cs diff --git a/src/Core/Interfaces/Data/IRepositoryAsync.cs b/src/Core/Interfaces/Data/IRepositoryAsync.cs new file mode 100644 index 0000000..9661628 --- /dev/null +++ b/src/Core/Interfaces/Data/IRepositoryAsync.cs @@ -0,0 +1,286 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AndcultureCode.CSharp.Core.Interfaces.Entity; + +namespace AndcultureCode.CSharp.Core.Interfaces.Data +{ + public interface IRepositoryAsync + where T : class, IEntity + { + #region Properties + + /// + /// Ability to set and get the underlying DbContext's command timeout + /// + int? CommandTimeout { get; set; } + + #endregion Properties + + + #region Methods + + /// + /// Perform a DbContext.BulkInsert on an enumeration of T within a single transaction + /// + /// + /// + /// + Task>> BulkCreate(IEnumerable items, long? createdById = null); + + /// + /// Calls BulkCreate() with a de-duped list of objects as determined by the + /// property (or properties) of the object for the 'property' argument + /// NOTE: Bulking is generally faster than batching, but locks the table. + /// + /// List of items to attempt to create + /// Property or properties of the typed object to determine distinctness + /// Id of the user creating the entity + /// + /// + + /// + /// Calls BulkCreate() after selecting a subset of 'items' based on the distinct value of 'property' + /// + /// + /// + /// + /// + /// + Task>> BulkCreateDistinct(IEnumerable items, Func property, long? createdById = null); + + /// + /// Calls BulkDelete() with a de-duped list of objects as determined by the + /// property (or properties) of the object for the 'property' argument + /// NOTE: Bulking is generally faster than batching, but locks the table. + /// + /// + /// + /// + /// + Task> BulkDelete(IEnumerable items, long? deletedById = null, bool soft = true); + + /// + /// Ability to update a list of entities in a single bulk operation. + /// + /// List of items to update + /// Id of user updating the entity + /// + Task> BulkUpdate(IEnumerable entities, long? updatedBy = default(long?)); + + /// + /// Ability to create an entity + /// + /// Item to be created + /// Id of user creating the item + /// + Task> Create(T item, long? createdById = null); + + /// + /// Ability to create entities individually using a list + /// + /// List of items to be created + /// Id of user creating the items + /// + Task>> Create(IEnumerable items, long? createdById = null); + + /// + /// Calls batched overload of Create() with a de-duped list of objects as determined by the + /// property (or properties) of the object for the 'property' argument. + /// NOTE: Batching is generally slower than bulking, but does not lock the table. + /// + /// List of items to attempt to create + /// Property or properties of the typed object to determine distinctness + /// Id of the user creating the entity + /// + /// + Task>> CreateDistinct(IEnumerable items, Func property, long? createdById = null); + + /// + /// Ability to delete an entity using an Id + /// + /// Id of item to be deleted + /// Id of user deleting the item + /// Boolean flag for soft-deleting the item + /// + Task> Delete(long id, long? deletedById = null, bool soft = true); + + /// + /// Ability to delete an entity using the entity itself + /// + /// Item to be deleted + /// Id of user deleting the item + /// Boolean flag for soft-deleting the item + /// + Task> Delete(T o, long? deletedById = null, bool soft = true); + + /// + /// Ability to delete a list of entities by batch size. + /// + /// List of items to delete + /// Id of user deleting the items + /// Number of items to include in a batch, defaults to 100 + /// Boolean flag for soft-deleting the items + /// + Task> Delete(IEnumerable items, long? deletedById = null, long batchSize = 100, bool soft = true); + + /// + /// Find all filtered, sorted and paged + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task>> FindAll( + Expression> filter = null, + Func, IOrderedQueryable> orderBy = null, + string includeProperties = null, + int? skip = null, + int? take = null, + bool? ignoreQueryFilters = false, + bool asNoTracking = false + ); + + /// + /// Configure lazy loaded queryable, given provided parameters, to load a list of grouped by a + /// + /// Filter to be used for querying. + /// Properties that should be used for sorting. + /// Filter to be used for grouping by of . + /// Navigation properties that should be included. + /// Number of entities that should be skipped. + /// Number of entities per page. + /// If true, global query filters will be ignored for this query. + /// Ignore change tracking on the result. Set true for read-only operations. + /// + Task>>> FindAll( + Expression> filter = null, + Func, IOrderedQueryable> orderBy = null, + Expression> groupBy = null, + string includeProperties = null, + int? skip = default(int?), + int? take = default(int?), + bool? ignoreQueryFilters = false, + bool asNoTracking = false + ); + + /// + /// Configure lazy loaded queryable, given provided parameters, to load a list of + /// grouped by a and selected by groupBySelector tranformed into + /// ref to: https://docs.microsoft.com/en-us/dotnet/api/system.linq.queryable.groupby?view=netcore-3.1#System_Linq_Queryable_GroupBy__3_System_Linq_IQueryable___0__System_Linq_Expressions_Expression_System_Func___0___1___System_Linq_Expressions_Expression_System_Func___1_System_Collections_Generic_IEnumerable___0____2___ + /// + /// Filter to be used for querying. + /// Properties that should be used for sorting. + /// Filter to be used for grouping by of . + /// Selector to be used on groupBy used to create a result of value from each group. + /// Navigation properties that should be included. + /// Number of entities that should be skipped. + /// Number of entities per page. + /// If true, global query filters will be ignored for this query. + /// Ignore change tracking on the result. Set true for read-only operations. + /// + Task>> FindAll( + Expression> filter = null, + Func, IOrderedQueryable> orderBy = null, + Expression> groupBy = null, + Expression, TResult>> groupBySelector = null, + string includeProperties = null, + int? skip = default(int?), + int? take = default(int?), + bool? ignoreQueryFilters = false, + bool asNoTracking = false + ); + + /// + /// Find all filtered, sorted and paged and converts to an IList + /// + /// + /// + /// + /// + /// + /// + /// + Task>> FindAllCommitted(Expression> filter = null, Func, IOrderedQueryable> orderBy = null, string includeProperties = null, int? skip = null, int? take = null, bool? ignoreQueryFilters = false); + + /// + /// Finds an entity by its Id. + /// + /// The entity identity value. + /// If true, global query filters will be ignored for this query. + /// The entity with the provided identity value. + Task> FindById(long id, bool? ignoreQueryFilters = false); + + /// + /// Finds an entity by its Id that also matches a filter. + /// + /// The entity identity value. + /// Filter to be used for querying. + /// The entity witht he provided identity value and filter condition met. + Task> FindById(long id, Expression> filter); + + /// + /// Finds an entity by its Id. + /// + /// The entity identity value. + /// Navigation properties that should be included. + /// The entity with the provided identity value. + Task> FindById(long id, params Expression>[] includeProperties); + + /// + /// Finds an entity by its Id. + /// + /// The entity identity value. + /// If true, global query filters will be ignored for this query. + /// Navigation properties that should be included. + /// The entity with the provided identity value. + Task> FindById(long id, bool? ignoreQueryFilters = false, params Expression>[] includeProperties); + + /// + /// Finds an entity by its Id. + /// + /// The entity identity value. + /// Navigation properties that should be included. + /// The entity with the provided identity value. + Task> FindById(long id, params string[] includeProperties); + + /// + /// Ability to restore a soft-deleted entity using the entity itself. + /// + /// Entity to be restored + /// + Task> Restore(T o); + + /// + /// Ability to restore a soft-deleted entity using the entity id. + /// + /// Id of entity to be restored + /// + Task> Restore(long id); + + /// + /// Ability to create or update an entity + /// + /// Item to create or update + /// Id of user creating or updating the entity + /// + Task> Update(T item, long? updatedBy = null); + + /// + /// Calls Update one-by-one for each item in the enumerated entities. + /// For large operations, BulkUpdate() is more efficient. + /// + /// + /// + /// True if entities updated without any exceptions. False if an exception was thrown. + Task> Update(IEnumerable entities, long? updatedBy = default(long?)); + + #endregion Methods + } +} From a4c62872aadcf74ddaff2fda854dedf927702c51 Mon Sep 17 00:00:00 2001 From: Kyle Lauffer Date: Fri, 18 Mar 2022 17:10:17 -0400 Subject: [PATCH 2/2] Issue #34 and #17 Adding Async methods for BulkCreateAsync --- src/Conductors/RepositoryConductor.cs | 68 +---- src/Conductors/RepositoryConductorAsync.cs | 78 +++++ src/Conductors/RepositoryCreateConductor.cs | 39 +-- .../RepositoryCreateConductorAsync.cs | 46 +++ .../Conductors/IRepositoryCreateConductor.cs | 9 +- .../IRepositoryCreateConductorAsync.cs | 20 ++ src/Core/Interfaces/Data/IRepository.cs | 2 +- src/Core/Interfaces/Data/IRepositoryAsync.cs | 273 +----------------- .../BulkCreateAsyncShould.cs | 63 ++++ .../IRepositoryMock.cs | 24 ++ 10 files changed, 250 insertions(+), 372 deletions(-) create mode 100644 src/Conductors/RepositoryConductorAsync.cs create mode 100644 src/Conductors/RepositoryCreateConductorAsync.cs create mode 100644 src/Core/Interfaces/Conductors/IRepositoryCreateConductorAsync.cs create mode 100644 test/Conductors.Tests/RepositoryConductorTests/BulkCreateAsyncShould.cs create mode 100644 test/Conductors.Tests/RepositoryConductorTests/IRepositoryMock.cs diff --git a/src/Conductors/RepositoryConductor.cs b/src/Conductors/RepositoryConductor.cs index 9c7279d..98a9187 100644 --- a/src/Conductors/RepositoryConductor.cs +++ b/src/Conductors/RepositoryConductor.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading.Tasks; using AndcultureCode.CSharp.Core; using AndcultureCode.CSharp.Core.Extensions; using AndcultureCode.CSharp.Core.Interfaces; @@ -14,71 +15,9 @@ namespace AndcultureCode.CSharp.Conductors /// Provides CRUD operations on a given repository /// /// - public class RepositoryConductor : Conductor, IRepositoryConductor + public partial class RepositoryConductor : Conductor, IRepositoryConductor where T : Entity { - #region Properties - - /// - /// Ability to set and get the underlying repository's command timeout - /// - public int? CommandTimeout - { - get => _readConductor.CommandTimeout; - set - { - _createConductor.CommandTimeout = value; - _deleteConductor.CommandTimeout = value; - _readConductor.CommandTimeout = value; - _updateConductor.CommandTimeout = value; - } - } - - /// - /// Conductor property to create an entity or entities - /// - protected readonly IRepositoryCreateConductor _createConductor; - - /// - /// Conductor property to get an entity or entities - /// - protected readonly IRepositoryReadConductor _readConductor; - - /// - /// Conductor property to update an entity or entities - /// - protected readonly IRepositoryUpdateConductor _updateConductor; - - /// - /// Conductor property to delete an entity or entities - /// - protected readonly IRepositoryDeleteConductor _deleteConductor; - - #endregion Properties - - #region Constructor - - /// - /// Constructor - /// - /// The conductor instance that should be used to perform create operations - /// The conductor instance that should be used to perform read operations - /// The conductor instance that should be used to perform update operations - /// The conductor instance that should be used to perform delete operations - public RepositoryConductor( - IRepositoryCreateConductor createConductor, - IRepositoryReadConductor readConductor, - IRepositoryUpdateConductor updateConductor, - IRepositoryDeleteConductor deleteConductor) - { - _createConductor = createConductor; - _readConductor = readConductor; - _updateConductor = updateConductor; - _deleteConductor = deleteConductor; - } - - #endregion Constructor - #region Public Methods #region Create @@ -88,7 +27,8 @@ public RepositoryConductor( /// /// List of items to create /// Id of user creating the items - /// + /// A collection of the created items + [Obsolete("This method is deprecated in favor of its async counter part", false)] public virtual IResult> BulkCreate(IEnumerable items, long? createdById = default(long?)) => _createConductor.BulkCreate(items, createdById); /// diff --git a/src/Conductors/RepositoryConductorAsync.cs b/src/Conductors/RepositoryConductorAsync.cs new file mode 100644 index 0000000..ce30201 --- /dev/null +++ b/src/Conductors/RepositoryConductorAsync.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using AndcultureCode.CSharp.Core.Interfaces; +using AndcultureCode.CSharp.Core.Interfaces.Conductors; +using AndcultureCode.CSharp.Core.Models.Entities; + +namespace AndcultureCode.CSharp.Conductors +{ + public partial class RepositoryConductor : Conductor, IRepositoryConductor + where T : Entity + { + #region Properties + + /// + /// Ability to set and get the underlying repository's command timeout + /// + public int? CommandTimeout + { + get => _readConductor.CommandTimeout; + set + { + _createConductor.CommandTimeout = value; + _deleteConductor.CommandTimeout = value; + _readConductor.CommandTimeout = value; + _updateConductor.CommandTimeout = value; + } + } + + /// + /// Conductor property to create an entity or entities + /// + protected readonly IRepositoryCreateConductor _createConductor; + + /// + /// Conductor property to get an entity or entities + /// + protected readonly IRepositoryReadConductor _readConductor; + + /// + /// Conductor property to update an entity or entities + /// + protected readonly IRepositoryUpdateConductor _updateConductor; + + /// + /// Conductor property to delete an entity or entities + /// + protected readonly IRepositoryDeleteConductor _deleteConductor; + + #endregion Properties + + #region Constructor + + /// + /// Constructor + /// + /// The conductor instance that should be used to perform create operations + /// The conductor instance that should be used to perform read operations + /// The conductor instance that should be used to perform update operations + /// The conductor instance that should be used to perform delete operations + public RepositoryConductor( + IRepositoryCreateConductor createConductor, + IRepositoryReadConductor readConductor, + IRepositoryUpdateConductor updateConductor, + IRepositoryDeleteConductor deleteConductor) + { + _createConductor = createConductor; + _readConductor = readConductor; + _updateConductor = updateConductor; + _deleteConductor = deleteConductor; + } + + #endregion Constructor + + + public Task>> BulkCreateAsync(IEnumerable items, long? createdById = null) => + _createConductor.BulkCreateAsync(items, createdById); + } +} diff --git a/src/Conductors/RepositoryCreateConductor.cs b/src/Conductors/RepositoryCreateConductor.cs index 6488a1a..03deac9 100644 --- a/src/Conductors/RepositoryCreateConductor.cs +++ b/src/Conductors/RepositoryCreateConductor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using AndcultureCode.CSharp.Core.Interfaces; using AndcultureCode.CSharp.Core.Interfaces.Conductors; using AndcultureCode.CSharp.Core.Interfaces.Data; @@ -11,47 +12,13 @@ namespace AndcultureCode.CSharp.Conductors /// Ability to create an entity or multiple entities /// /// - public class RepositoryCreateConductor : Conductor, IRepositoryCreateConductor + public partial class RepositoryCreateConductor : Conductor, IRepositoryCreateConductor where T : class, IEntity { - #region Properties - - /// - /// Ability to set and get the underlying DbContext's command timeout - /// - public int? CommandTimeout - { - get => _repository.CommandTimeout; - set => _repository.CommandTimeout = value; - } - - readonly IRepository _repository; - - #endregion - - #region Constructor - - /// - /// Constructor - /// - /// - public RepositoryCreateConductor( - IRepository repository - ) - { - _repository = repository; - } - - #endregion #region IRepositoryCreateConductor - /// - /// Ability to create entities using a list in a single bulk operation. - /// - /// List of items to create - /// Id of user creating the items - /// + /// public virtual IResult> BulkCreate(IEnumerable items, long? createdById = default(long?)) { return _repository.BulkCreate(items, createdById); diff --git a/src/Conductors/RepositoryCreateConductorAsync.cs b/src/Conductors/RepositoryCreateConductorAsync.cs new file mode 100644 index 0000000..40c6417 --- /dev/null +++ b/src/Conductors/RepositoryCreateConductorAsync.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AndcultureCode.CSharp.Core.Interfaces; +using AndcultureCode.CSharp.Core.Interfaces.Conductors; +using AndcultureCode.CSharp.Core.Interfaces.Data; +using AndcultureCode.CSharp.Core.Interfaces.Entity; + +namespace AndcultureCode.CSharp.Conductors +{ + public partial class RepositoryCreateConductor : Conductor, IRepositoryCreateConductor + where T : class, IEntity + { + /// + /// Ability to set and get the underlying DbContext's command timeout + /// + public int? CommandTimeout + { + get => _repository.CommandTimeout; + set => _repository.CommandTimeout = value; + } + + readonly IRepository _repository; + + + /// + /// Constructor + /// + /// + public RepositoryCreateConductor( + IRepository repository + ) + { + _repository = repository; + } + + /// + public virtual Task>> BulkCreateAsync(IEnumerable items, long? createdById = null) + { + if(items == null) throw new ArgumentNullException(nameof(items)); + if(!items.Any()) throw new ArgumentException("An empty collection was provided", nameof(items)); + return _repository.BulkCreateAsync(items, createdById); + } + } +} diff --git a/src/Core/Interfaces/Conductors/IRepositoryCreateConductor.cs b/src/Core/Interfaces/Conductors/IRepositoryCreateConductor.cs index bab32a4..99268d4 100644 --- a/src/Core/Interfaces/Conductors/IRepositoryCreateConductor.cs +++ b/src/Core/Interfaces/Conductors/IRepositoryCreateConductor.cs @@ -4,7 +4,7 @@ namespace AndcultureCode.CSharp.Core.Interfaces.Conductors { - public interface IRepositoryCreateConductor + public partial interface IRepositoryCreateConductor where T : class, IEntity { #region Properties @@ -19,6 +19,13 @@ public interface IRepositoryCreateConductor #region Methods + /// + /// Ability to create entities using a list in a single bulk operation. + /// + /// List of items to create + /// Id of user creating the items + /// + [Obsolete("This method is deprecated in favor of its async counter part", false)] IResult> BulkCreate(IEnumerable items, long? createdById = null); /// diff --git a/src/Core/Interfaces/Conductors/IRepositoryCreateConductorAsync.cs b/src/Core/Interfaces/Conductors/IRepositoryCreateConductorAsync.cs new file mode 100644 index 0000000..fa2d21e --- /dev/null +++ b/src/Core/Interfaces/Conductors/IRepositoryCreateConductorAsync.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using AndcultureCode.CSharp.Core.Interfaces.Entity; + +namespace AndcultureCode.CSharp.Core.Interfaces.Conductors +{ + public partial interface IRepositoryCreateConductor + where T : class, IEntity + { + /// + /// Ability to asynchronously create entities using a list in a single bulk operation. + /// + /// List of items to create + /// Id of user creating the items + /// A collection of the created items + Task>> BulkCreateAsync(IEnumerable items, long? createdById = null); + } +} diff --git a/src/Core/Interfaces/Data/IRepository.cs b/src/Core/Interfaces/Data/IRepository.cs index 9f196fb..c431ca9 100644 --- a/src/Core/Interfaces/Data/IRepository.cs +++ b/src/Core/Interfaces/Data/IRepository.cs @@ -10,7 +10,7 @@ namespace AndcultureCode.CSharp.Core.Interfaces.Data /// /// /// - public interface IRepository + public partial interface IRepository where T : class, IEntity { #region Properties diff --git a/src/Core/Interfaces/Data/IRepositoryAsync.cs b/src/Core/Interfaces/Data/IRepositoryAsync.cs index 9661628..4169d2b 100644 --- a/src/Core/Interfaces/Data/IRepositoryAsync.cs +++ b/src/Core/Interfaces/Data/IRepositoryAsync.cs @@ -1,286 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; +using System.Collections.Generic; using System.Threading.Tasks; using AndcultureCode.CSharp.Core.Interfaces.Entity; namespace AndcultureCode.CSharp.Core.Interfaces.Data { - public interface IRepositoryAsync + public partial interface IRepository where T : class, IEntity { - #region Properties - - /// - /// Ability to set and get the underlying DbContext's command timeout - /// - int? CommandTimeout { get; set; } - - #endregion Properties - - - #region Methods - /// /// Perform a DbContext.BulkInsert on an enumeration of T within a single transaction /// /// /// /// - Task>> BulkCreate(IEnumerable items, long? createdById = null); - - /// - /// Calls BulkCreate() with a de-duped list of objects as determined by the - /// property (or properties) of the object for the 'property' argument - /// NOTE: Bulking is generally faster than batching, but locks the table. - /// - /// List of items to attempt to create - /// Property or properties of the typed object to determine distinctness - /// Id of the user creating the entity - /// - /// - - /// - /// Calls BulkCreate() after selecting a subset of 'items' based on the distinct value of 'property' - /// - /// - /// - /// - /// - /// - Task>> BulkCreateDistinct(IEnumerable items, Func property, long? createdById = null); - - /// - /// Calls BulkDelete() with a de-duped list of objects as determined by the - /// property (or properties) of the object for the 'property' argument - /// NOTE: Bulking is generally faster than batching, but locks the table. - /// - /// - /// - /// - /// - Task> BulkDelete(IEnumerable items, long? deletedById = null, bool soft = true); - - /// - /// Ability to update a list of entities in a single bulk operation. - /// - /// List of items to update - /// Id of user updating the entity - /// - Task> BulkUpdate(IEnumerable entities, long? updatedBy = default(long?)); - - /// - /// Ability to create an entity - /// - /// Item to be created - /// Id of user creating the item - /// - Task> Create(T item, long? createdById = null); - - /// - /// Ability to create entities individually using a list - /// - /// List of items to be created - /// Id of user creating the items - /// - Task>> Create(IEnumerable items, long? createdById = null); - - /// - /// Calls batched overload of Create() with a de-duped list of objects as determined by the - /// property (or properties) of the object for the 'property' argument. - /// NOTE: Batching is generally slower than bulking, but does not lock the table. - /// - /// List of items to attempt to create - /// Property or properties of the typed object to determine distinctness - /// Id of the user creating the entity - /// - /// - Task>> CreateDistinct(IEnumerable items, Func property, long? createdById = null); - - /// - /// Ability to delete an entity using an Id - /// - /// Id of item to be deleted - /// Id of user deleting the item - /// Boolean flag for soft-deleting the item - /// - Task> Delete(long id, long? deletedById = null, bool soft = true); - - /// - /// Ability to delete an entity using the entity itself - /// - /// Item to be deleted - /// Id of user deleting the item - /// Boolean flag for soft-deleting the item - /// - Task> Delete(T o, long? deletedById = null, bool soft = true); - - /// - /// Ability to delete a list of entities by batch size. - /// - /// List of items to delete - /// Id of user deleting the items - /// Number of items to include in a batch, defaults to 100 - /// Boolean flag for soft-deleting the items - /// - Task> Delete(IEnumerable items, long? deletedById = null, long batchSize = 100, bool soft = true); - - /// - /// Find all filtered, sorted and paged - /// - /// - /// - /// - /// - /// - /// - /// - /// - Task>> FindAll( - Expression> filter = null, - Func, IOrderedQueryable> orderBy = null, - string includeProperties = null, - int? skip = null, - int? take = null, - bool? ignoreQueryFilters = false, - bool asNoTracking = false - ); - - /// - /// Configure lazy loaded queryable, given provided parameters, to load a list of grouped by a - /// - /// Filter to be used for querying. - /// Properties that should be used for sorting. - /// Filter to be used for grouping by of . - /// Navigation properties that should be included. - /// Number of entities that should be skipped. - /// Number of entities per page. - /// If true, global query filters will be ignored for this query. - /// Ignore change tracking on the result. Set true for read-only operations. - /// - Task>>> FindAll( - Expression> filter = null, - Func, IOrderedQueryable> orderBy = null, - Expression> groupBy = null, - string includeProperties = null, - int? skip = default(int?), - int? take = default(int?), - bool? ignoreQueryFilters = false, - bool asNoTracking = false - ); - - /// - /// Configure lazy loaded queryable, given provided parameters, to load a list of - /// grouped by a and selected by groupBySelector tranformed into - /// ref to: https://docs.microsoft.com/en-us/dotnet/api/system.linq.queryable.groupby?view=netcore-3.1#System_Linq_Queryable_GroupBy__3_System_Linq_IQueryable___0__System_Linq_Expressions_Expression_System_Func___0___1___System_Linq_Expressions_Expression_System_Func___1_System_Collections_Generic_IEnumerable___0____2___ - /// - /// Filter to be used for querying. - /// Properties that should be used for sorting. - /// Filter to be used for grouping by of . - /// Selector to be used on groupBy used to create a result of value from each group. - /// Navigation properties that should be included. - /// Number of entities that should be skipped. - /// Number of entities per page. - /// If true, global query filters will be ignored for this query. - /// Ignore change tracking on the result. Set true for read-only operations. - /// - Task>> FindAll( - Expression> filter = null, - Func, IOrderedQueryable> orderBy = null, - Expression> groupBy = null, - Expression, TResult>> groupBySelector = null, - string includeProperties = null, - int? skip = default(int?), - int? take = default(int?), - bool? ignoreQueryFilters = false, - bool asNoTracking = false - ); - - /// - /// Find all filtered, sorted and paged and converts to an IList - /// - /// - /// - /// - /// - /// - /// - /// - Task>> FindAllCommitted(Expression> filter = null, Func, IOrderedQueryable> orderBy = null, string includeProperties = null, int? skip = null, int? take = null, bool? ignoreQueryFilters = false); - - /// - /// Finds an entity by its Id. - /// - /// The entity identity value. - /// If true, global query filters will be ignored for this query. - /// The entity with the provided identity value. - Task> FindById(long id, bool? ignoreQueryFilters = false); - - /// - /// Finds an entity by its Id that also matches a filter. - /// - /// The entity identity value. - /// Filter to be used for querying. - /// The entity witht he provided identity value and filter condition met. - Task> FindById(long id, Expression> filter); - - /// - /// Finds an entity by its Id. - /// - /// The entity identity value. - /// Navigation properties that should be included. - /// The entity with the provided identity value. - Task> FindById(long id, params Expression>[] includeProperties); - - /// - /// Finds an entity by its Id. - /// - /// The entity identity value. - /// If true, global query filters will be ignored for this query. - /// Navigation properties that should be included. - /// The entity with the provided identity value. - Task> FindById(long id, bool? ignoreQueryFilters = false, params Expression>[] includeProperties); - - /// - /// Finds an entity by its Id. - /// - /// The entity identity value. - /// Navigation properties that should be included. - /// The entity with the provided identity value. - Task> FindById(long id, params string[] includeProperties); - - /// - /// Ability to restore a soft-deleted entity using the entity itself. - /// - /// Entity to be restored - /// - Task> Restore(T o); - - /// - /// Ability to restore a soft-deleted entity using the entity id. - /// - /// Id of entity to be restored - /// - Task> Restore(long id); - - /// - /// Ability to create or update an entity - /// - /// Item to create or update - /// Id of user creating or updating the entity - /// - Task> Update(T item, long? updatedBy = null); - - /// - /// Calls Update one-by-one for each item in the enumerated entities. - /// For large operations, BulkUpdate() is more efficient. - /// - /// - /// - /// True if entities updated without any exceptions. False if an exception was thrown. - Task> Update(IEnumerable entities, long? updatedBy = default(long?)); + Task>> BulkCreateAsync(IEnumerable items, long? createdById = null); - #endregion Methods } } diff --git a/test/Conductors.Tests/RepositoryConductorTests/BulkCreateAsyncShould.cs b/test/Conductors.Tests/RepositoryConductorTests/BulkCreateAsyncShould.cs new file mode 100644 index 0000000..1263ec8 --- /dev/null +++ b/test/Conductors.Tests/RepositoryConductorTests/BulkCreateAsyncShould.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Linq; +using AndcultureCode.CSharp.Core.Interfaces.Conductors; +using AndcultureCode.CSharp.Testing.Extensions; +using AndcultureCode.CSharp.Testing.Tests; +using Moq; +using Shouldly; +using Xunit; +using Xunit.Abstractions; +using System.Linq.Expressions; +using System; +using AndcultureCode.CSharp.Testing.Models.Stubs; +using System.Threading.Tasks; + +namespace AndcultureCode.CSharp.Conductors.Tests.RepositoryConductorTests +{ + public class BulkCreateAsyncShould : ProjectUnitTest + { + #region Setup + + public BulkCreateAsyncShould(ITestOutputHelper output) : base(output) + { + } + + private IRepositoryConductor SetupSut( + IRepositoryMock repositoryMock + ) + { + var repositoryCreateConductor = new RepositoryCreateConductor(repositoryMock.Object); + return new RepositoryConductor( + createConductor: repositoryCreateConductor, + deleteConductor: null, + readConductor: null, + updateConductor: null + ); + } + + #endregion Setup + + [Fact] + public async Task Throw_Argument_Null_Exception_When_Given_Null_Input() + { + // Arrange + var repositoryMock = new IRepositoryMock(); + var respositoryConductor = SetupSut(repositoryMock); + + // Act & Assert + await Assert.ThrowsAsync(() => respositoryConductor.BulkCreateAsync(null)); + } + + [Fact] + public async Task Throw_Argument_Exception_When_Given_Empty_Input() + { + // Arrange + var repositoryMock = new IRepositoryMock(); + var respositoryConductor = SetupSut(repositoryMock); + + // Act & Assert + await Assert.ThrowsAsync(() => respositoryConductor.BulkCreateAsync(new List())); + } + + } +} diff --git a/test/Conductors.Tests/RepositoryConductorTests/IRepositoryMock.cs b/test/Conductors.Tests/RepositoryConductorTests/IRepositoryMock.cs new file mode 100644 index 0000000..a26c13f --- /dev/null +++ b/test/Conductors.Tests/RepositoryConductorTests/IRepositoryMock.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AndcultureCode.CSharp.Core.Interfaces; +using AndcultureCode.CSharp.Core.Interfaces.Conductors; +using AndcultureCode.CSharp.Core.Interfaces.Data; +using AndcultureCode.CSharp.Core.Interfaces.Entity; +using Moq; + +namespace AndcultureCode.CSharp.Conductors.Tests.RepositoryConductorTests +{ + + + internal sealed class IRepositoryMock : Mock> + where T : class, IEntity + { + public IRepositoryMock BulkCreateAsync(IResult> output, Action callback) + { + Setup(x => x.BulkCreateAsync(It.IsAny>(), null)) + .Returns(Task.FromResult(output)); + return this; + } + } +}