From a059d0f8e12080820ef86873a5be6ec179a8e4cb Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 15 Feb 2018 21:20:57 +0530 Subject: [PATCH 01/40] Support Transaction Scope --- .../IUnitOfWork.cs | 9 ++++ .../IUnitOfWorkOfT.cs | 11 +++++ .../UnitOfWork.cs | 49 +++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs index ad81c5d..692e74c 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs @@ -3,6 +3,8 @@ using System; using System.Linq; using System.Threading.Tasks; +using System.Transactions; +using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore { @@ -57,5 +59,12 @@ public interface IUnitOfWork : IDisposable /// The parameters. /// An that contains elements that satisfy the condition specified by raw SQL. IQueryable FromSql(string sql, params object[] parameters) where TEntity : class; + + /// + /// Starts Databaselevel Transaction + /// + /// The IsolationLevel + /// Transaction Context + IDbContextTransaction BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted); } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs index 79ba4ed..d527cfe 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs @@ -1,6 +1,7 @@ // Copyright (c) Arch team. All rights reserved. using System.Threading.Tasks; +using System.Transactions; namespace Microsoft.EntityFrameworkCore { /// @@ -20,5 +21,15 @@ public interface IUnitOfWork : IUnitOfWork where TContext : DbContext /// An optional array. /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); + + /// + /// Saves all changes made in this context to the database with distributed transaction. + /// + /// True if save changes ensure auto record the change history. + /// The IsolationLevel + /// An optional array. + /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. + Task SaveChangesAsync(bool ensureAutoHistory = false, IsolationLevel isolation = IsolationLevel.ReadCommitted, params IUnitOfWork[] unitOfWorks); + } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs index c09641e..fc9ad66 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs @@ -105,6 +105,16 @@ public IRepository GetRepository() where TEntity : class /// An that contains elements that satisfy the condition specified by raw SQL. public IQueryable FromSql(string sql, params object[] parameters) where TEntity : class => _context.Set().FromSql(sql, parameters); + /// + /// Starts Databaselevel Transaction + /// + /// The IsolationLevel + /// Transaction Context + public IDbContextTransaction BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted) + { + return _context.Database.BeginTransaction(isolation); + } + /// /// Saves all changes made in this context to the database. /// @@ -172,6 +182,45 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false, params I } } + /// + /// Saves all changes made in this context to the database with distributed transaction. + /// + /// True if save changes ensure auto record the change history. + /// The IsolationLevel + /// An optional array. + /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. + public async Task SaveChangesAsync(bool ensureAutoHistory = false, IsolationLevel isolation = IsolationLevel.ReadCommitted, params IUnitOfWork[] unitOfWorks) + { + // TransactionScope will be included in .NET Core v2.0 + using (var transaction = _context.Database.BeginTransaction(isolation)) + { + try + { + var count = 0; + foreach (var unitOfWork in unitOfWorks) + { + var uow = unitOfWork as UnitOfWork; + uow.DbContext.Database.UseTransaction(transaction.GetDbTransaction()); + count += await uow.SaveChangesAsync(ensureAutoHistory); + } + + count += await SaveChangesAsync(ensureAutoHistory); + + transaction.Commit(); + + return count; + } + catch (Exception ex) + { + + transaction.Rollback(); + + throw ex; + } + } + } + + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// From 550eb386e1133e40476d0c689610cb97361d22bc Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Fri, 16 Feb 2018 10:59:24 +0530 Subject: [PATCH 02/40] Changed using System.Data; --- src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs | 2 +- src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs index 692e74c..745eff8 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs @@ -3,7 +3,7 @@ using System; using System.Linq; using System.Threading.Tasks; -using System.Transactions; +using System.Data; using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs index d527cfe..c263b1f 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs @@ -1,7 +1,7 @@ // Copyright (c) Arch team. All rights reserved. using System.Threading.Tasks; -using System.Transactions; +using System.Data; namespace Microsoft.EntityFrameworkCore { /// From daadfe783e7d67d2d83b811e1b9d4e37b3ff9ef3 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Fri, 23 Feb 2018 18:57:26 +0530 Subject: [PATCH 03/40] Resolved Conflict After Trackgrap API Commited. --- .../IUnitOfWork.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs index 745eff8..b6db3c7 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.ChangeTracking; using System.Data; using Microsoft.EntityFrameworkCore.Storage; @@ -60,6 +61,13 @@ public interface IUnitOfWork : IDisposable /// An that contains elements that satisfy the condition specified by raw SQL. IQueryable FromSql(string sql, params object[] parameters) where TEntity : class; + /// + /// Uses TrakGrap Api to attach disconnected entities + /// + /// Root entity + /// Delegate to convert Object's State properities to Entities entry state. + + void TrackGraph(object rootEntity, Action callback); /// /// Starts Databaselevel Transaction /// @@ -67,4 +75,4 @@ public interface IUnitOfWork : IDisposable /// Transaction Context IDbContextTransaction BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted); } -} +} \ No newline at end of file From e4ef99d578bf1d21ef252c7aea57f445d0d21b8c Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Fri, 23 Feb 2018 19:02:30 +0530 Subject: [PATCH 04/40] Corrected Alignment --- src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs index b6db3c7..5489399 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs @@ -66,8 +66,8 @@ public interface IUnitOfWork : IDisposable /// /// Root entity /// Delegate to convert Object's State properities to Entities entry state. - void TrackGraph(object rootEntity, Action callback); + /// /// Starts Databaselevel Transaction /// From e8bb14b38f55afe63b75cebb4e3f71907b51d065 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 1 Mar 2018 16:43:17 +0530 Subject: [PATCH 05/40] Removed SaveChangesAsync Dublicate method. --- .../IUnitOfWorkOfT.cs | 13 +----- .../UnitOfWork.cs | 41 +------------------ 2 files changed, 4 insertions(+), 50 deletions(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs index c263b1f..7e0beba 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs @@ -17,19 +17,10 @@ public interface IUnitOfWork : IUnitOfWork where TContext : DbContext /// /// Saves all changes made in this context to the database with distributed transaction. /// - /// True if save changes ensure auto record the change history. - /// An optional array. - /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. - Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); - - /// - /// Saves all changes made in this context to the database with distributed transaction. - /// - /// True if save changes ensure auto record the change history. /// The IsolationLevel + /// True if save changes ensure auto record the change history. /// An optional array. /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. - Task SaveChangesAsync(bool ensureAutoHistory = false, IsolationLevel isolation = IsolationLevel.ReadCommitted, params IUnitOfWork[] unitOfWorks); - + Task SaveChangesAsync(IsolationLevel isolation = IsolationLevel.ReadCommitted, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs index 6819b3b..ca7ecae 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs @@ -149,48 +149,11 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false) /// /// Saves all changes made in this context to the database with distributed transaction. /// - /// True if save changes ensure auto record the change history. - /// An optional array. - /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. - public async Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) - { - // TransactionScope will be included in .NET Core v2.0 - using (var transaction = _context.Database.BeginTransaction()) - { - try - { - var count = 0; - foreach (var unitOfWork in unitOfWorks) - { - var uow = unitOfWork as UnitOfWork; - uow.DbContext.Database.UseTransaction(transaction.GetDbTransaction()); - count += await uow.SaveChangesAsync(ensureAutoHistory); - } - - count += await SaveChangesAsync(ensureAutoHistory); - - transaction.Commit(); - - return count; - } - catch (Exception ex) - { - - transaction.Rollback(); - - throw ex; - } - } - } - - /// - /// Saves all changes made in this context to the database with distributed transaction. - /// - /// True if save changes ensure auto record the change history. /// The IsolationLevel + /// True if save changes ensure auto record the change history. /// An optional array. /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. - public async Task SaveChangesAsync(bool ensureAutoHistory = false, IsolationLevel isolation = IsolationLevel.ReadCommitted, params IUnitOfWork[] unitOfWorks) + public async Task SaveChangesAsync(IsolationLevel isolation = IsolationLevel.ReadCommitted, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) { // TransactionScope will be included in .NET Core v2.0 using (var transaction = _context.Database.BeginTransaction(isolation)) From 6a6329c55bb66ead4ec6d43ce5387c9ad4ef0832 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 1 Mar 2018 16:53:39 +0530 Subject: [PATCH 06/40] Update IUnitOfWork.cs --- src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs index a1b27fb..a702a1b 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs @@ -74,6 +74,5 @@ public interface IUnitOfWork : IDisposable /// The IsolationLevel /// Transaction Context IDbContextTransaction BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted); - } } From 28df4421fdc36a7564ae082f06f4377709fa8d28 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Mon, 12 Mar 2018 20:41:10 +0530 Subject: [PATCH 07/40] Raw Sql Returns DataTable Introduced Execute Sql Which will result Datatable, Amuch needed functionality for Reporting --- .../IUnitOfWork.cs | 9 +++++++ ...soft.EntityFrameworkCore.UnitOfWork.csproj | 5 ++++ .../UnitOfWork.cs | 25 +++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs index a702a1b..2c5d7df 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs @@ -52,6 +52,15 @@ public interface IUnitOfWork : IDisposable /// The number of state entities written to database. int ExecuteSqlCommand(string sql, params object[] parameters); + + /// + /// Executes the specified raw SQL command. + /// + /// The raw SQL. + /// The parameters. + /// The number of state entities written to database. + DataTable ExecuteDtSqlCommand(string sql, params object[] parameters); + /// /// Uses raw SQL queries to fetch the specified data. /// diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index 27e2dfb..19d0215 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -19,4 +19,9 @@ + + + C:\Program Files\dotnet\sdk\NuGetFallbackFolder\system.data.sqlclient\4.4.0\ref\netstandard2.0\System.Data.SqlClient.dll + + diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs index ca7ecae..5a60d73 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.SqlClient; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -97,6 +98,30 @@ public IRepository GetRepository() where TEntity : class /// The number of state entities written to database. public int ExecuteSqlCommand(string sql, params object[] parameters) => _context.Database.ExecuteSqlCommand(sql, parameters); + + /// + /// Executes the specified raw SQL command. + /// + /// The raw SQL. + /// The parameters. + /// The DataTable. + public DataTable ExecuteDtSqlCommand(string sql, params object[] parameters) + { + SqlConnection conn = (SqlConnection) _context.Database.GetDbConnection(); + SqlCommand cmd = new SqlCommand(sql, conn); + cmd.CommandTimeout = 0; + conn.Open(); + // create data adapter + SqlDataAdapter da = new SqlDataAdapter(cmd); + // this will query your database and return the result to your datatable + DataTable dataTable = new DataTable(); + da.Fill(dataTable); + da.Dispose(); + conn.Close(); + return dataTable; + } + + /// /// Uses raw SQL queries to fetch the specified data. /// From 7334bef6e982393c83d770f87da486b5600bf72f Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 2 Aug 2018 17:19:10 +0530 Subject: [PATCH 08/40] SQL Dataset Corrected --- .../Microsoft.EntityFrameworkCore.UnitOfWork.csproj | 2 +- .../UnitOfWork.cs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index 19d0215..b8f9ed3 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs index 5a60d73..9d2a494 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs @@ -110,6 +110,15 @@ public DataTable ExecuteDtSqlCommand(string sql, params object[] parameters) SqlConnection conn = (SqlConnection) _context.Database.GetDbConnection(); SqlCommand cmd = new SqlCommand(sql, conn); cmd.CommandTimeout = 0; + + if(parameters != null && parameters.Count() > 0) + { + foreach(object obj in parameters) + { + cmd.Parameters.Add(obj); + } + } + conn.Open(); // create data adapter SqlDataAdapter da = new SqlDataAdapter(cmd); From b6943d360ffa7bf6766a715d3ed7d7c085d355b7 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 16 Aug 2018 12:30:04 +0530 Subject: [PATCH 09/40] Get List with predicate --- .../IRepository.cs | 33 +++++++ .../Repository.cs | 88 +++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs index ba257c3..d26a8f6 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs @@ -295,5 +295,38 @@ Task GetFirstOrDefaultAsync(Expression> predicate = /// /// The entities. void Delete(IEnumerable entities); + + + /// + /// Gets the based on a predicate, orderby delegate. This method default no-tracking query. + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// True to disable changing tracking; otherwise, false. Default to true. + /// + /// A to observe while waiting for the task to complete. + /// + /// An that contains elements that satisfy the condition specified by . + /// This method default no-tracking query. + Task> GetListAsync(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// True to disable changing tracking; otherwise, false. Default to true. + /// An that contains elements that satisfy the condition specified by . + /// This method default no-tracking query. + List GetList(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true); } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index 15797f4..23dbbc8 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -570,5 +570,93 @@ public void Delete(object id) /// /// The entities. public void Delete(IEnumerable entities) => _dbSet.RemoveRange(entities); + + + + /// + /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// True to disable changing tracking; otherwise, false. Default to true. + /// An that contains elements that satisfy the condition specified by . + /// This method default no-tracking query. + public List GetList(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true) + { + IQueryable query = _dbSet; + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(predicate); + } + + if (orderBy != null) + { + return orderBy(query).ToList(); + } + else + { + return query.ToList(); + } + } + + /// + /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// True to disable changing tracking; otherwise, false. Default to true. + /// + /// A to observe while waiting for the task to complete. + /// + /// An that contains elements that satisfy the condition specified by . + /// This method default no-tracking query. + public Task> GetListAsync(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)) + { + IQueryable query = _dbSet; + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(predicate); + } + + if (orderBy != null) + { + return orderBy(query).ToListAsync(cancellationToken); + } + else + { + return query.ToListAsync(cancellationToken); + } + } + + } } From e1558bd02b841fdb613aca8366aec63fb1d070c5 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Mon, 17 Sep 2018 20:09:26 +0530 Subject: [PATCH 10/40] Previous Next Functionality Partially Implemented --- .../IRepository.cs | 32 ++ .../Repository.cs | 281 ++++++++++++++++++ 2 files changed, 313 insertions(+) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs index d26a8f6..84c14e3 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs @@ -328,5 +328,37 @@ List GetList(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true); + + + /// + /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + TEntity GetNextById(params object[] keyValues); + + + /// + /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + Task GetNextByIdAsync(params object[] keyValues); + + + /// + /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + TEntity GetPreviousById(params object[] keyValues); + + /// + /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + Task GetPreviousByIdAsync(params object[] keyValues); + } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index 23dbbc8..ce0db36 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -658,5 +658,286 @@ public Task> GetListAsync(Expression> predicat } + /// + /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + public TEntity GetNextById(params object[] keyValues) + { + return _dbSet.Find(IncrementKey(keyValues)); + } + + + + /// + /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + public Task GetNextByIdAsync(params object[] keyValues) + { + return _dbSet.FindAsync(IncrementKey(keyValues)); + /* + ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext; + ObjectSet set = objectContext.CreateObjectSet(); + IEnumerable keyNames = set.EntitySet.ElementType + .KeyMembers + .Select(k => k.Name); + * / + TEntity res = _dbSet.Find(IncrementKey(keyValues)); + //if (res != null) + return Task.Factory.StartNew(() => res); + + /* + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderBy(keyColums[0],"asc"); + List lstObjs = GetList(null, ordByExp); + + TEntity resExact = _dbSet.Find(cp); + + int curobj = lstObjs.IndexOf(resExact); + int nxt = curobj + 1; + return Task.Factory.StartNew(() => lstObjs.ElementAtOrDefault(nxt)); + */ + } + + + /// + /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + public TEntity GetPreviousById(params object[] keyValues) + { + return _dbSet.Find(DecrementKey(keyValues)); + } + + + /// + /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + public Task GetPreviousByIdAsync(params object[] keyValues) + { + return _dbSet.FindAsync(DecrementKey(keyValues)); + } + + #region Next, Previous Support Methos + + private object[] IncrementKey(object[] id) + { + int idx = id.Length -1; + int val = (int)id[idx]; + id[idx] = ++val; + return id; + } + + private object[] DecrementKey(object[] id) + { + int idx = id.Length - 1; + int val = (int)id[idx]; + id[idx] = --val; + return id; + } + + + + private static MemberExpression GetMemberExpression(Expression param, string propertyName) + { + if (propertyName.Contains(".")) + { + int index = propertyName.IndexOf("."); + var subParam = Expression.Property(param, propertyName.Substring(0, index)); + return GetMemberExpression(subParam, propertyName.Substring(index + 1)); + } + + return Expression.Property(param, propertyName); + } + + + public static Func, IOrderedQueryable> GetOrderBy(string orderColumn, string orderType) + { + Type typeQueryable = typeof(IQueryable); + ParameterExpression argQueryable = Expression.Parameter(typeQueryable, "p"); + var outerExpression = Expression.Lambda(argQueryable, argQueryable); + string[] props = orderColumn.Split('.'); + IQueryable query = new List().AsQueryable(); + Type type = typeof(T); + ParameterExpression arg = Expression.Parameter(type, "x"); + + Expression expr = arg; + foreach (string prop in props) + { + PropertyInfo pi = type.GetProperty(prop, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + expr = Expression.Property(expr, pi); + type = pi.PropertyType; + } + LambdaExpression lambda = Expression.Lambda(expr, arg); + string methodName = orderType == "asc" ? "OrderBy" : "OrderByDescending"; + + MethodCallExpression resultExp = + Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(T), type }, outerExpression.Body, Expression.Quote(lambda)); + var finalLambda = Expression.Lambda(resultExp, argQueryable); + return (Func, IOrderedQueryable>)finalLambda.Compile(); + } + + + public static Expression, IOrderedQueryable>> GetOrderByExpression(IEnumerable lstSelection, bool isDescending = false) + { + + bool isThenBy = false; + ParameterExpression inParameter = Expression.Parameter(typeof(T), "s"); + foreach (string propName in lstSelection) + { + MemberExpression prop = GetMemberExpression(inParameter, propName); + var propertyInfo = (PropertyInfo)prop.Member; + Type pType = propertyInfo.PropertyType; + if (isThenBy) + { + var thenByMethod = typeof(Queryable).GetMethods() + .First(method => method.Name == "ThenBy" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(pType); + + var ThenByDescending = typeof(Queryable).GetMethods() + .First(method => method.Name == "ThenByDescending" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(pType); + if (isDescending) + { + return query => (IOrderedQueryable) + ThenByDescending.Invoke(null, new object[] { query, prop }); + } + else + { + return query => (IOrderedQueryable) + thenByMethod.Invoke(null, new object[] { query, prop }); + } + } + else + { + isThenBy = true; + var orderByMethod = typeof(Queryable).GetMethods() + .First(method => method.Name == "OrderBy" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(pType); + var orderByDescMethod = typeof(Queryable).GetMethods() + .First(method => method.Name == "OrderByDescending" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(pType); + + if (isDescending) { + return query => (IOrderedQueryable) + orderByDescMethod.Invoke(null, new object[] { query, prop }); + } + else { + return query => (IOrderedQueryable) + orderByMethod.Invoke(null, new object[] { query, prop }); + } + } + } + + return null; + } + + #endregion + + /* + + #region Logic33 + + + public static IEnumerable BuildOrderBys( + this IEnumerable source, + IEnumerable properties) + { + if (properties == null || properties.Count() == 0) return source; + + var typeOfT = typeof(T); + + Type t = typeOfT; + + IOrderedEnumerable result = null; + var thenBy = false; + + foreach (var item in properties) + { + var oExpr = Expression.Parameter(typeOfT, "o"); + + MemberExpression prop = GetMemberExpression(oExpr, item); + var propertyInfo = (PropertyInfo)prop.Member; + var propertyType = propertyInfo.PropertyType; + var isAscending = true; + + if (thenBy) + { + var prevExpr = Expression.Parameter(typeof(IOrderedEnumerable), "prevExpr"); + var expr1 = Expression.Lambda, IOrderedEnumerable>>( + Expression.Call( + (isAscending ? thenByMethod : thenByDescendingMethod).MakeGenericMethod(typeOfT, propertyType), + prevExpr, + Expression.Lambda( + typeof(Func<,>).MakeGenericType(typeOfT, propertyType), + Expression.MakeMemberAccess(oExpr, propertyInfo), + oExpr) + ), + prevExpr) + .Compile(); + result = expr1(result); + } + else + { + var prevExpr = Expression.Parameter(typeof(IEnumerable), "prevExpr"); + var expr1 = Expression.Lambda, IOrderedEnumerable>>( + Expression.Call( + (isAscending ? orderByMethod : orderByDescendingMethod).MakeGenericMethod(typeOfT, propertyType), + prevExpr, + Expression.Lambda( + typeof(Func<,>).MakeGenericType(typeOfT, propertyType), + Expression.MakeMemberAccess(oExpr, propertyInfo), + oExpr) + ), + prevExpr) + .Compile(); + result = expr1(source); + thenBy = true; + } + } + return result; + } + + + private static MethodInfo orderByMethod = + MethodOf(() => Enumerable.OrderBy(default(IEnumerable), default(Func))) + .GetGenericMethodDefinition(); + + private static MethodInfo orderByDescendingMethod = + MethodOf(() => Enumerable.OrderByDescending(default(IEnumerable), default(Func))) + .GetGenericMethodDefinition(); + + private static MethodInfo thenByMethod = + MethodOf(() => Enumerable.ThenBy(default(IOrderedEnumerable), default(Func))) + .GetGenericMethodDefinition(); + + private static MethodInfo thenByDescendingMethod = + MethodOf(() => Enumerable.ThenByDescending(default(IOrderedEnumerable), default(Func))) + .GetGenericMethodDefinition(); + + public static MethodInfo MethodOf(Expression> method) + { + MethodCallExpression mce = (MethodCallExpression)method.Body; + MethodInfo mi = mce.Method; + return mi; + } + + + #endregion + + */ } } From 654bc6dd96b1bd57d72e48cd925f61ec7bff5fdb Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 27 Sep 2018 12:37:23 +0530 Subject: [PATCH 11/40] First Next Previous Last Functionality First Next Previous Last Functionality --- .../IRepository.cs | 26 + .../Repository.cs | 460 ++++++++++++++++-- 2 files changed, 436 insertions(+), 50 deletions(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs index 84c14e3..c712d5e 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs @@ -360,5 +360,31 @@ List GetList(Expression> predicate = null, /// The found entity or null. Task GetPreviousByIdAsync(params object[] keyValues); + /// + /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + TEntity GetFirst(); + + + /// + /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + Task GetFirstAsync(); + + /// + /// Finds the Last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + TEntity GetLast(); + + + /// + /// Finds the last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + Task GetLastAsync(); + } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index ce0db36..941ebd8 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -665,10 +665,45 @@ public Task> GetListAsync(Expression> predicat /// The found entity or null. public TEntity GetNextById(params object[] keyValues) { - return _dbSet.Find(IncrementKey(keyValues)); - } + TEntity res = _dbSet.Find(IncrementKey(keyValues)); + if (res != null) + { + return res; + } + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + //var ordByExp = GetOrderBy(keyColums[0],"asc"); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + if (lstObjs != null && lstObjs.Count > 0) + { + //Form Where Condition + Expression> expr = GetWhereConditionExpression(key, DecrementKey(keyValues)); + Func func = expr.Compile(); + Predicate pred = func.Invoke; + TEntity currObj = lstObjs.Find(pred); + + int curobj = lstObjs.IndexOf(currObj); + if (curobj != -1) + { + int nxt = curobj + 1; + return lstObjs.ElementAtOrDefault(nxt); + } + else + { + return null; + } + } + else + { + return null; + } + } /// /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. @@ -677,35 +712,44 @@ public TEntity GetNextById(params object[] keyValues) /// The found entity or null. public Task GetNextByIdAsync(params object[] keyValues) { - return _dbSet.FindAsync(IncrementKey(keyValues)); - /* - ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext; - ObjectSet set = objectContext.CreateObjectSet(); - IEnumerable keyNames = set.EntitySet.ElementType - .KeyMembers - .Select(k => k.Name); - * / TEntity res = _dbSet.Find(IncrementKey(keyValues)); - //if (res != null) + if (res != null) + { return Task.Factory.StartNew(() => res); + } - /* //No Result Found. So Order the Entity with key column and select next Entity IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); - var ordByExp = GetOrderBy(keyColums[0],"asc"); - List lstObjs = GetList(null, ordByExp); + //var ordByExp = GetOrderBy(keyColums[0],"asc"); + var ordByExp = GetOrderByExpression(keyColums); - TEntity resExact = _dbSet.Find(cp); + List lstObjs = GetList(null, ordByExp.Compile()); - int curobj = lstObjs.IndexOf(resExact); - int nxt = curobj + 1; - return Task.Factory.StartNew(() => lstObjs.ElementAtOrDefault(nxt)); - */ + if (lstObjs != null && lstObjs.Count > 0) + { + //Form Where Condition + Expression> expr = GetWhereConditionExpression(key, DecrementKey(keyValues)); + Func func = expr.Compile(); + Predicate pred = func.Invoke; + TEntity currObj = lstObjs.Find(pred); + + int curobj = lstObjs.IndexOf(currObj); + if(curobj != -1) + { + int nxt = curobj + 1; + return Task.Factory.StartNew(() => lstObjs.ElementAtOrDefault(nxt)); + }else + { + return null; + } + }else + { + return null; + } } - /// /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// @@ -713,9 +757,45 @@ public Task GetNextByIdAsync(params object[] keyValues) /// The found entity or null. public TEntity GetPreviousById(params object[] keyValues) { - return _dbSet.Find(DecrementKey(keyValues)); - } + TEntity res = _dbSet.Find(DecrementKey(keyValues)); + if (res != null) + { + return res; + } + + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + //var ordByExp = GetOrderBy(keyColums[0],"asc"); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + if (lstObjs != null && lstObjs.Count > 0) + { + //Form Where Condition + Expression> expr = GetWhereConditionExpression(key, IncrementKey(keyValues)); + Func func = expr.Compile(); + Predicate pred = func.Invoke; + TEntity currObj = lstObjs.Find(pred); + + int curobj = lstObjs.IndexOf(currObj); + if (curobj != -1) + { + int prev = curobj - 1; + return lstObjs.ElementAtOrDefault(prev); + } + else + { + return null; + } + } + else + { + return null; + } + } /// /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. @@ -724,7 +804,140 @@ public TEntity GetPreviousById(params object[] keyValues) /// The found entity or null. public Task GetPreviousByIdAsync(params object[] keyValues) { - return _dbSet.FindAsync(DecrementKey(keyValues)); + TEntity res = _dbSet.Find(DecrementKey(keyValues)); + if (res != null) + { + return Task.Factory.StartNew(() => res); + } + + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + //var ordByExp = GetOrderBy(keyColums[0],"asc"); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + //Form Where Condition + Expression> expr = GetWhereConditionExpression(key, IncrementKey(keyValues)); + Func func = expr.Compile(); + Predicate pred = func.Invoke; + TEntity currObj = lstObjs.Find(pred); + + int curobj = lstObjs.IndexOf(currObj); + if (curobj != -1) + { + int prev = curobj - 1; + return Task.Factory.StartNew(() => lstObjs.ElementAtOrDefault(prev)); + } + else + { + return null; + } + } + else + { + return null; + } + } + + /// + /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + public TEntity GetFirst() + { + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + return lstObjs.FirstOrDefault(); + } + else + { + return null; + } + } + + /// + /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + public Task GetFirstAsync() + { + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + return Task.Factory.StartNew(() => lstObjs.FirstOrDefault()); + } + else + { + return null; + } + } + + /// + /// Finds the Last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + public TEntity GetLast() + { + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderByExpression(keyColums,true); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + return lstObjs.FirstOrDefault(); + } + else + { + return null; + } + } + + /// + /// Finds the Last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + public Task GetLastAsync() + { + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderByExpression(keyColums,true); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + return Task.Factory.StartNew(() => lstObjs.FirstOrDefault()); + } + else + { + return null; + } } #region Next, Previous Support Methos @@ -745,8 +958,6 @@ private object[] DecrementKey(object[] id) return id; } - - private static MemberExpression GetMemberExpression(Expression param, string propertyName) { if (propertyName.Contains(".")) @@ -755,11 +966,9 @@ private static MemberExpression GetMemberExpression(Expression param, string pro var subParam = Expression.Property(param, propertyName.Substring(0, index)); return GetMemberExpression(subParam, propertyName.Substring(index + 1)); } - return Expression.Property(param, propertyName); } - public static Func, IOrderedQueryable> GetOrderBy(string orderColumn, string orderType) { Type typeQueryable = typeof(IQueryable); @@ -786,72 +995,223 @@ public static Func, IOrderedQueryable> GetOrderBy(string ord return (Func, IOrderedQueryable>)finalLambda.Compile(); } - public static Expression, IOrderedQueryable>> GetOrderByExpression(IEnumerable lstSelection, bool isDescending = false) { - bool isThenBy = false; ParameterExpression inParameter = Expression.Parameter(typeof(T), "s"); foreach (string propName in lstSelection) { - MemberExpression prop = GetMemberExpression(inParameter, propName); + MemberExpression prop = GetMemberExpression(inParameter, propName); //s.mfrId var propertyInfo = (PropertyInfo)prop.Member; + var lambda = Expression.Lambda(prop, inParameter); // s => s.mfrId Type pType = propertyInfo.PropertyType; - if (isThenBy) + Type[] argumentTypes = new[] { typeof(T),pType }; + if (isThenBy) { var thenByMethod = typeof(Queryable).GetMethods() .First(method => method.Name == "ThenBy" && method.GetParameters().Count() == 2) - .MakeGenericMethod(pType); + .MakeGenericMethod(argumentTypes); var ThenByDescending = typeof(Queryable).GetMethods() .First(method => method.Name == "ThenByDescending" && method.GetParameters().Count() == 2) - .MakeGenericMethod(pType); + .MakeGenericMethod(argumentTypes); if (isDescending) { return query => (IOrderedQueryable) - ThenByDescending.Invoke(null, new object[] { query, prop }); + ThenByDescending.Invoke(null, new object[] { query, lambda }); } else { return query => (IOrderedQueryable) - thenByMethod.Invoke(null, new object[] { query, prop }); + thenByMethod.Invoke(null, new object[] { query, lambda }); } } else { isThenBy = true; - var orderByMethod = typeof(Queryable).GetMethods() - .First(method => method.Name == "OrderBy" - && method.GetParameters().Count() == 2) - .MakeGenericMethod(pType); - var orderByDescMethod = typeof(Queryable).GetMethods() - .First(method => method.Name == "OrderByDescending" - && method.GetParameters().Count() == 2) - .MakeGenericMethod(pType); + var orderByMethod = typeof(Queryable).GetMethods() + .First(method => method.Name == "OrderBy" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(argumentTypes); + + var orderByDescMethod = typeof(Queryable).GetMethods() + .First(method => method.Name == "OrderByDescending" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(argumentTypes); if (isDescending) { return query => (IOrderedQueryable) - orderByDescMethod.Invoke(null, new object[] { query, prop }); + orderByDescMethod.Invoke(null, new object[] { query, lambda }); } else { return query => (IOrderedQueryable) - orderByMethod.Invoke(null, new object[] { query, prop }); + orderByMethod.Invoke(null, new object[] { query, lambda }); } } } - return null; } - - #endregion - /* + static MethodInfo LikeMethod = typeof(DbFunctionsExtensions).GetMethod("Like", new Type[] { typeof(DbFunctions), typeof(string), typeof(string) }); - #region Logic33 + static MethodInfo StartsWithMethod = typeof(String).GetMethod("StartsWith", new Type[] { typeof(String) }); + + static MethodInfo ContainsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) }); + + static MethodInfo EndsWithMethod = typeof(String).GetMethod("EndsWith", new Type[] { typeof(String) }); + + public static Expression> GetWhereConditionExpression(IKey key, params object[] keyValues) + { + if (key == null || keyValues == null) return null; + //Create the expression parameters + ParameterExpression inParameter = Expression.Parameter(typeof(T)); + + Expression whereExp = null; + int idx = 0; + foreach (IProperty p in key.Properties) + { + string dataPropertyName = p.Name; + Type dataType = p.PropertyInfo.PropertyType; + Object propVale = keyValues[idx++]; + if (propVale != null) + { + Expression LHS = GetMemberExpression(inParameter, dataPropertyName); //Expression.Property(inParameter, dataPropertyName); + Expression RHS = Expression.Convert(Expression.Constant(propVale), dataType); + + if (dataType == typeof(DateTime)) + { + //MethodInfo truncTimeMethod = typeof(EF).GetProperty("Functions").GetType().GetMethod("TruncateTime", new Type[] { typeof(DateTime?) }); + //MethodInfo conMethod = typeof(System.Data.Entity.DbFunctions).GetMethod("TruncateTime", new Type[] { typeof(DateTime?) }); + //LHS = Expression.Call(conMethod, Expression.Convert(Expression.Property(inParameter, dataPropertyName), typeof(DateTime?))); + RHS = Expression.Convert(Expression.Constant(((DateTime)propVale).Date), typeof(DateTime?)); + } + string conOperator = "eq"; + Expression expr = null; + switch (conOperator) + { + case "<": + case "lt": + expr = Expression.LessThan(LHS, RHS); + break; + + case ">": + case "gt": + expr = Expression.GreaterThan(LHS, RHS); + break; + + case "<=": + case "le": + expr = Expression.LessThanOrEqual(LHS, RHS); + break; + + case ">=": + case "ge": + expr = Expression.GreaterThanOrEqual(LHS, RHS); + break; + + case "!=": + case "<>": + case "ne": + expr = Expression.NotEqual(LHS, RHS); + break; + + case "IsNull": + expr = Expression.Equal(LHS, Expression.Constant(null, dataType)); + break; + + case "IsNotNull": + expr = Expression.NotEqual(LHS, Expression.Constant(null, dataType)); + break; + + case "Like": + if (LHS.Type != typeof(string)) + { + LHS = Expression.Convert(Expression.Convert(LHS, typeof(object)), typeof(string)); + } + RHS = Expression.Convert(Expression.Constant(propVale.ToString().Replace(" ", "%") + "%"), dataType); + expr = Expression.Call(LikeMethod, Expression.Convert(Expression.Constant(EF.Functions), typeof(DbFunctions)), LHS, RHS); + break; + + case "Contains": + if (LHS.Type != typeof(string)) + { + LHS = Expression.Convert(Expression.Convert(LHS, typeof(object)), typeof(string)); + RHS = Expression.Convert(Expression.Constant("%" + propVale + "%"), dataType); + expr = Expression.Call(LikeMethod, Expression.Convert(Expression.Constant(EF.Functions), typeof(DbFunctions)), LHS, RHS); + } + else + { + expr = Expression.Call(LHS, ContainsMethod, Expression.Constant(propVale.ToString())); + } + break; + + case "StartsWith": + if (LHS.Type != typeof(string)) + { + LHS = Expression.Convert(Expression.Convert(LHS, typeof(object)), typeof(string)); + RHS = Expression.Convert(Expression.Constant(propVale + "%"), dataType); + expr = Expression.Call(LikeMethod, Expression.Convert(Expression.Constant(EF.Functions), typeof(DbFunctions)), LHS, RHS); + } + else + { + expr = Expression.Call(LHS, StartsWithMethod, Expression.Constant(propVale.ToString())); + } + break; + + case "EndsWith": + if (LHS.Type != typeof(string)) + { + LHS = Expression.Convert(Expression.Convert(LHS, typeof(object)), typeof(string)); + RHS = Expression.Convert(Expression.Constant("%" + propVale), dataType); + expr = Expression.Call(LikeMethod, Expression.Convert(Expression.Constant(EF.Functions), typeof(DbFunctions)), LHS, RHS); + } + else + { + expr = Expression.Call(LHS, EndsWithMethod, Expression.Constant(propVale.ToString())); + } + break; + + case "=": + case "==": + case "eq": + default: + expr = Expression.Equal(LHS, RHS); + break; + } + + if (whereExp == null) + { + whereExp = expr; + } + else + { + String condi = "AND"; + if (condi != null && condi.Equals("AND", StringComparison.OrdinalIgnoreCase)) + { + whereExp = Expression.AndAlso(whereExp, expr); + } + else + { + whereExp = Expression.OrElse(whereExp, expr); + } + } + } + + } + if (whereExp == null) + { + whereExp = Expression.Constant(true); + } + return Expression.Lambda>(whereExp, inParameter); + } + #endregion + + /* + #region Logic33 public static IEnumerable BuildOrderBys( this IEnumerable source, IEnumerable properties) From f6f5a294be64a0e39ee7dcd275021fa6cbfbb07c Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 4 Oct 2018 11:50:23 +0530 Subject: [PATCH 12/40] Pre Next Bug Fixed --- src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index 941ebd8..f30f271 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -742,11 +742,11 @@ public Task GetNextByIdAsync(params object[] keyValues) return Task.Factory.StartNew(() => lstObjs.ElementAtOrDefault(nxt)); }else { - return null; + return Task.FromResult(null); } }else { - return null; + return Task.FromResult(null); } } From d54c30f0ec2142b9083a522a6258d40426075af3 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 4 Oct 2018 12:18:26 +0530 Subject: [PATCH 13/40] Prev Next Bug Fixed --- .../Repository.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index f30f271..6bca1c2 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -835,12 +835,12 @@ public Task GetPreviousByIdAsync(params object[] keyValues) } else { - return null; + return Task.FromResult(null); } } else { - return null; + return Task.FromResult(null); } } @@ -888,7 +888,7 @@ public Task GetFirstAsync() } else { - return null; + return Task.FromResult(null); } } @@ -936,7 +936,7 @@ public Task GetLastAsync() } else { - return null; + return Task.FromResult(null); } } From 8100a95af482e8c09e6f25dd8ef12a8577206273 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Mon, 22 Oct 2018 17:37:15 +0530 Subject: [PATCH 14/40] Dot Net Core 2.2 Updated --- src/Host/Host.csproj | 28 +++++++++---------- ...soft.EntityFrameworkCore.UnitOfWork.csproj | 4 +-- ...ntityFrameworkCore.UnitOfWork.Tests.csproj | 13 +++++---- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/Host/Host.csproj b/src/Host/Host.csproj index 2c6ebe3..d7c17eb 100644 --- a/src/Host/Host.csproj +++ b/src/Host/Host.csproj @@ -1,23 +1,23 @@  - netcoreapp2.0 + netcoreapp2.2 true Exe - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index b8f9ed3..5f41f34 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -16,8 +16,8 @@ https://github.com/arch/UnitOfWork.git - - + + diff --git a/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj b/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj index 48fc589..77c2784 100644 --- a/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj +++ b/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj @@ -1,14 +1,17 @@  - netcoreapp2.0 + netcoreapp2.2 - - - - + + + + + all + runtime; build; native; contentfiles; analyzers + From 67da6f805415a5589c0866ad69630b4da248a2a5 Mon Sep 17 00:00:00 2001 From: Ramya Date: Wed, 19 Dec 2018 13:44:48 +0530 Subject: [PATCH 15/40] Update Microsoft.EntityFrameworkCore.UnitOfWork.csproj DotNetCore Version Updated --- .../Microsoft.EntityFrameworkCore.UnitOfWork.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index 5f41f34..74d5855 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -17,7 +17,7 @@ - + From d22d18bf39aebdd5af3714ac174389e428a52f5a Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Tue, 25 Dec 2018 10:20:54 +0530 Subject: [PATCH 16/40] Update Microsoft.EntityFrameworkCore.UnitOfWork.csproj SQL Client Added --- .../Microsoft.EntityFrameworkCore.UnitOfWork.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index 74d5855..53c0bbe 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -18,6 +18,7 @@ + From 5388de4aa70400dee87c1675d5c65f7ba81b3d61 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Wed, 13 Mar 2019 21:00:11 +0530 Subject: [PATCH 17/40] Modified to New Version --- README.md | 3 +- src/Host/Controllers/ValuesController.cs | 2 +- src/Host/Host.csproj | 26 +- src/Host/Models/CustomBlogRepository.cs | 12 + src/Host/Startup.cs | 5 +- .../IRepository.cs | 91 ++ .../IRepositoryFactory.cs | 3 +- .../IUnitOfWork.cs | 12 +- .../IUnitOfWorkOfT.cs | 5 +- ...soft.EntityFrameworkCore.UnitOfWork.csproj | 12 +- .../Repository.cs | 783 +++++++++++++++++- .../UnitOfWork.cs | 115 ++- .../UnitOfWorkServiceCollectionExtensions.cs | 16 + ...ntityFrameworkCore.UnitOfWork.Tests.csproj | 11 +- 14 files changed, 994 insertions(+), 102 deletions(-) create mode 100644 src/Host/Models/CustomBlogRepository.cs diff --git a/README.md b/README.md index 53cd221..8a86642 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ public void ConfigureServices(IServiceCollection services) // use in memory for testing. services .AddDbContext(opt => opt.UseInMemoryDatabase()) - .AddUnitOfWork(); + .AddUnitOfWork() + .AddCustomRepository(); } private readonly IUnitOfWork _unitOfWork; diff --git a/src/Host/Controllers/ValuesController.cs b/src/Host/Controllers/ValuesController.cs index 1049a61..f8368e6 100644 --- a/src/Host/Controllers/ValuesController.cs +++ b/src/Host/Controllers/ValuesController.cs @@ -23,7 +23,7 @@ public ValuesController(IUnitOfWork unitOfWork, ILogger logger _logger = logger; // seeding - var repo = _unitOfWork.GetRepository(); + var repo = _unitOfWork.GetRepository(hasCustomRepository: true); if (repo.Count() == 0) { repo.Insert(new Blog diff --git a/src/Host/Host.csproj b/src/Host/Host.csproj index 2c6ebe3..4eeaa79 100644 --- a/src/Host/Host.csproj +++ b/src/Host/Host.csproj @@ -5,19 +5,19 @@ Exe - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/Host/Models/CustomBlogRepository.cs b/src/Host/Models/CustomBlogRepository.cs new file mode 100644 index 0000000..e72a7dc --- /dev/null +++ b/src/Host/Models/CustomBlogRepository.cs @@ -0,0 +1,12 @@ +using Microsoft.EntityFrameworkCore; + +namespace Host.Models +{ + public class CustomBlogRepository : Repository, IRepository + { + public CustomBlogRepository(BloggingContext dbContext) : base(dbContext) + { + + } + } +} diff --git a/src/Host/Startup.cs b/src/Host/Startup.cs index 0f67f5e..fbff04e 100644 --- a/src/Host/Startup.cs +++ b/src/Host/Startup.cs @@ -32,9 +32,10 @@ public void ConfigureServices(IServiceCollection services) { // use in memory for testing. services - .AddDbContext(opt => opt.UseMySql("Server=localhost;database=unitofwork;uid=root;pwd=p@ssword;")) + .AddDbContext(opt => opt.UseMySql("Server=localhost;database=uow;uid=root;pwd=root1234;")) //.AddDbContext(opt => opt.UseInMemoryDatabase("UnitOfWork")) - .AddUnitOfWork(); + .AddUnitOfWork() + .AddCustomRepository(); services.AddMvc(); } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs index ba257c3..c712d5e 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepository.cs @@ -295,5 +295,96 @@ Task GetFirstOrDefaultAsync(Expression> predicate = /// /// The entities. void Delete(IEnumerable entities); + + + /// + /// Gets the based on a predicate, orderby delegate. This method default no-tracking query. + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// True to disable changing tracking; otherwise, false. Default to true. + /// + /// A to observe while waiting for the task to complete. + /// + /// An that contains elements that satisfy the condition specified by . + /// This method default no-tracking query. + Task> GetListAsync(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// True to disable changing tracking; otherwise, false. Default to true. + /// An that contains elements that satisfy the condition specified by . + /// This method default no-tracking query. + List GetList(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true); + + + /// + /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + TEntity GetNextById(params object[] keyValues); + + + /// + /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + Task GetNextByIdAsync(params object[] keyValues); + + + /// + /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + TEntity GetPreviousById(params object[] keyValues); + + /// + /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + Task GetPreviousByIdAsync(params object[] keyValues); + + /// + /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + TEntity GetFirst(); + + + /// + /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + Task GetFirstAsync(); + + /// + /// Finds the Last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + TEntity GetLast(); + + + /// + /// Finds the last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + Task GetLastAsync(); + } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs index ed5a85b..19b808b 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs @@ -10,8 +10,9 @@ public interface IRepositoryFactory /// /// Gets the specified repository for the . /// + /// True if providing custom repositry /// The type of the entity. /// An instance of type inherited from interface. - IRepository GetRepository() where TEntity : class; + IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class; } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs index 5489399..ce40051 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs @@ -26,9 +26,10 @@ public interface IUnitOfWork : IDisposable /// /// Gets the specified repository for the . /// + /// True if providing custom repositry /// The type of the entity. /// An instance of type inherited from interface. - IRepository GetRepository() where TEntity : class; + IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class; /// /// Saves all changes made in this context to the database. @@ -52,6 +53,15 @@ public interface IUnitOfWork : IDisposable /// The number of state entities written to database. int ExecuteSqlCommand(string sql, params object[] parameters); + + /// + /// Executes the specified raw SQL command. + /// + /// The raw SQL. + /// The parameters. + /// The number of state entities written to database. + DataTable ExecuteDtSqlCommand(string sql, params object[] parameters); + /// /// Uses raw SQL queries to fetch the specified data. /// diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs index c263b1f..bd9b77b 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using System.Data; +using System.Transactions; namespace Microsoft.EntityFrameworkCore { /// @@ -25,11 +26,11 @@ public interface IUnitOfWork : IUnitOfWork where TContext : DbContext /// /// Saves all changes made in this context to the database with distributed transaction. /// + /// The transaction to use /// True if save changes ensure auto record the change history. - /// The IsolationLevel /// An optional array. /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. - Task SaveChangesAsync(bool ensureAutoHistory = false, IsolationLevel isolation = IsolationLevel.ReadCommitted, params IUnitOfWork[] unitOfWorks); + Task SaveChangesAsync(Transaction transaction, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index 27e2dfb..7916b7b 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -1,7 +1,7 @@  A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported. - 2.0.3 + 2.1.0 rigofunc;rigofunc@outlook.com; netstandard2.0 $(NoWarn);CS1591 @@ -16,7 +16,13 @@ https://github.com/arch/UnitOfWork.git - - + + + + + + + C:\Program Files\dotnet\sdk\NuGetFallbackFolder\system.data.sqlclient\4.4.0\ref\netstandard2.0\System.Data.SqlClient.dll + diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index 15797f4..3be1553 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -38,7 +38,7 @@ public Repository(DbContext dbContext) /// /// This only been used for supporting multiple tables in the same model. This require the tables in the same database. /// - public void ChangeTable(string table) + public virtual void ChangeTable(string table) { if (_dbContext.Model.FindEntityType(typeof(TEntity)).Relational() is RelationalEntityTypeAnnotations relational) { @@ -69,7 +69,7 @@ public IQueryable GetAll() /// True to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public IPagedList GetPagedList(Expression> predicate = null, + public virtual IPagedList GetPagedList(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, int pageIndex = 0, @@ -116,7 +116,7 @@ public IPagedList GetPagedList(Expression> predicat /// /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public Task> GetPagedListAsync(Expression> predicate = null, + public virtual Task> GetPagedListAsync(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, int pageIndex = 0, @@ -162,7 +162,7 @@ public Task> GetPagedListAsync(ExpressionTrue to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public IPagedList GetPagedList(Expression> selector, + public virtual IPagedList GetPagedList(Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -212,7 +212,7 @@ public IPagedList GetPagedList(Expression /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public Task> GetPagedListAsync(Expression> selector, + public virtual Task> GetPagedListAsync(Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -257,7 +257,7 @@ public Task> GetPagedListAsync(ExpressionTrue to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public TEntity GetFirstOrDefault(Expression> predicate = null, + public virtual TEntity GetFirstOrDefault(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true) @@ -290,7 +290,7 @@ public TEntity GetFirstOrDefault(Expression> predicate = nul /// - public async Task GetFirstOrDefaultAsync(Expression> predicate = null, + public virtual async Task GetFirstOrDefaultAsync(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true) @@ -331,7 +331,7 @@ public async Task GetFirstOrDefaultAsync(Expression /// True to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public TResult GetFirstOrDefault(Expression> selector, + public virtual TResult GetFirstOrDefault(Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -364,7 +364,7 @@ public TResult GetFirstOrDefault(Expression> sel } /// - public async Task GetFirstOrDefaultAsync(Expression> selector, + public virtual async Task GetFirstOrDefaultAsync(Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -402,21 +402,21 @@ public async Task GetFirstOrDefaultAsync(ExpressionThe raw SQL. /// The parameters. /// An that contains elements that satisfy the condition specified by raw SQL. - public IQueryable FromSql(string sql, params object[] parameters) => _dbSet.FromSql(sql, parameters); + public virtual IQueryable FromSql(string sql, params object[] parameters) => _dbSet.FromSql(sql, parameters); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The values of the primary key for the entity to be found. /// The found entity or null. - public TEntity Find(params object[] keyValues) => _dbSet.Find(keyValues); + public virtual TEntity Find(params object[] keyValues) => _dbSet.Find(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The values of the primary key for the entity to be found. /// A that represents the asynchronous insert operation. - public Task FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); + public virtual Task FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. @@ -424,14 +424,14 @@ public async Task GetFirstOrDefaultAsync(ExpressionThe values of the primary key for the entity to be found. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous find operation. The task result contains the found entity or null. - public Task FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); + public virtual Task FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); /// /// Gets the count based on a predicate. /// /// /// - public int Count(Expression> predicate = null) + public virtual int Count(Expression> predicate = null) { if (predicate == null) { @@ -447,7 +447,7 @@ public int Count(Expression> predicate = null) /// Inserts a new entity synchronously. /// /// The entity to insert. - public void Insert(TEntity entity) + public virtual void Insert(TEntity entity) { var entry = _dbSet.Add(entity); } @@ -456,13 +456,13 @@ public void Insert(TEntity entity) /// Inserts a range of entities synchronously. /// /// The entities to insert. - public void Insert(params TEntity[] entities) => _dbSet.AddRange(entities); + public virtual void Insert(params TEntity[] entities) => _dbSet.AddRange(entities); /// /// Inserts a range of entities synchronously. /// /// The entities to insert. - public void Insert(IEnumerable entities) => _dbSet.AddRange(entities); + public virtual void Insert(IEnumerable entities) => _dbSet.AddRange(entities); /// /// Inserts a new entity asynchronously. @@ -470,7 +470,7 @@ public void Insert(TEntity entity) /// The entity to insert. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. - public Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) { return _dbSet.AddAsync(entity, cancellationToken); @@ -486,7 +486,7 @@ public void Insert(TEntity entity) /// /// The entities to insert. /// A that represents the asynchronous insert operation. - public Task InsertAsync(params TEntity[] entities) => _dbSet.AddRangeAsync(entities); + public virtual Task InsertAsync(params TEntity[] entities) => _dbSet.AddRangeAsync(entities); /// /// Inserts a range of entities asynchronously. @@ -494,13 +494,13 @@ public void Insert(TEntity entity) /// The entities to insert. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. - public Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) => _dbSet.AddRangeAsync(entities, cancellationToken); + public virtual Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) => _dbSet.AddRangeAsync(entities, cancellationToken); /// /// Updates the specified entity. /// /// The entity. - public void Update(TEntity entity) + public virtual void Update(TEntity entity) { _dbSet.Update(entity); } @@ -509,7 +509,7 @@ public void Update(TEntity entity) /// Updates the specified entity. /// /// The entity. - public void UpdateAsync(TEntity entity) + public virtual void UpdateAsync(TEntity entity) { _dbSet.Update(entity); @@ -519,25 +519,25 @@ public void UpdateAsync(TEntity entity) /// Updates the specified entities. /// /// The entities. - public void Update(params TEntity[] entities) => _dbSet.UpdateRange(entities); + public virtual void Update(params TEntity[] entities) => _dbSet.UpdateRange(entities); /// /// Updates the specified entities. /// /// The entities. - public void Update(IEnumerable entities) => _dbSet.UpdateRange(entities); + public virtual void Update(IEnumerable entities) => _dbSet.UpdateRange(entities); /// /// Deletes the specified entity. /// /// The entity to delete. - public void Delete(TEntity entity) => _dbSet.Remove(entity); + public virtual void Delete(TEntity entity) => _dbSet.Remove(entity); /// /// Deletes the entity by the specified primary key. /// /// The primary key value. - public void Delete(object id) + public virtual void Delete(object id) { // using a stub entity to mark for deletion var typeInfo = typeof(TEntity).GetTypeInfo(); @@ -563,12 +563,739 @@ public void Delete(object id) /// Deletes the specified entities. /// /// The entities. - public void Delete(params TEntity[] entities) => _dbSet.RemoveRange(entities); + public virtual void Delete(params TEntity[] entities) => _dbSet.RemoveRange(entities); /// /// Deletes the specified entities. /// /// The entities. - public void Delete(IEnumerable entities) => _dbSet.RemoveRange(entities); + public virtual void Delete(IEnumerable entities) => _dbSet.RemoveRange(entities); + + /// + /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// True to disable changing tracking; otherwise, false. Default to true. + /// An that contains elements that satisfy the condition specified by . + /// This method default no-tracking query. + public virtual List GetList(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true) + { + IQueryable query = _dbSet; + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(predicate); + } + + if (orderBy != null) + { + return orderBy(query).ToList(); + } + else + { + return query.ToList(); + } + } + + /// + /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// True to disable changing tracking; otherwise, false. Default to true. + /// + /// A to observe while waiting for the task to complete. + /// + /// An that contains elements that satisfy the condition specified by . + /// This method default no-tracking query. + public virtual Task> GetListAsync(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)) + { + IQueryable query = _dbSet; + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(predicate); + } + + if (orderBy != null) + { + return orderBy(query).ToListAsync(cancellationToken); + } + else + { + return query.ToListAsync(cancellationToken); + } + } + + + /// + /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + public virtual TEntity GetNextById(params object[] keyValues) + { + TEntity res = _dbSet.Find(IncrementKey(keyValues)); + if (res != null) + { + return res; + } + + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + //var ordByExp = GetOrderBy(keyColums[0],"asc"); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + //Form Where Condition + Expression> expr = GetWhereConditionExpression(key, DecrementKey(keyValues)); + Func func = expr.Compile(); + Predicate pred = func.Invoke; + TEntity currObj = lstObjs.Find(pred); + + int curobj = lstObjs.IndexOf(currObj); + if (curobj != -1) + { + int nxt = curobj + 1; + return lstObjs.ElementAtOrDefault(nxt); + } + else + { + return null; + } + } + else + { + return null; + } + } + + /// + /// Finds next entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + public virtual Task GetNextByIdAsync(params object[] keyValues) + { + TEntity res = _dbSet.Find(IncrementKey(keyValues)); + if (res != null) + { + return Task.Factory.StartNew(() => res); + } + + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + //var ordByExp = GetOrderBy(keyColums[0],"asc"); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + //Form Where Condition + Expression> expr = GetWhereConditionExpression(key, DecrementKey(keyValues)); + Func func = expr.Compile(); + Predicate pred = func.Invoke; + TEntity currObj = lstObjs.Find(pred); + + int curobj = lstObjs.IndexOf(currObj); + if(curobj != -1) + { + int nxt = curobj + 1; + return Task.Factory.StartNew(() => lstObjs.ElementAtOrDefault(nxt)); + }else + { + return Task.FromResult(null); + } + }else + { + return Task.FromResult(null); + } + } + + /// + /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + public virtual TEntity GetPreviousById(params object[] keyValues) + { + TEntity res = _dbSet.Find(DecrementKey(keyValues)); + if (res != null) + { + return res; + } + + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + //var ordByExp = GetOrderBy(keyColums[0],"asc"); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + //Form Where Condition + Expression> expr = GetWhereConditionExpression(key, IncrementKey(keyValues)); + Func func = expr.Compile(); + Predicate pred = func.Invoke; + TEntity currObj = lstObjs.Find(pred); + + int curobj = lstObjs.IndexOf(currObj); + if (curobj != -1) + { + int prev = curobj - 1; + return lstObjs.ElementAtOrDefault(prev); + } + else + { + return null; + } + } + else + { + return null; + } + } + + /// + /// Finds previous entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The values of the primary key for the entity to be found. + /// The found entity or null. + public virtual Task GetPreviousByIdAsync(params object[] keyValues) + { + TEntity res = _dbSet.Find(DecrementKey(keyValues)); + if (res != null) + { + return Task.Factory.StartNew(() => res); + } + + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + //var ordByExp = GetOrderBy(keyColums[0],"asc"); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + //Form Where Condition + Expression> expr = GetWhereConditionExpression(key, IncrementKey(keyValues)); + Func func = expr.Compile(); + Predicate pred = func.Invoke; + TEntity currObj = lstObjs.Find(pred); + + int curobj = lstObjs.IndexOf(currObj); + if (curobj != -1) + { + int prev = curobj - 1; + return Task.Factory.StartNew(() => lstObjs.ElementAtOrDefault(prev)); + } + else + { + return Task.FromResult(null); + } + } + else + { + return Task.FromResult(null); + } + } + + /// + /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + public virtual TEntity GetFirst() + { + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + return lstObjs.FirstOrDefault(); + } + else + { + return null; + } + } + + /// + /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + public virtual Task GetFirstAsync() + { + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderByExpression(keyColums); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + return Task.Factory.StartNew(() => lstObjs.FirstOrDefault()); + } + else + { + return Task.FromResult(null); + } + } + + /// + /// Finds the Last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + public virtual TEntity GetLast() + { + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderByExpression(keyColums,true); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + return lstObjs.FirstOrDefault(); + } + else + { + return null; + } + } + + /// + /// Finds the Last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. + /// + /// The found entity or null. + public virtual Task GetLastAsync() + { + //No Result Found. So Order the Entity with key column and select next Entity + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + List keyColums = key.Properties.Select(q => q.Name).ToList(); + var ordByExp = GetOrderByExpression(keyColums,true); + + List lstObjs = GetList(null, ordByExp.Compile()); + + if (lstObjs != null && lstObjs.Count > 0) + { + return Task.Factory.StartNew(() => lstObjs.FirstOrDefault()); + } + else + { + return Task.FromResult(null); + } + } + + #region Next, Previous Support Methos + + private object[] IncrementKey(object[] id) + { + int idx = id.Length -1; + int val = (int)id[idx]; + id[idx] = ++val; + return id; + } + + private object[] DecrementKey(object[] id) + { + int idx = id.Length - 1; + int val = (int)id[idx]; + id[idx] = --val; + return id; + } + + private static MemberExpression GetMemberExpression(Expression param, string propertyName) + { + if (propertyName.Contains(".")) + { + int index = propertyName.IndexOf("."); + var subParam = Expression.Property(param, propertyName.Substring(0, index)); + return GetMemberExpression(subParam, propertyName.Substring(index + 1)); + } + return Expression.Property(param, propertyName); + } + + public static Func, IOrderedQueryable> GetOrderBy(string orderColumn, string orderType) + { + Type typeQueryable = typeof(IQueryable); + ParameterExpression argQueryable = Expression.Parameter(typeQueryable, "p"); + var outerExpression = Expression.Lambda(argQueryable, argQueryable); + string[] props = orderColumn.Split('.'); + IQueryable query = new List().AsQueryable(); + Type type = typeof(T); + ParameterExpression arg = Expression.Parameter(type, "x"); + + Expression expr = arg; + foreach (string prop in props) + { + PropertyInfo pi = type.GetProperty(prop, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + expr = Expression.Property(expr, pi); + type = pi.PropertyType; + } + LambdaExpression lambda = Expression.Lambda(expr, arg); + string methodName = orderType == "asc" ? "OrderBy" : "OrderByDescending"; + + MethodCallExpression resultExp = + Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(T), type }, outerExpression.Body, Expression.Quote(lambda)); + var finalLambda = Expression.Lambda(resultExp, argQueryable); + return (Func, IOrderedQueryable>)finalLambda.Compile(); + } + + public static Expression, IOrderedQueryable>> GetOrderByExpression(IEnumerable lstSelection, bool isDescending = false) + { + bool isThenBy = false; + ParameterExpression inParameter = Expression.Parameter(typeof(T), "s"); + foreach (string propName in lstSelection) + { + MemberExpression prop = GetMemberExpression(inParameter, propName); //s.mfrId + var propertyInfo = (PropertyInfo)prop.Member; + var lambda = Expression.Lambda(prop, inParameter); // s => s.mfrId + Type pType = propertyInfo.PropertyType; + Type[] argumentTypes = new[] { typeof(T),pType }; + if (isThenBy) + { + var thenByMethod = typeof(Queryable).GetMethods() + .First(method => method.Name == "ThenBy" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(argumentTypes); + + var ThenByDescending = typeof(Queryable).GetMethods() + .First(method => method.Name == "ThenByDescending" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(argumentTypes); + if (isDescending) + { + return query => (IOrderedQueryable) + ThenByDescending.Invoke(null, new object[] { query, lambda }); + } + else + { + return query => (IOrderedQueryable) + thenByMethod.Invoke(null, new object[] { query, lambda }); + } + } + else + { + isThenBy = true; + var orderByMethod = typeof(Queryable).GetMethods() + .First(method => method.Name == "OrderBy" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(argumentTypes); + + var orderByDescMethod = typeof(Queryable).GetMethods() + .First(method => method.Name == "OrderByDescending" + && method.GetParameters().Count() == 2) + .MakeGenericMethod(argumentTypes); + + if (isDescending) { + return query => (IOrderedQueryable) + orderByDescMethod.Invoke(null, new object[] { query, lambda }); + } + else { + return query => (IOrderedQueryable) + orderByMethod.Invoke(null, new object[] { query, lambda }); + } + } + } + return null; + } + + static MethodInfo LikeMethod = typeof(DbFunctionsExtensions).GetMethod("Like", new Type[] { typeof(DbFunctions), typeof(string), typeof(string) }); + + static MethodInfo StartsWithMethod = typeof(String).GetMethod("StartsWith", new Type[] { typeof(String) }); + + static MethodInfo ContainsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) }); + + static MethodInfo EndsWithMethod = typeof(String).GetMethod("EndsWith", new Type[] { typeof(String) }); + + public static Expression> GetWhereConditionExpression(IKey key, params object[] keyValues) + { + if (key == null || keyValues == null) return null; + //Create the expression parameters + ParameterExpression inParameter = Expression.Parameter(typeof(T)); + + Expression whereExp = null; + int idx = 0; + foreach (IProperty p in key.Properties) + { + string dataPropertyName = p.Name; + Type dataType = p.PropertyInfo.PropertyType; + Object propVale = keyValues[idx++]; + + if (propVale != null) + { + Expression LHS = GetMemberExpression(inParameter, dataPropertyName); //Expression.Property(inParameter, dataPropertyName); + Expression RHS = Expression.Convert(Expression.Constant(propVale), dataType); + + if (dataType == typeof(DateTime)) + { + //MethodInfo truncTimeMethod = typeof(EF).GetProperty("Functions").GetType().GetMethod("TruncateTime", new Type[] { typeof(DateTime?) }); + //MethodInfo conMethod = typeof(System.Data.Entity.DbFunctions).GetMethod("TruncateTime", new Type[] { typeof(DateTime?) }); + //LHS = Expression.Call(conMethod, Expression.Convert(Expression.Property(inParameter, dataPropertyName), typeof(DateTime?))); + RHS = Expression.Convert(Expression.Constant(((DateTime)propVale).Date), typeof(DateTime?)); + } + string conOperator = "eq"; + Expression expr = null; + switch (conOperator) + { + case "<": + case "lt": + expr = Expression.LessThan(LHS, RHS); + break; + + case ">": + case "gt": + expr = Expression.GreaterThan(LHS, RHS); + break; + + case "<=": + case "le": + expr = Expression.LessThanOrEqual(LHS, RHS); + break; + + case ">=": + case "ge": + expr = Expression.GreaterThanOrEqual(LHS, RHS); + break; + + case "!=": + case "<>": + case "ne": + expr = Expression.NotEqual(LHS, RHS); + break; + + case "IsNull": + expr = Expression.Equal(LHS, Expression.Constant(null, dataType)); + break; + + case "IsNotNull": + expr = Expression.NotEqual(LHS, Expression.Constant(null, dataType)); + break; + + case "Like": + if (LHS.Type != typeof(string)) + { + LHS = Expression.Convert(Expression.Convert(LHS, typeof(object)), typeof(string)); + } + RHS = Expression.Convert(Expression.Constant(propVale.ToString().Replace(" ", "%") + "%"), dataType); + expr = Expression.Call(LikeMethod, Expression.Convert(Expression.Constant(EF.Functions), typeof(DbFunctions)), LHS, RHS); + break; + + case "Contains": + if (LHS.Type != typeof(string)) + { + LHS = Expression.Convert(Expression.Convert(LHS, typeof(object)), typeof(string)); + RHS = Expression.Convert(Expression.Constant("%" + propVale + "%"), dataType); + expr = Expression.Call(LikeMethod, Expression.Convert(Expression.Constant(EF.Functions), typeof(DbFunctions)), LHS, RHS); + } + else + { + expr = Expression.Call(LHS, ContainsMethod, Expression.Constant(propVale.ToString())); + } + break; + + case "StartsWith": + if (LHS.Type != typeof(string)) + { + LHS = Expression.Convert(Expression.Convert(LHS, typeof(object)), typeof(string)); + RHS = Expression.Convert(Expression.Constant(propVale + "%"), dataType); + expr = Expression.Call(LikeMethod, Expression.Convert(Expression.Constant(EF.Functions), typeof(DbFunctions)), LHS, RHS); + } + else + { + expr = Expression.Call(LHS, StartsWithMethod, Expression.Constant(propVale.ToString())); + } + break; + + case "EndsWith": + if (LHS.Type != typeof(string)) + { + LHS = Expression.Convert(Expression.Convert(LHS, typeof(object)), typeof(string)); + RHS = Expression.Convert(Expression.Constant("%" + propVale), dataType); + expr = Expression.Call(LikeMethod, Expression.Convert(Expression.Constant(EF.Functions), typeof(DbFunctions)), LHS, RHS); + } + else + { + expr = Expression.Call(LHS, EndsWithMethod, Expression.Constant(propVale.ToString())); + } + break; + + case "=": + case "==": + case "eq": + default: + expr = Expression.Equal(LHS, RHS); + break; + } + + if (whereExp == null) + { + whereExp = expr; + } + else + { + String condi = "AND"; + if (condi != null && condi.Equals("AND", StringComparison.OrdinalIgnoreCase)) + { + whereExp = Expression.AndAlso(whereExp, expr); + } + else + { + whereExp = Expression.OrElse(whereExp, expr); + } + } + } + + } + + if (whereExp == null) + { + whereExp = Expression.Constant(true); + } + return Expression.Lambda>(whereExp, inParameter); + } + #endregion + + /* + #region Logic33 + public static IEnumerable BuildOrderBys( + this IEnumerable source, + IEnumerable properties) + { + if (properties == null || properties.Count() == 0) return source; + + var typeOfT = typeof(T); + + Type t = typeOfT; + + IOrderedEnumerable result = null; + var thenBy = false; + + foreach (var item in properties) + { + var oExpr = Expression.Parameter(typeOfT, "o"); + + MemberExpression prop = GetMemberExpression(oExpr, item); + var propertyInfo = (PropertyInfo)prop.Member; + var propertyType = propertyInfo.PropertyType; + var isAscending = true; + + if (thenBy) + { + var prevExpr = Expression.Parameter(typeof(IOrderedEnumerable), "prevExpr"); + var expr1 = Expression.Lambda, IOrderedEnumerable>>( + Expression.Call( + (isAscending ? thenByMethod : thenByDescendingMethod).MakeGenericMethod(typeOfT, propertyType), + prevExpr, + Expression.Lambda( + typeof(Func<,>).MakeGenericType(typeOfT, propertyType), + Expression.MakeMemberAccess(oExpr, propertyInfo), + oExpr) + ), + prevExpr) + .Compile(); + result = expr1(result); + } + else + { + var prevExpr = Expression.Parameter(typeof(IEnumerable), "prevExpr"); + var expr1 = Expression.Lambda, IOrderedEnumerable>>( + Expression.Call( + (isAscending ? orderByMethod : orderByDescendingMethod).MakeGenericMethod(typeOfT, propertyType), + prevExpr, + Expression.Lambda( + typeof(Func<,>).MakeGenericType(typeOfT, propertyType), + Expression.MakeMemberAccess(oExpr, propertyInfo), + oExpr) + ), + prevExpr) + .Compile(); + result = expr1(source); + thenBy = true; + } + } + return result; + } + + + private static MethodInfo orderByMethod = + MethodOf(() => Enumerable.OrderBy(default(IEnumerable), default(Func))) + .GetGenericMethodDefinition(); + + private static MethodInfo orderByDescendingMethod = + MethodOf(() => Enumerable.OrderByDescending(default(IEnumerable), default(Func))) + .GetGenericMethodDefinition(); + + private static MethodInfo thenByMethod = + MethodOf(() => Enumerable.ThenBy(default(IOrderedEnumerable), default(Func))) + .GetGenericMethodDefinition(); + + private static MethodInfo thenByDescendingMethod = + MethodOf(() => Enumerable.ThenByDescending(default(IOrderedEnumerable), default(Func))) + .GetGenericMethodDefinition(); + + public static MethodInfo MethodOf(Expression> method) + { + MethodCallExpression mce = (MethodCallExpression)method.Body; + MethodInfo mi = mce.Method; + return mi; + } + + + #endregion + + */ } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs index 6819b3b..aa0befc 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs @@ -3,10 +3,13 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.SqlClient; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using System.Transactions; using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage; @@ -71,15 +74,26 @@ public void ChangeDatabase(string database) /// /// Gets the specified repository for the . /// + /// True if providing custom repositry /// The type of the entity. /// An instance of type inherited from interface. - public IRepository GetRepository() where TEntity : class + public IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class { if (repositories == null) { repositories = new Dictionary(); } + // what's the best way to support custom reposity? + if (hasCustomRepository) + { + var customRepo = _context.GetService>(); + if (customRepo != null) + { + return customRepo; + } + } + var type = typeof(TEntity); if (!repositories.ContainsKey(type)) { @@ -97,6 +111,38 @@ public IRepository GetRepository() where TEntity : class /// The number of state entities written to database. public int ExecuteSqlCommand(string sql, params object[] parameters) => _context.Database.ExecuteSqlCommand(sql, parameters); + /// + /// Executes the specified raw SQL command. + /// + /// The raw SQL. + /// The parameters. + /// The DataTable. + public DataTable ExecuteDtSqlCommand(string sql, params object[] parameters) + { + SqlConnection conn = (SqlConnection) _context.Database.GetDbConnection(); + SqlCommand cmd = new SqlCommand(sql, conn); + cmd.CommandTimeout = 0; + + if(parameters != null && parameters.Count() > 0) + { + foreach(object obj in parameters) + { + cmd.Parameters.Add(obj); + } + } + + conn.Open(); + // create data adapter + SqlDataAdapter da = new SqlDataAdapter(cmd); + // this will query your database and return the result to your datatable + DataTable dataTable = new DataTable(); + da.Fill(dataTable); + da.Dispose(); + conn.Close(); + return dataTable; + } + + /// /// Uses raw SQL queries to fetch the specified data. /// @@ -111,7 +157,7 @@ public IRepository GetRepository() where TEntity : class /// /// The IsolationLevel /// Transaction Context - public IDbContextTransaction BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted) + public IDbContextTransaction BeginTransaction(System.Data.IsolationLevel isolation = System.Data.IsolationLevel.ReadCommitted) { return _context.Database.BeginTransaction(isolation); } @@ -154,74 +200,51 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false) /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. public async Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) { - // TransactionScope will be included in .NET Core v2.0 - using (var transaction = _context.Database.BeginTransaction()) + using (var ts = new TransactionScope()) { - try + var count = 0; + foreach (var unitOfWork in unitOfWorks) { - var count = 0; - foreach (var unitOfWork in unitOfWorks) - { - var uow = unitOfWork as UnitOfWork; - uow.DbContext.Database.UseTransaction(transaction.GetDbTransaction()); - count += await uow.SaveChangesAsync(ensureAutoHistory); - } - - count += await SaveChangesAsync(ensureAutoHistory); - - transaction.Commit(); - - return count; + count += await unitOfWork.SaveChangesAsync(ensureAutoHistory); } - catch (Exception ex) - { - transaction.Rollback(); + count += await SaveChangesAsync(ensureAutoHistory); - throw ex; - } + ts.Complete(); + + return count; } } + /// /// Saves all changes made in this context to the database with distributed transaction. /// + /// The transaction to use /// True if save changes ensure auto record the change history. - /// The IsolationLevel /// An optional array. /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. - public async Task SaveChangesAsync(bool ensureAutoHistory = false, IsolationLevel isolation = IsolationLevel.ReadCommitted, params IUnitOfWork[] unitOfWorks) + public async Task SaveChangesAsync(Transaction transaction, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) { - // TransactionScope will be included in .NET Core v2.0 - using (var transaction = _context.Database.BeginTransaction(isolation)) + + using (var ts = new TransactionScope(transaction)) { - try + var count = 0; + foreach (var unitOfWork in unitOfWorks) { - var count = 0; - foreach (var unitOfWork in unitOfWorks) - { - var uow = unitOfWork as UnitOfWork; - uow.DbContext.Database.UseTransaction(transaction.GetDbTransaction()); - count += await uow.SaveChangesAsync(ensureAutoHistory); - } - - count += await SaveChangesAsync(ensureAutoHistory); - - transaction.Commit(); - - return count; + count += await unitOfWork.SaveChangesAsync(ensureAutoHistory); } - catch (Exception ex) - { - transaction.Rollback(); + count += await SaveChangesAsync(ensureAutoHistory); - throw ex; - } + ts.Complete(); + + return count; } } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs index 38f5be3..4f83e71 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs @@ -98,5 +98,21 @@ public static IServiceCollection AddUnitOfWork + /// Registers the custom repository as a service in the . + /// + /// The type of the entity. + /// The type of the custom repositry. + /// The to add services to. + /// The same service collection so that multiple calls can be chained. + public static IServiceCollection AddCustomRepository(this IServiceCollection services) + where TEntity : class + where TRepository : class, IRepository + { + services.AddScoped, TRepository>(); + + return services; + } } } diff --git a/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj b/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj index 48fc589..3872287 100644 --- a/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj +++ b/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj @@ -5,10 +5,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers + From 06953814f3050c260ad7e9d7b7afedbeecd30422 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Tue, 19 Mar 2019 09:06:16 +0530 Subject: [PATCH 18/40] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 53cd221..8a86642 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ public void ConfigureServices(IServiceCollection services) // use in memory for testing. services .AddDbContext(opt => opt.UseInMemoryDatabase()) - .AddUnitOfWork(); + .AddUnitOfWork() + .AddCustomRepository(); } private readonly IUnitOfWork _unitOfWork; From 5bd4612808e2bc7c959649f095138ea2afad850d Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Tue, 6 Aug 2019 13:10:19 +0530 Subject: [PATCH 19/40] PK Int to Long --- .../Microsoft.EntityFrameworkCore.UnitOfWork.csproj | 1 + .../Repository.cs | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index 53c0bbe..b89f413 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -14,6 +14,7 @@ https://github.com/arch/UnitOfWork/blob/master/LICENSE git https://github.com/arch/UnitOfWork.git + AnyCPU;x86 diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index 6bca1c2..c28f327 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -944,16 +944,16 @@ public Task GetLastAsync() private object[] IncrementKey(object[] id) { - int idx = id.Length -1; - int val = (int)id[idx]; + long idx = id.Length -1; + long val = (long)id[idx]; id[idx] = ++val; return id; } private object[] DecrementKey(object[] id) { - int idx = id.Length - 1; - int val = (int)id[idx]; + long idx = id.Length - 1; + long val = (long)id[idx]; id[idx] = --val; return id; } From 1b0a7bf77acc21baae97668b2aa91bb4a0ecc240 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Wed, 6 Nov 2019 10:34:02 +0530 Subject: [PATCH 20/40] Re Referenced Autohistory Removed Autohistory package, Included Forked Autohistory --- UnitOfWork.sln | 12 +++++++++++- .../Microsoft.EntityFrameworkCore.UnitOfWork.csproj | 4 +++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/UnitOfWork.sln b/UnitOfWork.sln index 5783adf..e37ee7e 100644 --- a/UnitOfWork.sln +++ b/UnitOfWork.sln @@ -16,7 +16,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.EntityFrameworkCore.UnitOfWork.Tests", "test\Microsoft.EntityFrameworkCore.UnitOfWork.Tests\Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj", "{403BEC49-090B-4992-9E9C-62C33A5B8F7D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.EntityFrameworkCore.UnitOfWork.Tests", "test\Microsoft.EntityFrameworkCore.UnitOfWork.Tests\Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj", "{403BEC49-090B-4992-9E9C-62C33A5B8F7D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.EntityFrameworkCore.AutoHistory", "..\AutoHistory\src\Microsoft.EntityFrameworkCore.AutoHistory\Microsoft.EntityFrameworkCore.AutoHistory.csproj", "{E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -36,6 +38,10 @@ Global {403BEC49-090B-4992-9E9C-62C33A5B8F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU {403BEC49-090B-4992-9E9C-62C33A5B8F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU {403BEC49-090B-4992-9E9C-62C33A5B8F7D}.Release|Any CPU.Build.0 = Release|Any CPU + {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -44,5 +50,9 @@ Global {18AE36B2-932C-4B16-BE27-B6AB25037F6F} = {A306ECD5-FA8B-47D0-8455-933FB16DA526} {A1816206-BBF2-40D2-BC3B-0956011BAA53} = {A306ECD5-FA8B-47D0-8455-933FB16DA526} {403BEC49-090B-4992-9E9C-62C33A5B8F7D} = {EBB11051-E5B2-4EBB-970B-938D28A77CA6} + {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2} = {A306ECD5-FA8B-47D0-8455-933FB16DA526} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {051325D4-AF0C-430D-9206-3CB8B64D01EB} EndGlobalSection EndGlobal diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index b89f413..9f49cb5 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -17,10 +17,12 @@ AnyCPU;x86 - + + + C:\Program Files\dotnet\sdk\NuGetFallbackFolder\system.data.sqlclient\4.4.0\ref\netstandard2.0\System.Data.SqlClient.dll From 620ae2ead308a03029097010f8c8ad66b633ed11 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Sat, 29 Feb 2020 12:47:28 +0530 Subject: [PATCH 21/40] Update Microsoft.EntityFrameworkCore.UnitOfWork.csproj --- .../Microsoft.EntityFrameworkCore.UnitOfWork.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index 9f49cb5..cff008f 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -17,7 +17,7 @@ AnyCPU;x86 - + From 213f670110be028581450a9267b71b4d9a053f93 Mon Sep 17 00:00:00 2001 From: Manikandan PS Date: Tue, 19 Jan 2021 19:40:51 +0530 Subject: [PATCH 22/40] Increment Decrement Changes --- UnitOfWork.sln | 12 +- src/Host/Controllers/ValuesController.cs | 2 +- src/Host/Host.csproj | 28 ++--- src/Host/Models/CustomBlogRepository.cs | 12 ++ src/Host/Startup.cs | 5 +- .../IRepositoryFactory.cs | 3 +- .../IUnitOfWork.cs | 5 +- .../IUnitOfWorkOfT.cs | 14 ++- ...ckup.EntityFrameworkCore.UnitOfWork.csproj | 28 +++++ ...soft.EntityFrameworkCore.UnitOfWork.csproj | 9 +- .../Repository.cs | 110 ++++++++++-------- .../UnitOfWork.cs | 72 ++++++++---- .../UnitOfWorkServiceCollectionExtensions.cs | 16 +++ ...ntityFrameworkCore.UnitOfWork.Tests.csproj | 10 +- 14 files changed, 213 insertions(+), 113 deletions(-) create mode 100644 src/Host/Models/CustomBlogRepository.cs create mode 100644 src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft - Backup.EntityFrameworkCore.UnitOfWork.csproj diff --git a/UnitOfWork.sln b/UnitOfWork.sln index e37ee7e..5783adf 100644 --- a/UnitOfWork.sln +++ b/UnitOfWork.sln @@ -16,9 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.EntityFrameworkCore.UnitOfWork.Tests", "test\Microsoft.EntityFrameworkCore.UnitOfWork.Tests\Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj", "{403BEC49-090B-4992-9E9C-62C33A5B8F7D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.EntityFrameworkCore.AutoHistory", "..\AutoHistory\src\Microsoft.EntityFrameworkCore.AutoHistory\Microsoft.EntityFrameworkCore.AutoHistory.csproj", "{E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.EntityFrameworkCore.UnitOfWork.Tests", "test\Microsoft.EntityFrameworkCore.UnitOfWork.Tests\Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj", "{403BEC49-090B-4992-9E9C-62C33A5B8F7D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -38,10 +36,6 @@ Global {403BEC49-090B-4992-9E9C-62C33A5B8F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU {403BEC49-090B-4992-9E9C-62C33A5B8F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU {403BEC49-090B-4992-9E9C-62C33A5B8F7D}.Release|Any CPU.Build.0 = Release|Any CPU - {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -50,9 +44,5 @@ Global {18AE36B2-932C-4B16-BE27-B6AB25037F6F} = {A306ECD5-FA8B-47D0-8455-933FB16DA526} {A1816206-BBF2-40D2-BC3B-0956011BAA53} = {A306ECD5-FA8B-47D0-8455-933FB16DA526} {403BEC49-090B-4992-9E9C-62C33A5B8F7D} = {EBB11051-E5B2-4EBB-970B-938D28A77CA6} - {E67B1E8D-7F12-45BC-9FA8-84586AE0F1A2} = {A306ECD5-FA8B-47D0-8455-933FB16DA526} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {051325D4-AF0C-430D-9206-3CB8B64D01EB} EndGlobalSection EndGlobal diff --git a/src/Host/Controllers/ValuesController.cs b/src/Host/Controllers/ValuesController.cs index 1049a61..f8368e6 100644 --- a/src/Host/Controllers/ValuesController.cs +++ b/src/Host/Controllers/ValuesController.cs @@ -23,7 +23,7 @@ public ValuesController(IUnitOfWork unitOfWork, ILogger logger _logger = logger; // seeding - var repo = _unitOfWork.GetRepository(); + var repo = _unitOfWork.GetRepository(hasCustomRepository: true); if (repo.Count() == 0) { repo.Insert(new Blog diff --git a/src/Host/Host.csproj b/src/Host/Host.csproj index d7c17eb..4eeaa79 100644 --- a/src/Host/Host.csproj +++ b/src/Host/Host.csproj @@ -1,23 +1,23 @@  - netcoreapp2.2 + netcoreapp2.0 true Exe - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/Host/Models/CustomBlogRepository.cs b/src/Host/Models/CustomBlogRepository.cs new file mode 100644 index 0000000..e72a7dc --- /dev/null +++ b/src/Host/Models/CustomBlogRepository.cs @@ -0,0 +1,12 @@ +using Microsoft.EntityFrameworkCore; + +namespace Host.Models +{ + public class CustomBlogRepository : Repository, IRepository + { + public CustomBlogRepository(BloggingContext dbContext) : base(dbContext) + { + + } + } +} diff --git a/src/Host/Startup.cs b/src/Host/Startup.cs index 0f67f5e..fbff04e 100644 --- a/src/Host/Startup.cs +++ b/src/Host/Startup.cs @@ -32,9 +32,10 @@ public void ConfigureServices(IServiceCollection services) { // use in memory for testing. services - .AddDbContext(opt => opt.UseMySql("Server=localhost;database=unitofwork;uid=root;pwd=p@ssword;")) + .AddDbContext(opt => opt.UseMySql("Server=localhost;database=uow;uid=root;pwd=root1234;")) //.AddDbContext(opt => opt.UseInMemoryDatabase("UnitOfWork")) - .AddUnitOfWork(); + .AddUnitOfWork() + .AddCustomRepository(); services.AddMvc(); } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs index ed5a85b..19b808b 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs @@ -10,8 +10,9 @@ public interface IRepositoryFactory /// /// Gets the specified repository for the . /// + /// True if providing custom repositry /// The type of the entity. /// An instance of type inherited from interface. - IRepository GetRepository() where TEntity : class; + IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class; } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs index 2c5d7df..ce40051 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWork.cs @@ -26,9 +26,10 @@ public interface IUnitOfWork : IDisposable /// /// Gets the specified repository for the . /// + /// True if providing custom repositry /// The type of the entity. /// An instance of type inherited from interface. - IRepository GetRepository() where TEntity : class; + IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class; /// /// Saves all changes made in this context to the database. @@ -84,4 +85,4 @@ public interface IUnitOfWork : IDisposable /// Transaction Context IDbContextTransaction BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted); } -} +} \ No newline at end of file diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs index 7e0beba..bd9b77b 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IUnitOfWorkOfT.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using System.Data; +using System.Transactions; namespace Microsoft.EntityFrameworkCore { /// @@ -17,10 +18,19 @@ public interface IUnitOfWork : IUnitOfWork where TContext : DbContext /// /// Saves all changes made in this context to the database with distributed transaction. /// - /// The IsolationLevel /// True if save changes ensure auto record the change history. /// An optional array. /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. - Task SaveChangesAsync(IsolationLevel isolation = IsolationLevel.ReadCommitted, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); + Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); + + /// + /// Saves all changes made in this context to the database with distributed transaction. + /// + /// The transaction to use + /// True if save changes ensure auto record the change history. + /// An optional array. + /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. + Task SaveChangesAsync(Transaction transaction, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); + } } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft - Backup.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft - Backup.EntityFrameworkCore.UnitOfWork.csproj new file mode 100644 index 0000000..7916b7b --- /dev/null +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft - Backup.EntityFrameworkCore.UnitOfWork.csproj @@ -0,0 +1,28 @@ + + + A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported. + 2.1.0 + rigofunc;rigofunc@outlook.com; + netstandard2.0 + $(NoWarn);CS1591 + true + true + Microsoft.EntityFrameworkCore.UnitOfWork + Microsoft.EntityFrameworkCore.UnitOfWork + Entity Framework Core;entity-framework-core;EF;Data;O/RM;unitofwork;Unit Of Work;unit-of-work + https://github.com/arch/UnitOfWork + https://github.com/arch/UnitOfWork/blob/master/LICENSE + git + https://github.com/arch/UnitOfWork.git + + + + + + + + + C:\Program Files\dotnet\sdk\NuGetFallbackFolder\system.data.sqlclient\4.4.0\ref\netstandard2.0\System.Data.SqlClient.dll + + + diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj index cff008f..7916b7b 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Microsoft.EntityFrameworkCore.UnitOfWork.csproj @@ -1,7 +1,7 @@  A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported. - 2.0.3 + 2.1.0 rigofunc;rigofunc@outlook.com; netstandard2.0 $(NoWarn);CS1591 @@ -14,15 +14,12 @@ https://github.com/arch/UnitOfWork/blob/master/LICENSE git https://github.com/arch/UnitOfWork.git - AnyCPU;x86 - + + - - - C:\Program Files\dotnet\sdk\NuGetFallbackFolder\system.data.sqlclient\4.4.0\ref\netstandard2.0\System.Data.SqlClient.dll diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index c28f327..080a44f 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -38,7 +38,7 @@ public Repository(DbContext dbContext) /// /// This only been used for supporting multiple tables in the same model. This require the tables in the same database. /// - public void ChangeTable(string table) + public virtual void ChangeTable(string table) { if (_dbContext.Model.FindEntityType(typeof(TEntity)).Relational() is RelationalEntityTypeAnnotations relational) { @@ -69,7 +69,7 @@ public IQueryable GetAll() /// True to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public IPagedList GetPagedList(Expression> predicate = null, + public virtual IPagedList GetPagedList(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, int pageIndex = 0, @@ -116,7 +116,7 @@ public IPagedList GetPagedList(Expression> predicat /// /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public Task> GetPagedListAsync(Expression> predicate = null, + public virtual Task> GetPagedListAsync(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, int pageIndex = 0, @@ -162,7 +162,7 @@ public Task> GetPagedListAsync(ExpressionTrue to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public IPagedList GetPagedList(Expression> selector, + public virtual IPagedList GetPagedList(Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -212,7 +212,7 @@ public IPagedList GetPagedList(Expression /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public Task> GetPagedListAsync(Expression> selector, + public virtual Task> GetPagedListAsync(Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -257,7 +257,7 @@ public Task> GetPagedListAsync(ExpressionTrue to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public TEntity GetFirstOrDefault(Expression> predicate = null, + public virtual TEntity GetFirstOrDefault(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true) @@ -290,7 +290,7 @@ public TEntity GetFirstOrDefault(Expression> predicate = nul /// - public async Task GetFirstOrDefaultAsync(Expression> predicate = null, + public virtual async Task GetFirstOrDefaultAsync(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true) @@ -331,7 +331,7 @@ public async Task GetFirstOrDefaultAsync(Expression /// True to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public TResult GetFirstOrDefault(Expression> selector, + public virtual TResult GetFirstOrDefault(Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -364,7 +364,7 @@ public TResult GetFirstOrDefault(Expression> sel } /// - public async Task GetFirstOrDefaultAsync(Expression> selector, + public virtual async Task GetFirstOrDefaultAsync(Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -402,21 +402,21 @@ public async Task GetFirstOrDefaultAsync(ExpressionThe raw SQL. /// The parameters. /// An that contains elements that satisfy the condition specified by raw SQL. - public IQueryable FromSql(string sql, params object[] parameters) => _dbSet.FromSql(sql, parameters); + public virtual IQueryable FromSql(string sql, params object[] parameters) => _dbSet.FromSql(sql, parameters); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The values of the primary key for the entity to be found. /// The found entity or null. - public TEntity Find(params object[] keyValues) => _dbSet.Find(keyValues); + public virtual TEntity Find(params object[] keyValues) => _dbSet.Find(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The values of the primary key for the entity to be found. /// A that represents the asynchronous insert operation. - public Task FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); + public virtual Task FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. @@ -424,14 +424,14 @@ public async Task GetFirstOrDefaultAsync(ExpressionThe values of the primary key for the entity to be found. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous find operation. The task result contains the found entity or null. - public Task FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); + public virtual Task FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); /// /// Gets the count based on a predicate. /// /// /// - public int Count(Expression> predicate = null) + public virtual int Count(Expression> predicate = null) { if (predicate == null) { @@ -447,7 +447,7 @@ public int Count(Expression> predicate = null) /// Inserts a new entity synchronously. /// /// The entity to insert. - public void Insert(TEntity entity) + public virtual void Insert(TEntity entity) { var entry = _dbSet.Add(entity); } @@ -456,13 +456,13 @@ public void Insert(TEntity entity) /// Inserts a range of entities synchronously. /// /// The entities to insert. - public void Insert(params TEntity[] entities) => _dbSet.AddRange(entities); + public virtual void Insert(params TEntity[] entities) => _dbSet.AddRange(entities); /// /// Inserts a range of entities synchronously. /// /// The entities to insert. - public void Insert(IEnumerable entities) => _dbSet.AddRange(entities); + public virtual void Insert(IEnumerable entities) => _dbSet.AddRange(entities); /// /// Inserts a new entity asynchronously. @@ -470,7 +470,7 @@ public void Insert(TEntity entity) /// The entity to insert. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. - public Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) { return _dbSet.AddAsync(entity, cancellationToken); @@ -486,7 +486,7 @@ public void Insert(TEntity entity) /// /// The entities to insert. /// A that represents the asynchronous insert operation. - public Task InsertAsync(params TEntity[] entities) => _dbSet.AddRangeAsync(entities); + public virtual Task InsertAsync(params TEntity[] entities) => _dbSet.AddRangeAsync(entities); /// /// Inserts a range of entities asynchronously. @@ -494,13 +494,13 @@ public void Insert(TEntity entity) /// The entities to insert. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. - public Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) => _dbSet.AddRangeAsync(entities, cancellationToken); + public virtual Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) => _dbSet.AddRangeAsync(entities, cancellationToken); /// /// Updates the specified entity. /// /// The entity. - public void Update(TEntity entity) + public virtual void Update(TEntity entity) { _dbSet.Update(entity); } @@ -509,7 +509,7 @@ public void Update(TEntity entity) /// Updates the specified entity. /// /// The entity. - public void UpdateAsync(TEntity entity) + public virtual void UpdateAsync(TEntity entity) { _dbSet.Update(entity); @@ -519,25 +519,25 @@ public void UpdateAsync(TEntity entity) /// Updates the specified entities. /// /// The entities. - public void Update(params TEntity[] entities) => _dbSet.UpdateRange(entities); + public virtual void Update(params TEntity[] entities) => _dbSet.UpdateRange(entities); /// /// Updates the specified entities. /// /// The entities. - public void Update(IEnumerable entities) => _dbSet.UpdateRange(entities); + public virtual void Update(IEnumerable entities) => _dbSet.UpdateRange(entities); /// /// Deletes the specified entity. /// /// The entity to delete. - public void Delete(TEntity entity) => _dbSet.Remove(entity); + public virtual void Delete(TEntity entity) => _dbSet.Remove(entity); /// /// Deletes the entity by the specified primary key. /// /// The primary key value. - public void Delete(object id) + public virtual void Delete(object id) { // using a stub entity to mark for deletion var typeInfo = typeof(TEntity).GetTypeInfo(); @@ -563,15 +563,13 @@ public void Delete(object id) /// Deletes the specified entities. /// /// The entities. - public void Delete(params TEntity[] entities) => _dbSet.RemoveRange(entities); + public virtual void Delete(params TEntity[] entities) => _dbSet.RemoveRange(entities); /// /// Deletes the specified entities. /// /// The entities. - public void Delete(IEnumerable entities) => _dbSet.RemoveRange(entities); - - + public virtual void Delete(IEnumerable entities) => _dbSet.RemoveRange(entities); /// /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. @@ -582,7 +580,7 @@ public void Delete(object id) /// True to disable changing tracking; otherwise, false. Default to true. /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public List GetList(Expression> predicate = null, + public virtual List GetList(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true) @@ -625,7 +623,7 @@ public List GetList(Expression> predicate = null, /// /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public Task> GetListAsync(Expression> predicate = null, + public virtual Task> GetListAsync(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true, @@ -663,7 +661,7 @@ public Task> GetListAsync(Expression> predicat /// /// The values of the primary key for the entity to be found. /// The found entity or null. - public TEntity GetNextById(params object[] keyValues) + public virtual TEntity GetNextById(params object[] keyValues) { TEntity res = _dbSet.Find(IncrementKey(keyValues)); if (res != null) @@ -710,7 +708,7 @@ public TEntity GetNextById(params object[] keyValues) /// /// The values of the primary key for the entity to be found. /// The found entity or null. - public Task GetNextByIdAsync(params object[] keyValues) + public virtual Task GetNextByIdAsync(params object[] keyValues) { TEntity res = _dbSet.Find(IncrementKey(keyValues)); if (res != null) @@ -755,7 +753,7 @@ public Task GetNextByIdAsync(params object[] keyValues) /// /// The values of the primary key for the entity to be found. /// The found entity or null. - public TEntity GetPreviousById(params object[] keyValues) + public virtual TEntity GetPreviousById(params object[] keyValues) { TEntity res = _dbSet.Find(DecrementKey(keyValues)); if (res != null) @@ -802,7 +800,7 @@ public TEntity GetPreviousById(params object[] keyValues) /// /// The values of the primary key for the entity to be found. /// The found entity or null. - public Task GetPreviousByIdAsync(params object[] keyValues) + public virtual Task GetPreviousByIdAsync(params object[] keyValues) { TEntity res = _dbSet.Find(DecrementKey(keyValues)); if (res != null) @@ -848,7 +846,7 @@ public Task GetPreviousByIdAsync(params object[] keyValues) /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The found entity or null. - public TEntity GetFirst() + public virtual TEntity GetFirst() { //No Result Found. So Order the Entity with key column and select next Entity IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); @@ -872,7 +870,7 @@ public TEntity GetFirst() /// Finds the first entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The found entity or null. - public Task GetFirstAsync() + public virtual Task GetFirstAsync() { //No Result Found. So Order the Entity with key column and select next Entity IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); @@ -896,7 +894,7 @@ public Task GetFirstAsync() /// Finds the Last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The found entity or null. - public TEntity GetLast() + public virtual TEntity GetLast() { //No Result Found. So Order the Entity with key column and select next Entity IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); @@ -920,7 +918,7 @@ public TEntity GetLast() /// Finds the Last entity with order by primary key. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The found entity or null. - public Task GetLastAsync() + public virtual Task GetLastAsync() { //No Result Found. So Order the Entity with key column and select next Entity IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); @@ -944,17 +942,37 @@ public Task GetLastAsync() private object[] IncrementKey(object[] id) { - long idx = id.Length -1; - long val = (long)id[idx]; - id[idx] = ++val; + int idx = id.Length -1; + var val = id[idx]; + + if(val.GetType() == typeof(int)) + { + int iVal = (int)val; + id[idx] = ++iVal; + }else if(val.GetType() == typeof(long)) + { + long lVal = (long)val; + id[idx] = ++lVal; + } + return id; } private object[] DecrementKey(object[] id) { - long idx = id.Length - 1; - long val = (long)id[idx]; - id[idx] = --val; + int idx = id.Length - 1; + var val = id[idx]; + + if (val.GetType() == typeof(int)) + { + int iVal = (int)val; + id[idx] = --iVal; + } + else if (val.GetType() == typeof(long)) + { + long lVal = (long)val; + id[idx] = --lVal; + } return id; } diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs index 9d2a494..aa0befc 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWork.cs @@ -7,7 +7,9 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using System.Transactions; using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage; @@ -72,15 +74,26 @@ public void ChangeDatabase(string database) /// /// Gets the specified repository for the . /// + /// True if providing custom repositry /// The type of the entity. /// An instance of type inherited from interface. - public IRepository GetRepository() where TEntity : class + public IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class { if (repositories == null) { repositories = new Dictionary(); } + // what's the best way to support custom reposity? + if (hasCustomRepository) + { + var customRepo = _context.GetService>(); + if (customRepo != null) + { + return customRepo; + } + } + var type = typeof(TEntity); if (!repositories.ContainsKey(type)) { @@ -98,7 +111,6 @@ public IRepository GetRepository() where TEntity : class /// The number of state entities written to database. public int ExecuteSqlCommand(string sql, params object[] parameters) => _context.Database.ExecuteSqlCommand(sql, parameters); - /// /// Executes the specified raw SQL command. /// @@ -145,7 +157,7 @@ public DataTable ExecuteDtSqlCommand(string sql, params object[] parameters) /// /// The IsolationLevel /// Transaction Context - public IDbContextTransaction BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted) + public IDbContextTransaction BeginTransaction(System.Data.IsolationLevel isolation = System.Data.IsolationLevel.ReadCommitted) { return _context.Database.BeginTransaction(isolation); } @@ -183,42 +195,56 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false) /// /// Saves all changes made in this context to the database with distributed transaction. /// - /// The IsolationLevel /// True if save changes ensure auto record the change history. /// An optional array. /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. - public async Task SaveChangesAsync(IsolationLevel isolation = IsolationLevel.ReadCommitted, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) + public async Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) { - // TransactionScope will be included in .NET Core v2.0 - using (var transaction = _context.Database.BeginTransaction(isolation)) + using (var ts = new TransactionScope()) { - try + var count = 0; + foreach (var unitOfWork in unitOfWorks) { - var count = 0; - foreach (var unitOfWork in unitOfWorks) - { - var uow = unitOfWork as UnitOfWork; - uow.DbContext.Database.UseTransaction(transaction.GetDbTransaction()); - count += await uow.SaveChangesAsync(ensureAutoHistory); - } + count += await unitOfWork.SaveChangesAsync(ensureAutoHistory); + } - count += await SaveChangesAsync(ensureAutoHistory); + count += await SaveChangesAsync(ensureAutoHistory); - transaction.Commit(); + ts.Complete(); - return count; - } - catch (Exception ex) - { + return count; + } + } - transaction.Rollback(); - throw ex; + /// + /// Saves all changes made in this context to the database with distributed transaction. + /// + /// The transaction to use + /// True if save changes ensure auto record the change history. + /// An optional array. + /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. + public async Task SaveChangesAsync(Transaction transaction, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) + { + + using (var ts = new TransactionScope(transaction)) + { + var count = 0; + foreach (var unitOfWork in unitOfWorks) + { + count += await unitOfWork.SaveChangesAsync(ensureAutoHistory); } + + count += await SaveChangesAsync(ensureAutoHistory); + + ts.Complete(); + + return count; } } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs index 38f5be3..4f83e71 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs @@ -98,5 +98,21 @@ public static IServiceCollection AddUnitOfWork + /// Registers the custom repository as a service in the . + /// + /// The type of the entity. + /// The type of the custom repositry. + /// The to add services to. + /// The same service collection so that multiple calls can be chained. + public static IServiceCollection AddCustomRepository(this IServiceCollection services) + where TEntity : class + where TRepository : class, IRepository + { + services.AddScoped, TRepository>(); + + return services; + } } } diff --git a/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj b/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj index 77c2784..3872287 100644 --- a/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj +++ b/test/Microsoft.EntityFrameworkCore.UnitOfWork.Tests/Microsoft.EntityFrameworkCore.UnitOfWork.Tests.csproj @@ -1,14 +1,14 @@  - netcoreapp2.2 + netcoreapp2.0 - - - - + + + + all runtime; build; native; contentfiles; analyzers From 807054a3294d1881db3fc311f375b2692e171a69 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Wed, 20 Jan 2021 10:32:03 +0530 Subject: [PATCH 23/40] Update Repository.cs Increment Decrement Logic Modified --- .../Repository.cs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs index 3be1553..080a44f 100644 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs +++ b/src/Microsoft.EntityFrameworkCore.UnitOfWork/Repository.cs @@ -943,16 +943,36 @@ public virtual Task GetLastAsync() private object[] IncrementKey(object[] id) { int idx = id.Length -1; - int val = (int)id[idx]; - id[idx] = ++val; + var val = id[idx]; + + if(val.GetType() == typeof(int)) + { + int iVal = (int)val; + id[idx] = ++iVal; + }else if(val.GetType() == typeof(long)) + { + long lVal = (long)val; + id[idx] = ++lVal; + } + return id; } private object[] DecrementKey(object[] id) { int idx = id.Length - 1; - int val = (int)id[idx]; - id[idx] = --val; + var val = id[idx]; + + if (val.GetType() == typeof(int)) + { + int iVal = (int)val; + id[idx] = --iVal; + } + else if (val.GetType() == typeof(long)) + { + long lVal = (long)val; + id[idx] = --lVal; + } return id; } From 8c04a3dc7c0f22c854a6d2ca76ce18b40d91d7e4 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Wed, 3 Mar 2021 13:11:52 +0530 Subject: [PATCH 24/40] .Net 5.0 --- samples/UnitOfWork.Host/Models/CustomBlogRepository.cs | 4 +++- samples/UnitOfWork.Host/Startup.cs | 1 + src/UnitOfWork/IUnitOfWork.cs | 2 ++ src/UnitOfWork/Repository.cs | 10 ++++++---- src/UnitOfWork/UnitOfWork.cs | 7 +++++++ src/UnitOfWork/UnitOfWork.csproj | 5 ++++- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/samples/UnitOfWork.Host/Models/CustomBlogRepository.cs b/samples/UnitOfWork.Host/Models/CustomBlogRepository.cs index e72a7dc..b33e932 100644 --- a/samples/UnitOfWork.Host/Models/CustomBlogRepository.cs +++ b/samples/UnitOfWork.Host/Models/CustomBlogRepository.cs @@ -1,4 +1,6 @@ -using Microsoft.EntityFrameworkCore; +using Arch.EntityFrameworkCore.UnitOfWork; +using Arch.EntityFrameworkCore.UnitOfWork.Host.Models; +using Microsoft.EntityFrameworkCore; namespace Host.Models { diff --git a/samples/UnitOfWork.Host/Startup.cs b/samples/UnitOfWork.Host/Startup.cs index b706c5d..e2bd5e5 100644 --- a/samples/UnitOfWork.Host/Startup.cs +++ b/samples/UnitOfWork.Host/Startup.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; +using Host.Models; namespace Arch.EntityFrameworkCore.UnitOfWork.Host { diff --git a/src/UnitOfWork/IUnitOfWork.cs b/src/UnitOfWork/IUnitOfWork.cs index 354eaa9..4e7a1c0 100644 --- a/src/UnitOfWork/IUnitOfWork.cs +++ b/src/UnitOfWork/IUnitOfWork.cs @@ -7,9 +7,11 @@ namespace Arch.EntityFrameworkCore.UnitOfWork { using System; + using System.Data; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.ChangeTracking; + using Microsoft.EntityFrameworkCore.Storage; /// /// Defines the interface(s) for unit of work. diff --git a/src/UnitOfWork/Repository.cs b/src/UnitOfWork/Repository.cs index 787bdb7..e8ef712 100644 --- a/src/UnitOfWork/Repository.cs +++ b/src/UnitOfWork/Repository.cs @@ -523,7 +523,7 @@ public virtual async Task GetFirstOrDefaultAsync(Expression /// The values of the primary key for the entity to be found. /// A that represents the asynchronous insert operation. - public virtual Task FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); + public virtual ValueTask FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. @@ -531,7 +531,7 @@ public virtual async Task GetFirstOrDefaultAsync(ExpressionThe values of the primary key for the entity to be found. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous find operation. The task result contains the found entity or null. - public virtual Task FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); + public virtual ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); /// /// Gets the count based on a predicate. @@ -773,7 +773,7 @@ public virtual TEntity Insert(TEntity entity) /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. public virtual ValueTask> InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) - public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) + //public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) { return _dbSet.AddAsync(entity, cancellationToken); @@ -1620,7 +1620,6 @@ public static MethodInfo MethodOf(Expression> method) #endregion */ - public virtual void Delete(IEnumerable entities) => _dbSet.RemoveRange(entities); /// /// Gets all entities. This method is not recommended @@ -1687,5 +1686,8 @@ public void ChangeEntityState(TEntity entity, EntityState state) { _dbContext.Entry(entity).State = state; } + + ValueTask IRepository.FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); + ValueTask IRepository.FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); } } diff --git a/src/UnitOfWork/UnitOfWork.cs b/src/UnitOfWork/UnitOfWork.cs index 1413b05..08cf88e 100644 --- a/src/UnitOfWork/UnitOfWork.cs +++ b/src/UnitOfWork/UnitOfWork.cs @@ -12,6 +12,7 @@ using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage; namespace Arch.EntityFrameworkCore.UnitOfWork { @@ -283,5 +284,11 @@ public void TrackGraph(object rootEntity, Action callback) { _context.ChangeTracker.TrackGraph(rootEntity, callback); } + + IDbContextTransaction IUnitOfWork.BeginTransaction(System.Data.IsolationLevel isolation) + { + return _context.Database.BeginTransaction(isolation); + } + } } diff --git a/src/UnitOfWork/UnitOfWork.csproj b/src/UnitOfWork/UnitOfWork.csproj index fdf335f..bfa80ba 100644 --- a/src/UnitOfWork/UnitOfWork.csproj +++ b/src/UnitOfWork/UnitOfWork.csproj @@ -19,7 +19,10 @@ snupkg - + + + + From 8eca1658dbc85e197ee97dd9260530dd995588cd Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Fri, 5 Mar 2021 18:56:04 +0530 Subject: [PATCH 25/40] Packeges Upgraded Packeges Upgraded --- .../UnitOfWork.Host/UnitOfWork.Host.csproj | 20 +++++++++---------- src/UnitOfWork/UnitOfWork.cs | 2 +- src/UnitOfWork/UnitOfWork.csproj | 6 +++--- test/UnitOfWork.Tests/UnitOfWork.Tests.csproj | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/samples/UnitOfWork.Host/UnitOfWork.Host.csproj b/samples/UnitOfWork.Host/UnitOfWork.Host.csproj index 3d001d5..d730bae 100644 --- a/samples/UnitOfWork.Host/UnitOfWork.Host.csproj +++ b/samples/UnitOfWork.Host/UnitOfWork.Host.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 true Exe @@ -9,15 +9,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/UnitOfWork/UnitOfWork.cs b/src/UnitOfWork/UnitOfWork.cs index 08cf88e..4b70a04 100644 --- a/src/UnitOfWork/UnitOfWork.cs +++ b/src/UnitOfWork/UnitOfWork.cs @@ -3,11 +3,11 @@ using System; using System.Collections.Generic; using System.Data; -using System.Data.SqlClient; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Transactions; +using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Infrastructure; diff --git a/src/UnitOfWork/UnitOfWork.csproj b/src/UnitOfWork/UnitOfWork.csproj index bfa80ba..d36a78c 100644 --- a/src/UnitOfWork/UnitOfWork.csproj +++ b/src/UnitOfWork/UnitOfWork.csproj @@ -3,7 +3,7 @@ A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported. 3.1.0 rigofunc;rigofunc@outlook.com; - netstandard2.0 + netstandard2.1 $(NoWarn);CS1591 true true @@ -19,8 +19,8 @@ snupkg - - + + diff --git a/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj b/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj index 1597ff6..b96845e 100644 --- a/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj +++ b/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj @@ -5,10 +5,10 @@ - - + + - + all runtime; build; native; contentfiles; analyzers From 28246367d304b2079384a1eba2b77cda4ca506ed Mon Sep 17 00:00:00 2001 From: Manikandan PS Date: Fri, 25 Jun 2021 16:52:32 +0530 Subject: [PATCH 26/40] Merged Code Change From Retail Modification --- src/UnitOfWork/IRepository.cs | 25 ++++- src/UnitOfWork/Repository.cs | 171 ++++++++++++++++++++----------- src/UnitOfWork/UnitOfWork.csproj | 10 +- 3 files changed, 145 insertions(+), 61 deletions(-) diff --git a/src/UnitOfWork/IRepository.cs b/src/UnitOfWork/IRepository.cs index f2ec259..db56771 100644 --- a/src/UnitOfWork/IRepository.cs +++ b/src/UnitOfWork/IRepository.cs @@ -430,6 +430,25 @@ Task> GetAllAsync(Expression> predicate = nul /// The entities. void Update(IEnumerable entities); + /// + /// Check Given Entity is exists + /// + /// The entity. + bool Exists(TEntity entity); + + /// + /// Inserts or Updates the specified entities. + /// + /// The entity. + void InsertOrUpdate(TEntity entity); + + /// + /// Inserts or Updates the specified entities. + /// + /// The entities. + void InsertOrUpdate(IEnumerable entities); + + /// /// Deletes the entity by the specified primary key. /// @@ -462,6 +481,7 @@ Task> GetAllAsync(Expression> predicate = nul /// A function to order elements. /// A function to include navigation properties /// True to disable changing tracking; otherwise, false. Default to true. + /// Ignore query filters /// /// A to observe while waiting for the task to complete. /// @@ -471,6 +491,7 @@ Task> GetListAsync(Expression> predicate = nul Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true, + bool ignoreQueryFilters = false, CancellationToken cancellationToken = default(CancellationToken)); /// @@ -480,12 +501,14 @@ Task> GetListAsync(Expression> predicate = nul /// A function to order elements. /// A function to include navigation properties /// True to disable changing tracking; otherwise, false. Default to true. + /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. List GetList(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, - bool disableTracking = true); + bool disableTracking = true, + bool ignoreQueryFilters = false); /// diff --git a/src/UnitOfWork/Repository.cs b/src/UnitOfWork/Repository.cs index e8ef712..0bac099 100644 --- a/src/UnitOfWork/Repository.cs +++ b/src/UnitOfWork/Repository.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using EFCore.BulkExtensions; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query; using Arch.EntityFrameworkCore.UnitOfWork.Collections; @@ -874,6 +875,104 @@ public virtual void Delete(object id) /// The entities. public virtual void Delete(IEnumerable entities) => _dbSet.RemoveRange(entities); + /// + /// Gets all entities. This method is not recommended + /// + /// The . + public async Task> GetAllAsync() + { + return await _dbSet.ToListAsync(); + } + /// + /// Gets all entities. This method is not recommended + /// + /// A function to test each element for a condition. + /// A function to order elements. + /// A function to include navigation properties + /// true to disable changing tracking; otherwise, false. Default to true. + /// Ignore query filters + /// An that contains elements that satisfy the condition specified by . + /// Ex: This method defaults to a read-only, no-tracking query. + public async Task> GetAllAsync(Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, bool ignoreQueryFilters = false) + { + IQueryable query = _dbSet; + + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(predicate); + } + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + if (orderBy != null) + { + return await orderBy(query).ToListAsync(); + } + else + { + return await query.ToListAsync(); + } + } + + private bool ExistsUpdateTimestamp(TEntity entity, out TEntity entityForUpdate) + { + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + object[] objArr = key.Properties.Select(q => entity.GetType().GetProperty(q.Name).GetValue(entity, null)).ToArray(); + TEntity obj = _dbSet.Find(objArr); + if (obj != null && obj.GetType().GetProperty("Timestamp") != null) + { + entity.GetType().GetProperty("Timestamp").SetValue(entity, obj.GetType().GetProperty("Timestamp").GetValue(obj, null)); + _dbContext.Entry(obj).State = EntityState.Detached; + } + entityForUpdate = entity; + + return obj != null; + + } + public virtual bool Exists(TEntity entity) + { + IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IKey key = entityType.FindPrimaryKey(); + object[] objArr = key.Properties.Select(q => entity.GetType().GetProperty(q.Name).GetValue(entity, null)).ToArray(); + TEntity obj = _dbSet.Find(objArr); + if (obj != null) _dbContext.Entry(obj).State = EntityState.Detached; + return obj != null; + } + + public virtual void InsertOrUpdate(TEntity entity) + { + TEntity entityForUpdate = null; + if (ExistsUpdateTimestamp(entity, out entityForUpdate)) { + //if (Exists(entity)) { + Update(entityForUpdate); + } else { + Insert(entity); + } + } + + public virtual void InsertOrUpdate(IEnumerable entities) + { + //foreach (TEntity entity in entities) InsertOrUpdate(entity); + _dbContext.BulkInsertOrUpdate(entities.ToList()); + } + /// /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. /// @@ -881,12 +980,14 @@ public virtual void Delete(object id) /// A function to order elements. /// A function to include navigation properties /// True to disable changing tracking; otherwise, false. Default to true. + /// /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. public virtual List GetList(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, - bool disableTracking = true) + bool disableTracking = true, + bool ignoreQueryFilters = false) { IQueryable query = _dbSet; if (disableTracking) @@ -904,6 +1005,11 @@ public virtual List GetList(Expression> predicate = query = query.Where(predicate); } + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + if (orderBy != null) { return orderBy(query).ToList(); @@ -921,6 +1027,7 @@ public virtual List GetList(Expression> predicate = /// A function to order elements. /// A function to include navigation properties /// True to disable changing tracking; otherwise, false. Default to true. + /// Ignore query filters /// /// A to observe while waiting for the task to complete. /// @@ -930,6 +1037,7 @@ public virtual Task> GetListAsync(Expression> Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true, + bool ignoreQueryFilters = false, CancellationToken cancellationToken = default(CancellationToken)) { IQueryable query = _dbSet; @@ -948,6 +1056,11 @@ public virtual Task> GetListAsync(Expression> query = query.Where(predicate); } + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + if (orderBy != null) { return orderBy(query).ToListAsync(cancellationToken); @@ -1621,62 +1734,6 @@ public static MethodInfo MethodOf(Expression> method) */ - /// - /// Gets all entities. This method is not recommended - /// - /// The . - public async Task> GetAllAsync() - { - return await _dbSet.ToListAsync(); - } - - /// - /// Gets all entities. This method is not recommended - /// - /// A function to test each element for a condition. - /// A function to order elements. - /// A function to include navigation properties - /// true to disable changing tracking; otherwise, false. Default to true. - /// Ignore query filters - /// An that contains elements that satisfy the condition specified by . - /// Ex: This method defaults to a read-only, no-tracking query. - public async Task> GetAllAsync(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, bool ignoreQueryFilters = false) - { - IQueryable query = _dbSet; - - if (disableTracking) - { - query = query.AsNoTracking(); - } - - if (include != null) - { - query = include(query); - } - - if (predicate != null) - { - query = query.Where(predicate); - } - - if (ignoreQueryFilters) - { - query = query.IgnoreQueryFilters(); - } - - if (orderBy != null) - { - return await orderBy(query).ToListAsync(); - } - else - { - return await query.ToListAsync(); - } - } - /// /// Change entity state for patch method on web api. /// diff --git a/src/UnitOfWork/UnitOfWork.csproj b/src/UnitOfWork/UnitOfWork.csproj index d36a78c..70cb484 100644 --- a/src/UnitOfWork/UnitOfWork.csproj +++ b/src/UnitOfWork/UnitOfWork.csproj @@ -19,10 +19,14 @@ snupkg - - + + + + - + + C:\Program Files\dotnet\sdk\NuGetFallbackFolder\system.data.sqlclient\4.4.0\ref\netstandard2.0\System.Data.SqlClient.dll + From 56909c36f34e0155f61da62f7622bd9f2a98fae8 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy_ManiSys Date: Tue, 3 Aug 2021 13:35:15 +0530 Subject: [PATCH 27/40] Datatable issue --- src/UnitOfWork/UnitOfWork.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/UnitOfWork/UnitOfWork.cs b/src/UnitOfWork/UnitOfWork.cs index 4b70a04..536de73 100644 --- a/src/UnitOfWork/UnitOfWork.cs +++ b/src/UnitOfWork/UnitOfWork.cs @@ -137,10 +137,13 @@ public DataTable ExecuteDtSqlCommand(string sql, params object[] parameters) SqlDataAdapter da = new SqlDataAdapter(cmd); // this will query your database and return the result to your datatable DataTable dataTable = new DataTable(); + DataSet ds = new DataSet(); + da.Fill(ds); da.Fill(dataTable); da.Dispose(); conn.Close(); - return dataTable; + //return dataTable; + return ds.Tables[0]; } From aff6a591c09d58e04a2f3f5d444d8a370b8911a6 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy_ManiSys Date: Wed, 4 Aug 2021 13:57:16 +0530 Subject: [PATCH 28/40] DS --- src/UnitOfWork/UnitOfWork.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/UnitOfWork/UnitOfWork.cs b/src/UnitOfWork/UnitOfWork.cs index 536de73..76d1bb2 100644 --- a/src/UnitOfWork/UnitOfWork.cs +++ b/src/UnitOfWork/UnitOfWork.cs @@ -142,8 +142,7 @@ public DataTable ExecuteDtSqlCommand(string sql, params object[] parameters) da.Fill(dataTable); da.Dispose(); conn.Close(); - //return dataTable; - return ds.Tables[0]; + return dataTable; } From 8e9521ec78603ab9a28d6e20a72097173f691f42 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 5 Aug 2021 12:39:39 +0530 Subject: [PATCH 29/40] .Net 5.0 Updated to .Net 5.0 --- samples/UnitOfWork.Host/Startup.cs | 4 +- .../UnitOfWork.Host/UnitOfWork.Host.csproj | 4 +- src/UnitOfWork/Repository.cs | 49 ------------------- src/UnitOfWork/UnitOfWork.csproj | 4 +- test/UnitOfWork.Tests/UnitOfWork.Tests.csproj | 2 +- 5 files changed, 7 insertions(+), 56 deletions(-) diff --git a/samples/UnitOfWork.Host/Startup.cs b/samples/UnitOfWork.Host/Startup.cs index e2bd5e5..081fce3 100644 --- a/samples/UnitOfWork.Host/Startup.cs +++ b/samples/UnitOfWork.Host/Startup.cs @@ -34,8 +34,8 @@ public void ConfigureServices(IServiceCollection services) { // use in memory for testing. services - .AddDbContext(opt => opt.UseMySql("Server=localhost;database=uow;uid=root;pwd=root1234;")) - //.AddDbContext(opt => opt.UseInMemoryDatabase("UnitOfWork")) + //.AddDbContext(opt => opt.UseMySql("Server=localhost;database=uow;uid=root;pwd=root1234;")) + .AddDbContext(opt => opt.UseInMemoryDatabase("UnitOfWork")) .AddUnitOfWork() .AddCustomRepository(); diff --git a/samples/UnitOfWork.Host/UnitOfWork.Host.csproj b/samples/UnitOfWork.Host/UnitOfWork.Host.csproj index d730bae..61060bd 100644 --- a/samples/UnitOfWork.Host/UnitOfWork.Host.csproj +++ b/samples/UnitOfWork.Host/UnitOfWork.Host.csproj @@ -15,9 +15,9 @@ - + - + diff --git a/src/UnitOfWork/Repository.cs b/src/UnitOfWork/Repository.cs index 800f7c0..802af90 100644 --- a/src/UnitOfWork/Repository.cs +++ b/src/UnitOfWork/Repository.cs @@ -1833,55 +1833,6 @@ public async Task> GetAllAsync(Expression - /// Gets all entities. This method is not recommended - /// - /// The selector for projection. - /// A function to test each element for a condition. - /// A function to order elements. - /// A function to include navigation properties - /// true to disable changing tracking; otherwise, false. Default to true. - /// Ignore query filters - /// An that contains elements that satisfy the condition specified by . - /// Ex: This method defaults to a read-only, no-tracking query. - public async Task> GetAllAsync(Expression> selector, - Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, bool ignoreQueryFilters = false) - { - IQueryable query = _dbSet; - - if (disableTracking) - { - query = query.AsNoTracking(); - } - - if (include != null) - { - query = include(query); - } - - if (predicate != null) - { - query = query.Where(predicate); - } - - if (ignoreQueryFilters) - { - query = query.IgnoreQueryFilters(); - } - - - if (orderBy != null) - { - return await orderBy(query).Select(selector).ToListAsync(); - } - else - { - return await query.Select(selector).ToListAsync(); - } - } /// /// Change entity state for patch method on web api. diff --git a/src/UnitOfWork/UnitOfWork.csproj b/src/UnitOfWork/UnitOfWork.csproj index 70cb484..4d33f63 100644 --- a/src/UnitOfWork/UnitOfWork.csproj +++ b/src/UnitOfWork/UnitOfWork.csproj @@ -3,7 +3,7 @@ A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported. 3.1.0 rigofunc;rigofunc@outlook.com; - netstandard2.1 + net6.0 $(NoWarn);CS1591 true true @@ -19,7 +19,7 @@ snupkg - + diff --git a/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj b/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj index b96845e..f1dbf4d 100644 --- a/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj +++ b/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 From e1ed3dbb1d5468adafd788678d22e32073fccd83 Mon Sep 17 00:00:00 2001 From: Easwaramoorthy Date: Thu, 5 Aug 2021 13:07:37 +0530 Subject: [PATCH 30/40] Update UnitOfWork.csproj .Net 5.0 --- src/UnitOfWork/UnitOfWork.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UnitOfWork/UnitOfWork.csproj b/src/UnitOfWork/UnitOfWork.csproj index 4d33f63..cf485ae 100644 --- a/src/UnitOfWork/UnitOfWork.csproj +++ b/src/UnitOfWork/UnitOfWork.csproj @@ -3,7 +3,7 @@ A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported. 3.1.0 rigofunc;rigofunc@outlook.com; - net6.0 + net5.0 $(NoWarn);CS1591 true true From f6739d096975c0ba8fc110b1adf6ea5165693014 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 14:48:15 +0100 Subject: [PATCH 31/40] Update dotnet 6 and nugets. --- .../UnitOfWork.Host/UnitOfWork.Host.csproj | 20 +++++++++---------- src/UnitOfWork/UnitOfWork.csproj | 10 +++++----- test/UnitOfWork.Tests/UnitOfWork.Tests.csproj | 6 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/samples/UnitOfWork.Host/UnitOfWork.Host.csproj b/samples/UnitOfWork.Host/UnitOfWork.Host.csproj index 61060bd..6adbd89 100644 --- a/samples/UnitOfWork.Host/UnitOfWork.Host.csproj +++ b/samples/UnitOfWork.Host/UnitOfWork.Host.csproj @@ -1,6 +1,6 @@  - net5.0 + net6.0 true Exe @@ -9,15 +9,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/UnitOfWork/UnitOfWork.csproj b/src/UnitOfWork/UnitOfWork.csproj index cf485ae..796ff30 100644 --- a/src/UnitOfWork/UnitOfWork.csproj +++ b/src/UnitOfWork/UnitOfWork.csproj @@ -3,7 +3,7 @@ A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported. 3.1.0 rigofunc;rigofunc@outlook.com; - net5.0 + net6.0 $(NoWarn);CS1591 true true @@ -19,10 +19,10 @@ snupkg - - - - + + + + diff --git a/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj b/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj index f1dbf4d..0db32b5 100644 --- a/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj +++ b/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj @@ -1,12 +1,12 @@  - net5.0 + net6.0 - - + + all From aebc5fe6620cd348195f980ba942ddb850a11e78 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 14:50:12 +0100 Subject: [PATCH 32/40] Use async overloads where possible. Use await using... --- .../Controllers/ValuesController.cs | 4 +- src/UnitOfWork/UnitOfWork.cs | 37 ++++++++----------- .../IQueryablePageListExtensionsTests.cs | 32 ++++++++-------- 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/samples/UnitOfWork.Host/Controllers/ValuesController.cs b/samples/UnitOfWork.Host/Controllers/ValuesController.cs index 5fcfe36..5fc41ad 100644 --- a/samples/UnitOfWork.Host/Controllers/ValuesController.cs +++ b/samples/UnitOfWork.Host/Controllers/ValuesController.cs @@ -170,11 +170,11 @@ public async Task> Get(string term) { _logger.LogInformation("demo about first or default with include"); - var item = _unitOfWork.GetRepository().GetFirstOrDefault(predicate: x => x.Title.Contains(term), include: source => source.Include(blog => blog.Posts).ThenInclude(post => post.Comments)); + var item = await _unitOfWork.GetRepository().GetFirstOrDefaultAsync(predicate: x => x.Title.Contains(term), include: source => source.Include(blog => blog.Posts).ThenInclude(post => post.Comments)); _logger.LogInformation("demo about first or default without include"); - item = _unitOfWork.GetRepository().GetFirstOrDefault(predicate: x => x.Title.Contains(term), orderBy: source => source.OrderByDescending(b => b.Id)); + item = await _unitOfWork.GetRepository().GetFirstOrDefaultAsync(predicate: x => x.Title.Contains(term), orderBy: source => source.OrderByDescending(b => b.Id)); _logger.LogInformation("demo about first or default with projection"); diff --git a/src/UnitOfWork/UnitOfWork.cs b/src/UnitOfWork/UnitOfWork.cs index 07114df..770d078 100644 --- a/src/UnitOfWork/UnitOfWork.cs +++ b/src/UnitOfWork/UnitOfWork.cs @@ -203,20 +203,18 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false) /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. public async Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) { - using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) + using var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + var count = 0; + foreach (var unitOfWork in unitOfWorks) { - var count = 0; - foreach (var unitOfWork in unitOfWorks) - { - count += await unitOfWork.SaveChangesAsync(ensureAutoHistory).ConfigureAwait(false); - } + count += await unitOfWork.SaveChangesAsync(ensureAutoHistory).ConfigureAwait(false); + } - count += await SaveChangesAsync(ensureAutoHistory); + count += await SaveChangesAsync(ensureAutoHistory); - ts.Complete(); + ts.Complete(); - return count; - } + return count; } @@ -229,21 +227,18 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false, params I /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database. public async Task SaveChangesAsync(Transaction transaction, bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) { - - using (var ts = new TransactionScope(transaction)) + using var ts = new TransactionScope(transaction); + var count = 0; + foreach (var unitOfWork in unitOfWorks) { - var count = 0; - foreach (var unitOfWork in unitOfWorks) - { - count += await unitOfWork.SaveChangesAsync(ensureAutoHistory); - } + count += await unitOfWork.SaveChangesAsync(ensureAutoHistory); + } - count += await SaveChangesAsync(ensureAutoHistory); + count += await SaveChangesAsync(ensureAutoHistory); - ts.Complete(); + ts.Complete(); - return count; - } + return count; } diff --git a/test/UnitOfWork.Tests/IQueryablePageListExtensionsTests.cs b/test/UnitOfWork.Tests/IQueryablePageListExtensionsTests.cs index 5554b51..9ee781f 100644 --- a/test/UnitOfWork.Tests/IQueryablePageListExtensionsTests.cs +++ b/test/UnitOfWork.Tests/IQueryablePageListExtensionsTests.cs @@ -13,27 +13,25 @@ public class IQueryablePageListExtensionsTests [Fact] public async Task ToPagedListAsyncTest() { - using (var db = new InMemoryContext()) - { - var testItems = TestItems(); - await db.AddRangeAsync(testItems); - db.SaveChanges(); + await using var db = new InMemoryContext(); + var testItems = TestItems(); + await db.AddRangeAsync(testItems); + await db.SaveChangesAsync(); - var items = db.Customers.Where(t => t.Age > 1); + var items = db.Customers.Where(t => t.Age > 1); - var page = await items.ToPagedListAsync(1, 2); - Assert.NotNull(page); + var page = await items.ToPagedListAsync(1, 2); + Assert.NotNull(page); - Assert.Equal(4, page.TotalCount); - Assert.Equal(2, page.Items.Count); - Assert.Equal("E", page.Items[0].Name); + Assert.Equal(4, page.TotalCount); + Assert.Equal(2, page.Items.Count); + Assert.Equal("E", page.Items[0].Name); - page = await items.ToPagedListAsync(0, 2); - Assert.NotNull(page); - Assert.Equal(4, page.TotalCount); - Assert.Equal(2, page.Items.Count); - Assert.Equal("C", page.Items[0].Name); - } + page = await items.ToPagedListAsync(0, 2); + Assert.NotNull(page); + Assert.Equal(4, page.TotalCount); + Assert.Equal(2, page.Items.Count); + Assert.Equal("C", page.Items[0].Name); } public List TestItems() From 5dbe683b0722ffb2c8fb1511ba5ef87eff0876a4 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 14:52:59 +0100 Subject: [PATCH 33/40] Fix rootNamespaces and remove duplicit IRepositoryFactory.cs --- samples/UnitOfWork.Host/UnitOfWork.Host.csproj | 3 +++ .../IRepositoryFactory.cs | 18 ------------------ src/UnitOfWork/UnitOfWork.csproj | 1 + test/UnitOfWork.Tests/UnitOfWork.Tests.csproj | 2 ++ 4 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs diff --git a/samples/UnitOfWork.Host/UnitOfWork.Host.csproj b/samples/UnitOfWork.Host/UnitOfWork.Host.csproj index 6adbd89..03e55c7 100644 --- a/samples/UnitOfWork.Host/UnitOfWork.Host.csproj +++ b/samples/UnitOfWork.Host/UnitOfWork.Host.csproj @@ -1,9 +1,12 @@  + net6.0 + Arch.EntityFrameworkCore.UnitOfWork.Host true Exe + diff --git a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs b/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs deleted file mode 100644 index 19b808b..0000000 --- a/src/Microsoft.EntityFrameworkCore.UnitOfWork/IRepositoryFactory.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Arch team. All rights reserved. - -namespace Microsoft.EntityFrameworkCore -{ - /// - /// Defines the interfaces for interfaces. - /// - public interface IRepositoryFactory - { - /// - /// Gets the specified repository for the . - /// - /// True if providing custom repositry - /// The type of the entity. - /// An instance of type inherited from interface. - IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class; - } -} diff --git a/src/UnitOfWork/UnitOfWork.csproj b/src/UnitOfWork/UnitOfWork.csproj index 796ff30..d5880ec 100644 --- a/src/UnitOfWork/UnitOfWork.csproj +++ b/src/UnitOfWork/UnitOfWork.csproj @@ -7,6 +7,7 @@ $(NoWarn);CS1591 true true + Arch.EntityFrameworkCore.UnitOfWork Microsoft.EntityFrameworkCore.UnitOfWork Microsoft.EntityFrameworkCore.UnitOfWork Entity Framework Core;entity-framework-core;EF;Data;O/RM;unitofwork;Unit Of Work;unit-of-work diff --git a/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj b/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj index 0db32b5..72d5c91 100644 --- a/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj +++ b/test/UnitOfWork.Tests/UnitOfWork.Tests.csproj @@ -2,6 +2,8 @@ net6.0 + + Arch.EntityFrameworkCore.UnitOfWork.Tests From 1ac77b427dbf9bbe545bfc2bb1013aa43fc7fcb0 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 15:00:33 +0100 Subject: [PATCH 34/40] Cleanup tests - Make it compliant with C#10 coding standards. --- test/UnitOfWork.Tests/Entities/City.cs | 2 +- test/UnitOfWork.Tests/Entities/Country.cs | 2 +- test/UnitOfWork.Tests/Entities/Customer.cs | 2 +- test/UnitOfWork.Tests/Entities/Town.cs | 2 +- .../IQueryablePageListExtensionsTests.cs | 17 +++--- .../IRepositoryGetPagedListTest.cs | 54 +++++++++---------- test/UnitOfWork.Tests/InMemoryContext.cs | 9 ++-- .../TestGetFirstOrDefaultAsync.cs | 34 ++++++------ 8 files changed, 58 insertions(+), 64 deletions(-) diff --git a/test/UnitOfWork.Tests/Entities/City.cs b/test/UnitOfWork.Tests/Entities/City.cs index f632058..10539d2 100644 --- a/test/UnitOfWork.Tests/Entities/City.cs +++ b/test/UnitOfWork.Tests/Entities/City.cs @@ -3,7 +3,7 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Tests.Entities { - public class City + public record City { public int Id { get; set; } public string Name { get; set; } diff --git a/test/UnitOfWork.Tests/Entities/Country.cs b/test/UnitOfWork.Tests/Entities/Country.cs index d4d03d8..f23d2b0 100644 --- a/test/UnitOfWork.Tests/Entities/Country.cs +++ b/test/UnitOfWork.Tests/Entities/Country.cs @@ -2,7 +2,7 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Tests.Entities { - public class Country + public record Country { public int Id { get; set; } public string Name { get; set; } diff --git a/test/UnitOfWork.Tests/Entities/Customer.cs b/test/UnitOfWork.Tests/Entities/Customer.cs index d2188d5..094881a 100644 --- a/test/UnitOfWork.Tests/Entities/Customer.cs +++ b/test/UnitOfWork.Tests/Entities/Customer.cs @@ -1,6 +1,6 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Tests.Entities { - public class Customer + public record Customer { public int Id { get; set; } public string Name { get; set; } diff --git a/test/UnitOfWork.Tests/Entities/Town.cs b/test/UnitOfWork.Tests/Entities/Town.cs index aca070a..16ee910 100644 --- a/test/UnitOfWork.Tests/Entities/Town.cs +++ b/test/UnitOfWork.Tests/Entities/Town.cs @@ -1,6 +1,6 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Tests.Entities { - public class Town + public record Town { public int Id { get; set; } public string Name { get; set; } diff --git a/test/UnitOfWork.Tests/IQueryablePageListExtensionsTests.cs b/test/UnitOfWork.Tests/IQueryablePageListExtensionsTests.cs index 9ee781f..ecebdec 100644 --- a/test/UnitOfWork.Tests/IQueryablePageListExtensionsTests.cs +++ b/test/UnitOfWork.Tests/IQueryablePageListExtensionsTests.cs @@ -34,17 +34,14 @@ public async Task ToPagedListAsyncTest() Assert.Equal("C", page.Items[0].Name); } - public List TestItems() - { - return new List() + private static IEnumerable TestItems() => new List() { - new Customer(){Name="A", Age=1}, - new Customer(){Name="B", Age=1}, - new Customer(){Name="C", Age=2}, - new Customer(){Name="D", Age=3}, - new Customer(){Name="E", Age=4}, - new Customer(){Name="F", Age=5}, + new(){Name="A", Age=1}, + new(){Name="B", Age=1}, + new(){Name="C", Age=2}, + new(){Name="D", Age=3}, + new(){Name="E", Age=4}, + new(){Name="F", Age=5}, }; - } } } diff --git a/test/UnitOfWork.Tests/IRepositoryGetPagedListTest.cs b/test/UnitOfWork.Tests/IRepositoryGetPagedListTest.cs index 504a3d2..e95ee93 100644 --- a/test/UnitOfWork.Tests/IRepositoryGetPagedListTest.cs +++ b/test/UnitOfWork.Tests/IRepositoryGetPagedListTest.cs @@ -8,23 +8,23 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Tests { public class IRepositoryGetPagedListTest { - private static readonly InMemoryContext db; + private static readonly InMemoryContext Db; static IRepositoryGetPagedListTest() { - db = new InMemoryContext(); + Db = new InMemoryContext(); - db.AddRange(TestCountries); - db.AddRange(TestCities); - db.AddRange(TestTowns); + Db.AddRange(TestCountries); + Db.AddRange(TestCities); + Db.AddRange(TestTowns); - db.SaveChanges(); + Db.SaveChanges(); } [Fact] public void GetPagedList() { - var repository = new Repository(db); + var repository = new Repository(Db); var page = repository.GetPagedList(predicate: t => t.Name == "C", include: source => source.Include(t => t.Country), pageSize: 1); @@ -39,7 +39,7 @@ public void GetPagedList() [Fact] public async Task GetPagedListAsync() { - var repository = new Repository(db); + var repository = new Repository(Db); var page = await repository.GetPagedListAsync(predicate: t => t.Name == "C", include: source => source.Include(t => t.Country), pageSize: 1); @@ -54,7 +54,7 @@ public async Task GetPagedListAsync() [Fact] public async Task GetPagedListWithIncludingMultipleLevelsAsync() { - var repository = new Repository(db); + var repository = new Repository(Db); var page = await repository.GetPagedListAsync(predicate: t => t.Name == "A", include: country => country.Include(c => c.Cities).ThenInclude(city => city.Towns), pageSize: 1); @@ -67,7 +67,7 @@ public async Task GetPagedListWithIncludingMultipleLevelsAsync() [Fact] public void GetPagedListWithoutInclude() { - var repository = new Repository(db); + var repository = new Repository(Db); var page = repository.GetPagedList(pageIndex: 0, pageSize: 1); @@ -75,30 +75,30 @@ public void GetPagedListWithoutInclude() Assert.Null(page.Items[0].Country); } - protected static List TestCountries => new List + private static IEnumerable TestCountries => new List { - new Country {Id = 1, Name = "A"}, - new Country {Id = 2, Name = "B"} + new() {Id = 1, Name = "A"}, + new() {Id = 2, Name = "B"} }; - public static List TestCities => new List + private static IEnumerable TestCities => new List { - new City { Id = 1, Name = "A", CountryId = 1}, - new City { Id = 2, Name = "B", CountryId = 2}, - new City { Id = 3, Name = "C", CountryId = 1}, - new City { Id = 4, Name = "D", CountryId = 2}, - new City { Id = 5, Name = "E", CountryId = 1}, - new City { Id = 6, Name = "F", CountryId = 2}, + new() { Id = 1, Name = "A", CountryId = 1}, + new() { Id = 2, Name = "B", CountryId = 2}, + new() { Id = 3, Name = "C", CountryId = 1}, + new() { Id = 4, Name = "D", CountryId = 2}, + new() { Id = 5, Name = "E", CountryId = 1}, + new() { Id = 6, Name = "F", CountryId = 2}, }; - public static List TestTowns => new List + private static IEnumerable TestTowns => new List { - new Town { Id = 1, Name="A", CityId = 1 }, - new Town { Id = 2, Name="B", CityId = 2 }, - new Town { Id = 3, Name="C", CityId = 3 }, - new Town { Id = 4, Name="D", CityId = 4 }, - new Town { Id = 5, Name="E", CityId = 5 }, - new Town { Id = 6, Name="F", CityId = 6 }, + new() { Id = 1, Name="A", CityId = 1 }, + new() { Id = 2, Name="B", CityId = 2 }, + new() { Id = 3, Name="C", CityId = 3 }, + new() { Id = 4, Name="D", CityId = 4 }, + new() { Id = 5, Name="E", CityId = 5 }, + new() { Id = 6, Name="F", CityId = 6 }, }; } } diff --git a/test/UnitOfWork.Tests/InMemoryContext.cs b/test/UnitOfWork.Tests/InMemoryContext.cs index cf60438..e0c4099 100644 --- a/test/UnitOfWork.Tests/InMemoryContext.cs +++ b/test/UnitOfWork.Tests/InMemoryContext.cs @@ -5,12 +5,9 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Tests { public class InMemoryContext : DbContext { - public DbSet Countries { get; set; } - public DbSet Customers { get; set; } + public DbSet Countries => Set(); + public DbSet Customers => Set(); - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseInMemoryDatabase("test"); - } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseInMemoryDatabase("test"); } } diff --git a/test/UnitOfWork.Tests/TestGetFirstOrDefaultAsync.cs b/test/UnitOfWork.Tests/TestGetFirstOrDefaultAsync.cs index ded0b57..a178593 100644 --- a/test/UnitOfWork.Tests/TestGetFirstOrDefaultAsync.cs +++ b/test/UnitOfWork.Tests/TestGetFirstOrDefaultAsync.cs @@ -52,30 +52,30 @@ public async void TestGetFirstOrDefaultAsyncCanInclude() } - protected static List TestCountries => new List + private static IEnumerable TestCountries => new List { - new Country {Id = 1, Name = "A"}, - new Country {Id = 2, Name = "B"} + new() {Id = 1, Name = "A"}, + new() {Id = 2, Name = "B"} }; - public static List TestCities => new List + private static IEnumerable TestCities => new List { - new City { Id = 1, Name = "A", CountryId = 1}, - new City { Id = 2, Name = "B", CountryId = 2}, - new City { Id = 3, Name = "C", CountryId = 1}, - new City { Id = 4, Name = "D", CountryId = 2}, - new City { Id = 5, Name = "E", CountryId = 1}, - new City { Id = 6, Name = "F", CountryId = 2}, + new() { Id = 1, Name = "A", CountryId = 1}, + new() { Id = 2, Name = "B", CountryId = 2}, + new() { Id = 3, Name = "C", CountryId = 1}, + new() { Id = 4, Name = "D", CountryId = 2}, + new() { Id = 5, Name = "E", CountryId = 1}, + new() { Id = 6, Name = "F", CountryId = 2}, }; - public static List TestTowns => new List + private static IEnumerable TestTowns => new List { - new Town { Id = 1, Name="TownA", CityId = 1 }, - new Town { Id = 2, Name="TownB", CityId = 2 }, - new Town { Id = 3, Name="TownC", CityId = 3 }, - new Town { Id = 4, Name="TownD", CityId = 4 }, - new Town { Id = 5, Name="TownE", CityId = 5 }, - new Town { Id = 6, Name="TownF", CityId = 6 }, + new() { Id = 1, Name="TownA", CityId = 1 }, + new() { Id = 2, Name="TownB", CityId = 2 }, + new() { Id = 3, Name="TownC", CityId = 3 }, + new() { Id = 4, Name="TownD", CityId = 4 }, + new() { Id = 5, Name="TownE", CityId = 5 }, + new() { Id = 6, Name="TownF", CityId = 6 }, }; } } From 1c5b65d2b3c10df9e913674b98fe5ceefa8ba334 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 15:18:19 +0100 Subject: [PATCH 35/40] Improve Arch.EntityFrameworkCore.UnitOfWork.* code readability. Unify formatting, improve consistency. --- ...ns.cs => EnumerablePagedListExtensions.cs} | 2 +- src/UnitOfWork/Collections/PagedList.cs | 14 +- ...ions.cs => QueryablePageListExtensions.cs} | 11 +- src/UnitOfWork/IRepository.cs | 162 +++--- src/UnitOfWork/IRepositoryFactory.cs | 2 +- src/UnitOfWork/IUnitOfWork.cs | 8 +- src/UnitOfWork/IUnitOfWorkOfT.cs | 2 +- src/UnitOfWork/Repository.cs | 514 +++++++----------- src/UnitOfWork/UnitOfWork.cs | 73 +-- .../UnitOfWorkServiceCollectionExtensions.cs | 4 +- 10 files changed, 339 insertions(+), 453 deletions(-) rename src/UnitOfWork/Collections/{IEnumerablePagedListExtensions.cs => EnumerablePagedListExtensions.cs} (97%) rename src/UnitOfWork/Collections/{IQueryablePageListExtensions.cs => QueryablePageListExtensions.cs} (84%) diff --git a/src/UnitOfWork/Collections/IEnumerablePagedListExtensions.cs b/src/UnitOfWork/Collections/EnumerablePagedListExtensions.cs similarity index 97% rename from src/UnitOfWork/Collections/IEnumerablePagedListExtensions.cs rename to src/UnitOfWork/Collections/EnumerablePagedListExtensions.cs index 01c8ef5..42697f7 100644 --- a/src/UnitOfWork/Collections/IEnumerablePagedListExtensions.cs +++ b/src/UnitOfWork/Collections/EnumerablePagedListExtensions.cs @@ -8,7 +8,7 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Collections /// /// Provides some extension methods for to provide paging capability. /// - public static class IEnumerablePagedListExtensions + public static class EnumerablePagedListExtensions { /// /// Converts the specified source to by the specified and . diff --git a/src/UnitOfWork/Collections/PagedList.cs b/src/UnitOfWork/Collections/PagedList.cs index 7dca0db..13d9c40 100644 --- a/src/UnitOfWork/Collections/PagedList.cs +++ b/src/UnitOfWork/Collections/PagedList.cs @@ -70,15 +70,15 @@ internal PagedList(IEnumerable source, int pageIndex, int pageSize, int index throw new ArgumentException($"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex"); } - if (source is IQueryable querable) + if (source is IQueryable queryable) { PageIndex = pageIndex; PageSize = pageSize; IndexFrom = indexFrom; - TotalCount = querable.Count(); + TotalCount = queryable.Count(); TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize); - Items = querable.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToList(); + Items = queryable.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToList(); } else { @@ -95,7 +95,7 @@ internal PagedList(IEnumerable source, int pageIndex, int pageSize, int index /// /// Initializes a new instance of the class. /// - internal PagedList() => Items = new T[0]; + internal PagedList() => Items = Array.Empty(); } @@ -165,15 +165,15 @@ public PagedList(IEnumerable source, Func, IEnumer throw new ArgumentException($"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex"); } - if (source is IQueryable querable) + if (source is IQueryable queryable) { PageIndex = pageIndex; PageSize = pageSize; IndexFrom = indexFrom; - TotalCount = querable.Count(); + TotalCount = queryable.Count(); TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize); - var items = querable.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToArray(); + var items = queryable.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToArray(); Items = new List(converter(items)); } diff --git a/src/UnitOfWork/Collections/IQueryablePageListExtensions.cs b/src/UnitOfWork/Collections/QueryablePageListExtensions.cs similarity index 84% rename from src/UnitOfWork/Collections/IQueryablePageListExtensions.cs rename to src/UnitOfWork/Collections/QueryablePageListExtensions.cs index 9578aae..3d198cf 100644 --- a/src/UnitOfWork/Collections/IQueryablePageListExtensions.cs +++ b/src/UnitOfWork/Collections/QueryablePageListExtensions.cs @@ -6,7 +6,7 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Collections { - public static class IQueryablePageListExtensions + public static class QueryablePageListExtensions { /// /// Converts the specified source to by the specified and . @@ -20,7 +20,7 @@ public static class IQueryablePageListExtensions /// /// The start index value. /// An instance of the inherited from interface. - public static async Task> ToPagedListAsync(this IQueryable source, int pageIndex, int pageSize, int indexFrom = 0, CancellationToken cancellationToken = default(CancellationToken)) + public static async Task> ToPagedListAsync(this IQueryable source, int pageIndex, int pageSize, int indexFrom = 0, CancellationToken cancellationToken = default) { if (indexFrom > pageIndex) { @@ -28,8 +28,11 @@ public static class IQueryablePageListExtensions } var count = await source.CountAsync(cancellationToken).ConfigureAwait(false); - var items = await source.Skip((pageIndex - indexFrom) * pageSize) - .Take(pageSize).ToListAsync(cancellationToken).ConfigureAwait(false); + var items = await source + .Skip((pageIndex - indexFrom) * pageSize) + .Take(pageSize) + .ToListAsync(cancellationToken) + .ConfigureAwait(false); var pagedList = new PagedList() { diff --git a/src/UnitOfWork/IRepository.cs b/src/UnitOfWork/IRepository.cs index b4f94b3..0ccdc4a 100644 --- a/src/UnitOfWork/IRepository.cs +++ b/src/UnitOfWork/IRepository.cs @@ -33,7 +33,7 @@ public interface IRepository where TEntity : class void ChangeTable(string table); /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -44,16 +44,17 @@ public interface IRepository where TEntity : class /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - IPagedList GetPagedList(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - bool ignoreQueryFilters = false); + IPagedList GetPagedList( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + bool ignoreQueryFilters = false); /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -67,17 +68,18 @@ IPagedList GetPagedList(Expression> predicate = nul /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - Task> GetPagedListAsync(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - CancellationToken cancellationToken = default(CancellationToken), - bool ignoreQueryFilters = false); + Task> GetPagedListAsync( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default, + bool ignoreQueryFilters = false); /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// The selector for projection. /// A function to test each element for a condition. @@ -89,17 +91,18 @@ Task> GetPagedListAsync(Expression> pred /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - IPagedList GetPagedList(Expression> selector, - Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - bool ignoreQueryFilters = false) where TResult : class; + IPagedList GetPagedList( + Expression> selector, + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + bool ignoreQueryFilters = false) where TResult : class; /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// The selector for projection. /// A function to test each element for a condition. @@ -114,18 +117,19 @@ IPagedList GetPagedList(Expression> sel /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - Task> GetPagedListAsync(Expression> selector, - Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - CancellationToken cancellationToken = default(CancellationToken), - bool ignoreQueryFilters = false) where TResult : class; + Task> GetPagedListAsync( + Expression> selector, + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default, + bool ignoreQueryFilters = false) where TResult : class; /// - /// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method defaults to a read-only, no-tracking query. + /// Gets the first or default entity based on a predicate, orderBy delegate and include delegate. This method defaults to a read-only, no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -134,14 +138,15 @@ Task> GetPagedListAsync(ExpressionIgnore query filters /// An that contains elements that satisfy the condition specified by . /// This method defaults to a read-only, no-tracking query. - TEntity GetFirstOrDefault(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false); + TEntity GetFirstOrDefault( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false); /// - /// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method defaults to a read-only, no-tracking query. + /// Gets the first or default entity based on a predicate, orderBy delegate and include delegate. This method defaults to a read-only, no-tracking query. /// /// The selector for projection. /// A function to test each element for a condition. @@ -151,15 +156,16 @@ TEntity GetFirstOrDefault(Expression> predicate = null, /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method defaults to a read-only, no-tracking query. - TResult GetFirstOrDefault(Expression> selector, - Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false); + TResult GetFirstOrDefault( + Expression> selector, + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false); /// - /// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method defaults to a read-only, no-tracking query. + /// Gets the first or default entity based on a predicate, orderBy delegate and include delegate. This method defaults to a read-only, no-tracking query. /// /// The selector for projection. /// A function to test each element for a condition. @@ -177,7 +183,7 @@ Task GetFirstOrDefaultAsync(Expression> bool ignoreQueryFilters = false); /// - /// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method defaults to a read-only, no-tracking query. + /// Gets the first or default entity based on a predicate, orderBy delegate and include delegate. This method defaults to a read-only, no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -238,11 +244,12 @@ Task GetFirstOrDefaultAsync(Expression> predicate = /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// Ex: This method defaults to a read-only, no-tracking query. - IQueryable GetAll(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false); + IQueryable GetAll( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false); /// /// Gets all entities. This method is not recommended @@ -278,11 +285,12 @@ IQueryable GetAll(Expression> selector, /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// Ex: This method defaults to a read-only, no-tracking query. - Task> GetAllAsync(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false); + Task> GetAllAsync( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false); /// /// Gets all entities. This method is not recommended @@ -431,7 +439,7 @@ Task> GetAllAsync(Expression> sel /// The entity to insert. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. - ValueTask> InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)); + ValueTask> InsertAsync(TEntity entity, CancellationToken cancellationToken = default); /// /// Inserts a range of entities asynchronously. @@ -446,7 +454,7 @@ Task> GetAllAsync(Expression> sel /// The entities to insert. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. - Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)); + Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default); /// /// Updates the specified entity. @@ -511,7 +519,7 @@ Task> GetAllAsync(Expression> sel /// - /// Gets the based on a predicate, orderby delegate. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -523,15 +531,16 @@ Task> GetAllAsync(Expression> sel /// /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - Task> GetListAsync(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false, - CancellationToken cancellationToken = default(CancellationToken)); + Task> GetListAsync( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false, + CancellationToken cancellationToken = default); /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -540,11 +549,12 @@ Task> GetListAsync(Expression> predicate = nul /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - List GetList(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false); + List GetList( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false); /// diff --git a/src/UnitOfWork/IRepositoryFactory.cs b/src/UnitOfWork/IRepositoryFactory.cs index c5f017f..885ef48 100644 --- a/src/UnitOfWork/IRepositoryFactory.cs +++ b/src/UnitOfWork/IRepositoryFactory.cs @@ -14,7 +14,7 @@ public interface IRepositoryFactory /// /// Gets the specified repository for the . /// - /// True if providing custom repositry + /// True if providing custom repository /// The type of the entity. /// An instance of type inherited from interface. IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class; diff --git a/src/UnitOfWork/IUnitOfWork.cs b/src/UnitOfWork/IUnitOfWork.cs index 4e7a1c0..af4fee0 100644 --- a/src/UnitOfWork/IUnitOfWork.cs +++ b/src/UnitOfWork/IUnitOfWork.cs @@ -30,7 +30,7 @@ public interface IUnitOfWork : IDisposable /// /// Gets the specified repository for the . /// - /// True if providing custom repositry + /// True if providing custom repository /// The type of the entity. /// An instance of type inherited from interface. IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class; @@ -38,7 +38,7 @@ public interface IUnitOfWork : IDisposable /// /// Saves all changes made in this context to the database. /// - /// True if sayve changes ensure auto record the change history. + /// True if save changes ensure auto record the change history. /// The number of state entries written to the database. int SaveChanges(bool ensureAutoHistory = false); @@ -79,11 +79,11 @@ public interface IUnitOfWork : IDisposable /// Uses TrakGrap Api to attach disconnected entities /// /// Root entity - /// Delegate to convert Object's State properities to Entities entry state. + /// Delegate to convert Object's State properties to Entities entry state. void TrackGraph(object rootEntity, Action callback); /// - /// Starts Databaselevel Transaction + /// Starts DatabaseLevel Transaction /// /// The IsolationLevel /// Transaction Context diff --git a/src/UnitOfWork/IUnitOfWorkOfT.cs b/src/UnitOfWork/IUnitOfWorkOfT.cs index 4ce2e68..cadb711 100644 --- a/src/UnitOfWork/IUnitOfWorkOfT.cs +++ b/src/UnitOfWork/IUnitOfWorkOfT.cs @@ -10,7 +10,7 @@ namespace Arch.EntityFrameworkCore.UnitOfWork /// /// Defines the interface(s) for generic unit of work. /// - public interface IUnitOfWork : IUnitOfWork where TContext : DbContext { + public interface IUnitOfWork : IUnitOfWork where TContext : DbContext { /// /// Gets the db context. /// diff --git a/src/UnitOfWork/Repository.cs b/src/UnitOfWork/Repository.cs index 802af90..8d13755 100644 --- a/src/UnitOfWork/Repository.cs +++ b/src/UnitOfWork/Repository.cs @@ -22,8 +22,8 @@ namespace Arch.EntityFrameworkCore.UnitOfWork /// The type of the entity. public class Repository : IRepository where TEntity : class { - protected readonly DbContext _dbContext; - protected readonly DbSet _dbSet; + protected readonly DbContext DbContext; + protected readonly DbSet DbSet; /// /// Initializes a new instance of the class. @@ -31,8 +31,8 @@ public class Repository : IRepository where TEntity : class /// The database context. public Repository(DbContext dbContext) { - _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); - _dbSet = _dbContext.Set(); + DbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + DbSet = DbContext.Set(); } /// @@ -44,7 +44,7 @@ public Repository(DbContext dbContext) /// public virtual void ChangeTable(string table) { - if (_dbContext.Model.FindEntityType(typeof(TEntity)) is IConventionEntityType relational) + if (DbContext.Model.FindEntityType(typeof(TEntity)) is IConventionEntityType relational) { relational.SetTableName(table); } @@ -54,10 +54,7 @@ public virtual void ChangeTable(string table) /// Gets all entities. This method is not recommended /// /// The . - public IQueryable GetAll() - { - return _dbSet; - } + public IQueryable GetAll() => DbSet; /// /// Gets all entities. This method is not recommended @@ -74,7 +71,7 @@ public IQueryable GetAll( Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true, bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -100,10 +97,8 @@ public IQueryable GetAll( { return orderBy(query); } - else - { - return query; - } + + return query; } /// @@ -117,12 +112,13 @@ public IQueryable GetAll( /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// Ex: This method defaults to a read-only, no-tracking query. - public IQueryable GetAll(Expression> selector, + public IQueryable GetAll( + Expression> selector, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true, bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -148,10 +144,8 @@ public IQueryable GetAll(Expression> sel { return orderBy(query).Select(selector); } - else - { - return query.Select(selector); - } + + return query.Select(selector); } /// @@ -166,15 +160,16 @@ public IQueryable GetAll(Expression> sel /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public virtual IPagedList GetPagedList(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - bool ignoreQueryFilters = false) + public virtual IPagedList GetPagedList( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -200,10 +195,8 @@ public virtual IPagedList GetPagedList(Expression> { return orderBy(query).ToPagedList(pageIndex, pageSize); } - else - { - return query.ToPagedList(pageIndex, pageSize); - } + + return query.ToPagedList(pageIndex, pageSize); } /// @@ -221,16 +214,17 @@ public virtual IPagedList GetPagedList(Expression> /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public virtual Task> GetPagedListAsync(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - CancellationToken cancellationToken = default(CancellationToken), - bool ignoreQueryFilters = false) + public virtual Task> GetPagedListAsync( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default, + bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -256,10 +250,8 @@ public virtual Task> GetPagedListAsync(Expression @@ -275,17 +267,18 @@ public virtual Task> GetPagedListAsync(ExpressionIgnore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public virtual IPagedList GetPagedList(Expression> selector, - Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - bool ignoreQueryFilters = false) + public virtual IPagedList GetPagedList( + Expression> selector, + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + bool ignoreQueryFilters = false) where TResult : class { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -311,10 +304,8 @@ public virtual IPagedList GetPagedList(Expression @@ -333,18 +324,19 @@ public virtual IPagedList GetPagedList(ExpressionIgnore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public virtual Task> GetPagedListAsync(Expression> selector, - Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - CancellationToken cancellationToken = default(CancellationToken), - bool ignoreQueryFilters = false) + public virtual Task> GetPagedListAsync( + Expression> selector, + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default, + bool ignoreQueryFilters = false) where TResult : class { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -370,10 +362,8 @@ public virtual Task> GetPagedListAsync(Expression @@ -386,13 +376,14 @@ public virtual Task> GetPagedListAsync(ExpressionIgnore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public virtual TEntity GetFirstOrDefault(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false) + public virtual TEntity GetFirstOrDefault( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -418,21 +409,20 @@ public virtual TEntity GetFirstOrDefault(Expression> predica { return orderBy(query).FirstOrDefault(); } - else - { - return query.FirstOrDefault(); - } + + return query.FirstOrDefault(); } /// - public virtual async Task GetFirstOrDefaultAsync(Expression> predicate = null, + public virtual async Task GetFirstOrDefaultAsync( + Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true, bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -475,14 +465,15 @@ public virtual async Task GetFirstOrDefaultAsync(ExpressionIgnore query filters /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public virtual TResult GetFirstOrDefault(Expression> selector, - Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false) + public virtual TResult GetFirstOrDefault( + Expression> selector, + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -515,13 +506,14 @@ public virtual TResult GetFirstOrDefault(Expression - public virtual async Task GetFirstOrDefaultAsync(Expression> selector, - Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, bool ignoreQueryFilters = false) + public virtual async Task GetFirstOrDefaultAsync( + Expression> selector, + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -559,21 +551,21 @@ public virtual async Task GetFirstOrDefaultAsync(ExpressionThe raw SQL. /// The parameters. /// An that contains elements that satisfy the condition specified by raw SQL. - public virtual IQueryable FromSql(string sql, params object[] parameters) => _dbSet.FromSqlRaw(sql, parameters); + public virtual IQueryable FromSql(string sql, params object[] parameters) => DbSet.FromSqlRaw(sql, parameters); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The values of the primary key for the entity to be found. /// The found entity or null. - public virtual TEntity Find(params object[] keyValues) => _dbSet.Find(keyValues); + public virtual TEntity Find(params object[] keyValues) => DbSet.Find(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The values of the primary key for the entity to be found. /// A that represents the asynchronous insert operation. - public virtual ValueTask FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); + public virtual ValueTask FindAsync(params object[] keyValues) => DbSet.FindAsync(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. @@ -581,7 +573,7 @@ public virtual async Task GetFirstOrDefaultAsync(ExpressionThe values of the primary key for the entity to be found. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous find operation. The task result contains the found entity or null. - public virtual ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); + public virtual ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken) => DbSet.FindAsync(keyValues, cancellationToken); /// /// Gets the count based on a predicate. @@ -592,12 +584,10 @@ public virtual int Count(Expression> predicate = null) { if (predicate == null) { - return _dbSet.Count(); - } - else - { - return _dbSet.Count(predicate); + return DbSet.Count(); } + + return DbSet.Count(predicate); } /// @@ -609,12 +599,10 @@ public virtual async Task CountAsync(Expression> predic { if (predicate == null) { - return await _dbSet.CountAsync(); - } - else - { - return await _dbSet.CountAsync(predicate); + return await DbSet.CountAsync(); } + + return await DbSet.CountAsync(predicate); } /// @@ -622,34 +610,16 @@ public virtual async Task CountAsync(Expression> predic /// /// /// - public virtual long LongCount(Expression> predicate = null) - { - if (predicate == null) - { - return _dbSet.LongCount(); - } - else - { - return _dbSet.LongCount(predicate); - } - } + public virtual long LongCount(Expression> predicate = null) + => predicate == null ? DbSet.LongCount() : DbSet.LongCount(predicate); /// /// Gets async the long count based on a predicate. /// /// /// - public virtual async Task LongCountAsync(Expression> predicate = null) - { - if (predicate == null) - { - return await _dbSet.LongCountAsync(); - } - else - { - return await _dbSet.LongCountAsync(predicate); - } - } + public virtual async Task LongCountAsync(Expression> predicate = null) + => predicate == null ? await DbSet.LongCountAsync() : await DbSet.LongCountAsync(predicate); /// /// Gets the max based on a predicate. @@ -657,13 +627,8 @@ public virtual async Task LongCountAsync(Expression> p /// /// /// /// decimal - public virtual T Max(Expression> predicate = null, Expression> selector = null) - { - if (predicate == null) - return _dbSet.Max(selector); - else - return _dbSet.Where(predicate).Max(selector); - } + public virtual T Max(Expression> predicate = null, Expression> selector = null) + => predicate == null ? DbSet.Max(selector) : DbSet.Where(predicate).Max(selector); /// /// Gets the async max based on a predicate. @@ -671,13 +636,8 @@ public virtual T Max(Expression> predicate = null, Expres /// /// /// /// decimal - public virtual async Task MaxAsync(Expression> predicate = null, Expression> selector = null) - { - if (predicate == null) - return await _dbSet.MaxAsync(selector); - else - return await _dbSet.Where(predicate).MaxAsync(selector); - } + public virtual async Task MaxAsync(Expression> predicate = null, Expression> selector = null) + => predicate == null ? await DbSet.MaxAsync(selector) : await DbSet.Where(predicate).MaxAsync(selector); /// /// Gets the min based on a predicate. @@ -686,12 +646,7 @@ public virtual async Task MaxAsync(Expression> predica /// /// /// decimal public virtual T Min(Expression> predicate = null, Expression> selector = null) - { - if (predicate == null) - return _dbSet.Min(selector); - else - return _dbSet.Where(predicate).Min(selector); - } + => predicate == null ? DbSet.Min(selector) : DbSet.Where(predicate).Min(selector); /// /// Gets the async min based on a predicate. @@ -699,13 +654,8 @@ public virtual T Min(Expression> predicate = null, Expres /// /// /// /// decimal - public virtual async Task MinAsync(Expression> predicate = null, Expression> selector = null) - { - if (predicate == null) - return await _dbSet.MinAsync(selector); - else - return await _dbSet.Where(predicate).MinAsync(selector); - } + public virtual async Task MinAsync(Expression> predicate = null, Expression> selector = null) + => predicate == null ? await DbSet.MinAsync(selector) : await DbSet.Where(predicate).MinAsync(selector); /// /// Gets the average based on a predicate. @@ -713,13 +663,8 @@ public virtual async Task MinAsync(Expression> predica /// /// /// /// decimal - public virtual decimal Average(Expression> predicate = null, Expression> selector = null) - { - if (predicate == null) - return _dbSet.Average(selector); - else - return _dbSet.Where(predicate).Average(selector); - } + public virtual decimal Average(Expression> predicate = null, Expression> selector = null) + => predicate == null ? DbSet.Average(selector) : DbSet.Where(predicate).Average(selector); /// /// Gets the async average based on a predicate. @@ -727,13 +672,8 @@ public virtual decimal Average(Expression> predicate = null, /// /// /// /// decimal - public virtual async Task AverageAsync(Expression> predicate = null, Expression> selector = null) - { - if (predicate == null) - return await _dbSet.AverageAsync(selector); - else - return await _dbSet.Where(predicate).AverageAsync(selector); - } + public virtual async Task AverageAsync(Expression> predicate = null, Expression> selector = null) + => predicate == null ? await DbSet.AverageAsync(selector) : await DbSet.Where(predicate).AverageAsync(selector); /// /// Gets the sum based on a predicate. @@ -741,13 +681,8 @@ public virtual async Task AverageAsync(Expression> /// /// /// /// decimal - public virtual decimal Sum(Expression> predicate = null, Expression> selector = null) - { - if (predicate == null) - return _dbSet.Sum(selector); - else - return _dbSet.Where(predicate).Sum(selector); - } + public virtual decimal Sum(Expression> predicate = null, Expression> selector = null) + => predicate == null ? DbSet.Sum(selector) : DbSet.Where(predicate).Sum(selector); /// /// Gets the async sum based on a predicate. @@ -755,66 +690,42 @@ public virtual decimal Sum(Expression> predicate = null, Exp /// /// /// /// decimal - public virtual async Task SumAsync(Expression> predicate = null, Expression> selector = null) - { - if (predicate == null) - return await _dbSet.SumAsync(selector); - else - return await _dbSet.Where(predicate).SumAsync(selector); - } + public virtual async Task SumAsync(Expression> predicate = null, Expression> selector = null) + => predicate == null ? await DbSet.SumAsync(selector) : await DbSet.Where(predicate).SumAsync(selector); /// /// Gets the exists based on a predicate. /// /// /// - public bool Exists(Expression> selector = null) - { - if (selector == null) - { - return _dbSet.Any(); - } - else - { - return _dbSet.Any(selector); - } - } + public bool Exists(Expression> selector = null) + => selector == null ? DbSet.Any() : DbSet.Any(selector); + /// /// Gets the async exists based on a predicate. /// /// /// - public async Task ExistsAsync(Expression> selector = null) - { - if (selector == null) - { - return await _dbSet.AnyAsync(); - } - else - { - return await _dbSet.AnyAsync(selector); - } - } + public async Task ExistsAsync(Expression> selector = null) + => selector == null ? await DbSet.AnyAsync() : await DbSet.AnyAsync(selector); + /// /// Inserts a new entity synchronously. /// /// The entity to insert. - public virtual TEntity Insert(TEntity entity) - { - return _dbSet.Add(entity).Entity; - } + public virtual TEntity Insert(TEntity entity) => DbSet.Add(entity).Entity; /// /// Inserts a range of entities synchronously. /// /// The entities to insert. - public virtual void Insert(params TEntity[] entities) => _dbSet.AddRange(entities); + public virtual void Insert(params TEntity[] entities) => DbSet.AddRange(entities); /// /// Inserts a range of entities synchronously. /// /// The entities to insert. - public virtual void Insert(IEnumerable entities) => _dbSet.AddRange(entities); + public virtual void Insert(IEnumerable entities) => DbSet.AddRange(entities); /// /// Inserts a new entity asynchronously. @@ -822,24 +733,20 @@ public virtual TEntity Insert(TEntity entity) /// The entity to insert. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. - public virtual ValueTask> InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) - //public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) - { - return _dbSet.AddAsync(entity, cancellationToken); - - // Shadow properties? - //var property = _dbContext.Entry(entity).Property("Created"); - //if (property != null) { - //property.CurrentValue = DateTime.Now; - //} - } - + public virtual ValueTask> InsertAsync(TEntity entity, CancellationToken cancellationToken = default) + => DbSet.AddAsync(entity, cancellationToken); + + // Shadow properties? + //var property = _dbContext.Entry(entity).Property("Created"); + //if (property != null) { + //property.CurrentValue = DateTime.Now; + //} /// /// Inserts a range of entities asynchronously. /// /// The entities to insert. /// A that represents the asynchronous insert operation. - public virtual Task InsertAsync(params TEntity[] entities) => _dbSet.AddRangeAsync(entities); + public virtual Task InsertAsync(params TEntity[] entities) => DbSet.AddRangeAsync(entities); /// /// Inserts a range of entities asynchronously. @@ -847,44 +754,38 @@ public virtual TEntity Insert(TEntity entity) /// The entities to insert. /// A to observe while waiting for the task to complete. /// A that represents the asynchronous insert operation. - public virtual Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) => _dbSet.AddRangeAsync(entities, cancellationToken); + public virtual Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default) + => DbSet.AddRangeAsync(entities, cancellationToken); /// /// Updates the specified entity. /// /// The entity. - public virtual void Update(TEntity entity) - { - _dbSet.Update(entity); - } + public virtual void Update(TEntity entity) => DbSet.Update(entity); /// /// Updates the specified entity. /// /// The entity. - public virtual void UpdateAsync(TEntity entity) - { - _dbSet.Update(entity); - - } + public virtual void UpdateAsync(TEntity entity) => DbSet.Update(entity); /// /// Updates the specified entities. /// /// The entities. - public virtual void Update(params TEntity[] entities) => _dbSet.UpdateRange(entities); + public virtual void Update(params TEntity[] entities) => DbSet.UpdateRange(entities); /// /// Updates the specified entities. /// /// The entities. - public virtual void Update(IEnumerable entities) => _dbSet.UpdateRange(entities); + public virtual void Update(IEnumerable entities) => DbSet.UpdateRange(entities); /// /// Deletes the specified entity. /// /// The entity to delete. - public virtual void Delete(TEntity entity) => _dbSet.Remove(entity); + public virtual void Delete(TEntity entity) => DbSet.Remove(entity); /// /// Deletes the entity by the specified primary key. @@ -894,17 +795,17 @@ public virtual void Delete(object id) { // using a stub entity to mark for deletion var typeInfo = typeof(TEntity).GetTypeInfo(); - var key = _dbContext.Model.FindEntityType(typeInfo).FindPrimaryKey().Properties.FirstOrDefault(); + var key = DbContext.Model.FindEntityType(typeInfo).FindPrimaryKey().Properties.FirstOrDefault(); var property = typeInfo.GetProperty(key?.Name); if (property != null) { var entity = Activator.CreateInstance(); property.SetValue(entity, id); - _dbContext.Entry(entity).State = EntityState.Deleted; + DbContext.Entry(entity).State = EntityState.Deleted; } else { - var entity = _dbSet.Find(id); + var entity = DbSet.Find(id); if (entity != null) { Delete(entity); @@ -916,22 +817,20 @@ public virtual void Delete(object id) /// Deletes the specified entities. /// /// The entities. - public virtual void Delete(params TEntity[] entities) => _dbSet.RemoveRange(entities); + public virtual void Delete(params TEntity[] entities) => DbSet.RemoveRange(entities); /// /// Deletes the specified entities. /// /// The entities. - public virtual void Delete(IEnumerable entities) => _dbSet.RemoveRange(entities); + public virtual void Delete(IEnumerable entities) => DbSet.RemoveRange(entities); /// /// Gets all entities. This method is not recommended /// /// The . - public async Task> GetAllAsync() - { - return await _dbSet.ToListAsync(); - } + public async Task> GetAllAsync() => await DbSet.ToListAsync(); + /// /// Gets all entities. This method is not recommended /// @@ -942,12 +841,13 @@ public async Task> GetAllAsync() /// Ignore query filters /// An that contains elements that satisfy the condition specified by . /// Ex: This method defaults to a read-only, no-tracking query. - public async Task> GetAllAsync(Expression> predicate = null, + public async Task> GetAllAsync( + Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true, bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -981,14 +881,14 @@ public async Task> GetAllAsync(Expression> pr private bool ExistsUpdateTimestamp(TEntity entity, out TEntity entityForUpdate) { - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); object[] objArr = key.Properties.Select(q => entity.GetType().GetProperty(q.Name).GetValue(entity, null)).ToArray(); - TEntity obj = _dbSet.Find(objArr); + TEntity obj = DbSet.Find(objArr); if (obj != null && obj.GetType().GetProperty("Timestamp") != null) { entity.GetType().GetProperty("Timestamp").SetValue(entity, obj.GetType().GetProperty("Timestamp").GetValue(obj, null)); - _dbContext.Entry(obj).State = EntityState.Detached; + DbContext.Entry(obj).State = EntityState.Detached; } entityForUpdate = entity; @@ -997,30 +897,25 @@ private bool ExistsUpdateTimestamp(TEntity entity, out TEntity entityForUpdate) } public virtual bool Exists(TEntity entity) { - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); object[] objArr = key.Properties.Select(q => entity.GetType().GetProperty(q.Name).GetValue(entity, null)).ToArray(); - TEntity obj = _dbSet.Find(objArr); - if (obj != null) _dbContext.Entry(obj).State = EntityState.Detached; + TEntity obj = DbSet.Find(objArr); + if (obj != null) DbContext.Entry(obj).State = EntityState.Detached; return obj != null; } public virtual void InsertOrUpdate(TEntity entity) { - TEntity entityForUpdate = null; - if (ExistsUpdateTimestamp(entity, out entityForUpdate)) { - //if (Exists(entity)) { + if (ExistsUpdateTimestamp(entity, out var entityForUpdate)) { Update(entityForUpdate); } else { Insert(entity); } } - public virtual void InsertOrUpdate(IEnumerable entities) - { - //foreach (TEntity entity in entities) InsertOrUpdate(entity); - _dbContext.BulkInsertOrUpdate(entities.ToList()); - } + public virtual void InsertOrUpdate(IEnumerable entities) => + DbContext.BulkInsertOrUpdate(entities.ToList()); /// /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. @@ -1032,13 +927,14 @@ public virtual void InsertOrUpdate(IEnumerable entities) /// /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public virtual List GetList(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false) + public virtual List GetList( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { query = query.AsNoTracking(); @@ -1063,10 +959,8 @@ public virtual List GetList(Expression> predicate = { return orderBy(query).ToList(); } - else - { - return query.ToList(); - } + + return query.ToList(); } /// @@ -1082,14 +976,15 @@ public virtual List GetList(Expression> predicate = /// /// An that contains elements that satisfy the condition specified by . /// This method default no-tracking query. - public virtual Task> GetListAsync(Expression> predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true, - bool ignoreQueryFilters = false, - CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task> GetListAsync( + Expression> predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true, + bool ignoreQueryFilters = false, + CancellationToken cancellationToken = default) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { query = query.AsNoTracking(); @@ -1114,10 +1009,8 @@ public virtual Task> GetListAsync(Expression> { return orderBy(query).ToListAsync(cancellationToken); } - else - { - return query.ToListAsync(cancellationToken); - } + + return query.ToListAsync(cancellationToken); } @@ -1128,14 +1021,14 @@ public virtual Task> GetListAsync(Expression> /// The found entity or null. public virtual TEntity GetNextById(params object[] keyValues) { - TEntity res = _dbSet.Find(IncrementKey(keyValues)); + TEntity res = DbSet.Find(IncrementKey(keyValues)); if (res != null) { return res; } //No Result Found. So Order the Entity with key column and select next Entity - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); //var ordByExp = GetOrderBy(keyColums[0],"asc"); @@ -1175,14 +1068,14 @@ public virtual TEntity GetNextById(params object[] keyValues) /// The found entity or null. public virtual Task GetNextByIdAsync(params object[] keyValues) { - TEntity res = _dbSet.Find(IncrementKey(keyValues)); + TEntity res = DbSet.Find(IncrementKey(keyValues)); if (res != null) { return Task.Factory.StartNew(() => res); } //No Result Found. So Order the Entity with key column and select next Entity - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); //var ordByExp = GetOrderBy(keyColums[0],"asc"); @@ -1220,14 +1113,14 @@ public virtual Task GetNextByIdAsync(params object[] keyValues) /// The found entity or null. public virtual TEntity GetPreviousById(params object[] keyValues) { - TEntity res = _dbSet.Find(DecrementKey(keyValues)); + TEntity res = DbSet.Find(DecrementKey(keyValues)); if (res != null) { return res; } //No Result Found. So Order the Entity with key column and select next Entity - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); //var ordByExp = GetOrderBy(keyColums[0],"asc"); @@ -1267,14 +1160,14 @@ public virtual TEntity GetPreviousById(params object[] keyValues) /// The found entity or null. public virtual Task GetPreviousByIdAsync(params object[] keyValues) { - TEntity res = _dbSet.Find(DecrementKey(keyValues)); + TEntity res = DbSet.Find(DecrementKey(keyValues)); if (res != null) { return Task.Factory.StartNew(() => res); } //No Result Found. So Order the Entity with key column and select next Entity - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); //var ordByExp = GetOrderBy(keyColums[0],"asc"); @@ -1314,7 +1207,7 @@ public virtual Task GetPreviousByIdAsync(params object[] keyValues) public virtual TEntity GetFirst() { //No Result Found. So Order the Entity with key column and select next Entity - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); var ordByExp = GetOrderByExpression(keyColums); @@ -1323,7 +1216,7 @@ public virtual TEntity GetFirst() if (lstObjs != null && lstObjs.Count > 0) { - return lstObjs.FirstOrDefault(); + return lstObjs.FirstOrDefault(); } else { @@ -1338,7 +1231,7 @@ public virtual TEntity GetFirst() public virtual Task GetFirstAsync() { //No Result Found. So Order the Entity with key column and select next Entity - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); var ordByExp = GetOrderByExpression(keyColums); @@ -1347,7 +1240,7 @@ public virtual Task GetFirstAsync() if (lstObjs != null && lstObjs.Count > 0) { - return Task.Factory.StartNew(() => lstObjs.FirstOrDefault()); + return Task.Factory.StartNew(() => lstObjs.FirstOrDefault()); } else { @@ -1362,7 +1255,7 @@ public virtual Task GetFirstAsync() public virtual TEntity GetLast() { //No Result Found. So Order the Entity with key column and select next Entity - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); var ordByExp = GetOrderByExpression(keyColums,true); @@ -1371,7 +1264,7 @@ public virtual TEntity GetLast() if (lstObjs != null && lstObjs.Count > 0) { - return lstObjs.FirstOrDefault(); + return lstObjs.FirstOrDefault(); } else { @@ -1386,7 +1279,7 @@ public virtual TEntity GetLast() public virtual Task GetLastAsync() { //No Result Found. So Order the Entity with key column and select next Entity - IEntityType entityType = _dbContext.Model.FindEntityType(typeof(TEntity).ToString()); + IEntityType entityType = DbContext.Model.FindEntityType(typeof(TEntity).ToString()); IKey key = entityType.FindPrimaryKey(); List keyColums = key.Properties.Select(q => q.Name).ToList(); var ordByExp = GetOrderByExpression(keyColums,true); @@ -1395,7 +1288,7 @@ public virtual Task GetLastAsync() if (lstObjs != null && lstObjs.Count > 0) { - return Task.Factory.StartNew(() => lstObjs.FirstOrDefault()); + return Task.Factory.StartNew(() => lstObjs.FirstOrDefault()); } else { @@ -1458,7 +1351,7 @@ public static Func, IOrderedQueryable> GetOrderBy(string ord ParameterExpression argQueryable = Expression.Parameter(typeQueryable, "p"); var outerExpression = Expression.Lambda(argQueryable, argQueryable); string[] props = orderColumn.Split('.'); - IQueryable query = new List().AsQueryable(); + IQueryable query = new List().AsQueryable(); Type type = typeof(T); ParameterExpression arg = Expression.Parameter(type, "x"); @@ -1800,7 +1693,7 @@ public async Task> GetAllAsync(Expression, IIncludableQueryable> include = null, bool disableTracking = true, bool ignoreQueryFilters = false) { - IQueryable query = _dbSet; + IQueryable query = DbSet; if (disableTracking) { @@ -1839,12 +1732,9 @@ public async Task> GetAllAsync(Expression /// The entity. /// /// The entity state. - public void ChangeEntityState(TEntity entity, EntityState state) - { - _dbContext.Entry(entity).State = state; - } + public void ChangeEntityState(TEntity entity, EntityState state) => DbContext.Entry(entity).State = state; - ValueTask IRepository.FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues); - ValueTask IRepository.FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken); + ValueTask IRepository.FindAsync(params object[] keyValues) => DbSet.FindAsync(keyValues); + ValueTask IRepository.FindAsync(object[] keyValues, CancellationToken cancellationToken) => DbSet.FindAsync(keyValues, cancellationToken); } } diff --git a/src/UnitOfWork/UnitOfWork.cs b/src/UnitOfWork/UnitOfWork.cs index 770d078..502ea27 100644 --- a/src/UnitOfWork/UnitOfWork.cs +++ b/src/UnitOfWork/UnitOfWork.cs @@ -22,24 +22,20 @@ namespace Arch.EntityFrameworkCore.UnitOfWork /// The type of the db context. public class UnitOfWork : IRepositoryFactory, IUnitOfWork, IUnitOfWork where TContext : DbContext { - private readonly TContext _context; - private bool disposed = false; - private Dictionary repositories; + private bool _disposed; + private Dictionary _repositories; /// /// Initializes a new instance of the class. /// /// The context. - public UnitOfWork(TContext context) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); - } + public UnitOfWork(TContext context) => DbContext = context ?? throw new ArgumentNullException(nameof(context)); /// /// Gets the db context. /// /// The instance of type . - public TContext DbContext => _context; + public TContext DbContext { get; } /// /// Changes the database name. This require the databases in the same machine. NOTE: This only work for MySQL right now. @@ -50,7 +46,7 @@ public UnitOfWork(TContext context) /// public void ChangeDatabase(string database) { - var connection = _context.Database.GetDbConnection(); + var connection = DbContext.Database.GetDbConnection(); if (connection.State.HasFlag(ConnectionState.Open)) { connection.ChangeDatabase(database); @@ -62,7 +58,7 @@ public void ChangeDatabase(string database) } // Following code only working for mysql. - var items = _context.Model.GetEntityTypes(); + var items = DbContext.Model.GetEntityTypes(); foreach (var item in items) { if (item is IConventionEntityType entityType) @@ -75,20 +71,20 @@ public void ChangeDatabase(string database) /// /// Gets the specified repository for the . /// - /// True if providing custom repositry + /// True if providing custom repository /// The type of the entity. /// An instance of type inherited from interface. public IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class { - if (repositories == null) + if (_repositories == null) { - repositories = new Dictionary(); + _repositories = new Dictionary(); } - // what's the best way to support custom reposity? + // what's the best way to support custom repository? if (hasCustomRepository) { - var customRepo = _context.GetService>(); + var customRepo = DbContext.GetService>(); if (customRepo != null) { return customRepo; @@ -96,12 +92,12 @@ public IRepository GetRepository(bool hasCustomRepository = fa } var type = typeof(TEntity); - if (!repositories.ContainsKey(type)) + if (!_repositories.ContainsKey(type)) { - repositories[type] = new Repository(_context); + _repositories[type] = new Repository(DbContext); } - return (IRepository)repositories[type]; + return (IRepository)_repositories[type]; } /// @@ -110,7 +106,7 @@ public IRepository GetRepository(bool hasCustomRepository = fa /// The raw SQL. /// The parameters. /// The number of state entities written to database. - public int ExecuteSqlCommand(string sql, params object[] parameters) => _context.Database.ExecuteSqlRaw(sql, parameters); + public int ExecuteSqlCommand(string sql, params object[] parameters) => DbContext.Database.ExecuteSqlRaw(sql, parameters); /// /// Executes the specified raw SQL command. @@ -120,7 +116,7 @@ public IRepository GetRepository(bool hasCustomRepository = fa /// The DataTable. public DataTable ExecuteDtSqlCommand(string sql, params object[] parameters) { - SqlConnection conn = (SqlConnection) _context.Database.GetDbConnection(); + SqlConnection conn = (SqlConnection) DbContext.Database.GetDbConnection(); SqlCommand cmd = new SqlCommand(sql, conn); cmd.CommandTimeout = 0; @@ -153,17 +149,14 @@ public DataTable ExecuteDtSqlCommand(string sql, params object[] parameters) /// The raw SQL. /// The parameters. /// An that contains elements that satisfy the condition specified by raw SQL. - public IQueryable FromSql(string sql, params object[] parameters) where TEntity : class => _context.Set().FromSqlRaw(sql, parameters); + public IQueryable FromSql(string sql, params object[] parameters) where TEntity : class => DbContext.Set().FromSqlRaw(sql, parameters); /// /// Starts Databaselevel Transaction /// /// The IsolationLevel /// Transaction Context - public IDbContextTransaction BeginTransaction(System.Data.IsolationLevel isolation = System.Data.IsolationLevel.ReadCommitted) - { - return _context.Database.BeginTransaction(isolation); - } + public IDbContextTransaction BeginTransaction(System.Data.IsolationLevel isolation = System.Data.IsolationLevel.ReadCommitted) => DbContext.Database.BeginTransaction(isolation); /// /// Saves all changes made in this context to the database. @@ -174,10 +167,10 @@ public int SaveChanges(bool ensureAutoHistory = false) { if (ensureAutoHistory) { - _context.EnsureAutoHistory(); + DbContext.EnsureAutoHistory(); } - return _context.SaveChanges(); + return DbContext.SaveChanges(); } /// @@ -189,10 +182,10 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false) { if (ensureAutoHistory) { - _context.EnsureAutoHistory(); + DbContext.EnsureAutoHistory(); } - return await _context.SaveChangesAsync(); + return await DbContext.SaveChangesAsync(); } /// @@ -259,33 +252,23 @@ public void Dispose() /// The disposing. protected virtual void Dispose(bool disposing) { - if (!disposed) + if (!_disposed) { if (disposing) { // clear repositories - if (repositories != null) - { - repositories.Clear(); - } + _repositories?.Clear(); // dispose the db context. - _context.Dispose(); + DbContext.Dispose(); } } - disposed = true; + _disposed = true; } - public void TrackGraph(object rootEntity, Action callback) - { - _context.ChangeTracker.TrackGraph(rootEntity, callback); - } - - IDbContextTransaction IUnitOfWork.BeginTransaction(System.Data.IsolationLevel isolation) - { - return _context.Database.BeginTransaction(isolation); - } + public void TrackGraph(object rootEntity, Action callback) => DbContext.ChangeTracker.TrackGraph(rootEntity, callback); + IDbContextTransaction IUnitOfWork.BeginTransaction(System.Data.IsolationLevel isolation) => DbContext.Database.BeginTransaction(isolation); } } diff --git a/src/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs b/src/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs index 433ea00..6335145 100644 --- a/src/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs +++ b/src/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs @@ -22,7 +22,7 @@ public static class UnitOfWorkServiceCollectionExtensions public static IServiceCollection AddUnitOfWork(this IServiceCollection services) where TContext : DbContext { services.AddScoped>(); - // Following has a issue: IUnitOfWork cannot support multiple dbcontext/database, + // Following has a issue: IUnitOfWork cannot support multiple DbContext/Database, // that means cannot call AddUnitOfWork multiple times. // Solution: check IUnitOfWork whether or null services.AddScoped>(); @@ -104,7 +104,7 @@ public static IServiceCollection AddUnitOfWork. /// /// The type of the entity. - /// The type of the custom repositry. + /// The type of the custom repository. /// The to add services to. /// The same service collection so that multiple calls can be chained. public static IServiceCollection AddCustomRepository(this IServiceCollection services) From 83700a9f7a5b4b37eedd7a48c91bde32a027229c Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 15:23:25 +0100 Subject: [PATCH 36/40] Clean host app --- .../Controllers/ValuesController.cs | 254 +++++++++--------- .../Models/BlogggingContext.cs | 5 +- .../Models/CustomBlogRepository.cs | 9 +- samples/UnitOfWork.Host/Startup.cs | 1 - 4 files changed, 130 insertions(+), 139 deletions(-) diff --git a/samples/UnitOfWork.Host/Controllers/ValuesController.cs b/samples/UnitOfWork.Host/Controllers/ValuesController.cs index 5fc41ad..83d0052 100644 --- a/samples/UnitOfWork.Host/Controllers/ValuesController.cs +++ b/samples/UnitOfWork.Host/Controllers/ValuesController.cs @@ -13,7 +13,7 @@ namespace Arch.EntityFrameworkCore.UnitOfWork.Host.Controllers public class ValuesController : Controller { private readonly IUnitOfWork _unitOfWork; - private ILogger _logger; + private readonly ILogger _logger; // 1. IRepositoryFactory used for readonly scenario; // 2. IUnitOfWork used for read/write scenario; @@ -27,135 +27,137 @@ public ValuesController(IUnitOfWork unitOfWork, ILogger logger var repo = _unitOfWork.GetRepository(hasCustomRepository: true); if (repo.Count() == 0) { - repo.Insert(new Blog - { - Id = 1, - Url = "/a/" + 1, - Title = $"a{1}", - Posts = new List{ - new Post - { - Id = 1, - Title = "A", - Content = "A's content", - Comments = new List - { - new Comment - { - Id = 1, - Title = "A", - Content = "A's content", - }, - new Comment - { - Id = 2, - Title = "b", - Content = "b's content", - }, - new Comment - { - Id = 3, - Title = "c", - Content = "c's content", - } - }, - }, - new Post - { - Id = 2, - Title = "B", - Content = "B's content", - Comments = new List - { - new Comment - { - Id = 4, - Title = "A", - Content = "A's content", - }, - new Comment - { - Id = 5, - Title = "b", - Content = "b's content", - }, - new Comment - { - Id = 6, - Title = "c", - Content = "c's content", - } - }, - }, - new Post - { - Id = 3, - Title = "C", - Content = "C's content", - Comments = new List - { - new Comment - { - Id = 7, - Title = "A", - Content = "A's content", - }, - new Comment - { - Id = 8, - Title = "b", - Content = "b's content", - }, - new Comment - { - Id = 9, - Title = "c", - Content = "c's content", - } - }, - }, - new Post - { - Id = 4, - Title = "D", - Content = "D's content", - Comments = new List - { - new Comment - { - Id = 10, - Title = "A", - Content = "A's content", - }, - new Comment - { - Id = 11, - Title = "b", - Content = "b's content", - }, - new Comment - { - Id = 12, - Title = "c", - Content = "c's content", - } - }, - } - }, - }); + SeedInitialEntities(repo); _unitOfWork.SaveChanges(); } } + private static void SeedInitialEntities(IRepository repo) + => repo.Insert(new Blog + { + Id = 1, + Url = "/a/" + 1, + Title = $"a{1}", + Posts = new List{ + new Post + { + Id = 1, + Title = "A", + Content = "A's content", + Comments = new List + { + new Comment + { + Id = 1, + Title = "A", + Content = "A's content", + }, + new Comment + { + Id = 2, + Title = "b", + Content = "b's content", + }, + new Comment + { + Id = 3, + Title = "c", + Content = "c's content", + } + }, + }, + new Post + { + Id = 2, + Title = "B", + Content = "B's content", + Comments = new List + { + new Comment + { + Id = 4, + Title = "A", + Content = "A's content", + }, + new Comment + { + Id = 5, + Title = "b", + Content = "b's content", + }, + new Comment + { + Id = 6, + Title = "c", + Content = "c's content", + } + }, + }, + new Post + { + Id = 3, + Title = "C", + Content = "C's content", + Comments = new List + { + new Comment + { + Id = 7, + Title = "A", + Content = "A's content", + }, + new Comment + { + Id = 8, + Title = "b", + Content = "b's content", + }, + new Comment + { + Id = 9, + Title = "c", + Content = "c's content", + } + }, + }, + new Post + { + Id = 4, + Title = "D", + Content = "D's content", + Comments = new List + { + new Comment + { + Id = 10, + Title = "A", + Content = "A's content", + }, + new Comment + { + Id = 11, + Title = "b", + Content = "b's content", + }, + new Comment + { + Id = 12, + Title = "c", + Content = "c's content", + } + }, + } + }, + }); + // GET api/values [HttpGet] - public async Task> Get() - { - return await _unitOfWork.GetRepository().GetAllAsync(include: source => source.Include(blog => blog.Posts).ThenInclude(post => post.Comments)); - } + public async Task> Get() + => await _unitOfWork.GetRepository() + .GetAllAsync(include: source => source.Include(blog => blog.Posts).ThenInclude(post => post.Comments)); // GET api/values/Page/5/10 - [HttpGet("Page/{pageIndex}/{pageSize}")] + [HttpGet("Page/{pageIndex:int}/{pageSize:int}")] public async Task> Get(int pageIndex, int pageSize) { // projection @@ -184,11 +186,9 @@ public async Task> Get(string term) } // GET api/values/4 - [HttpGet("{id}")] - public async Task Get(int id) - { - return await _unitOfWork.GetRepository().FindAsync(id); - } + [HttpGet("{id:int}")] + public async Task Get(int id) + => await _unitOfWork.GetRepository().FindAsync(id); // POST api/values [HttpPost] diff --git a/samples/UnitOfWork.Host/Models/BlogggingContext.cs b/samples/UnitOfWork.Host/Models/BlogggingContext.cs index 1099928..e5fc31d 100644 --- a/samples/UnitOfWork.Host/Models/BlogggingContext.cs +++ b/samples/UnitOfWork.Host/Models/BlogggingContext.cs @@ -12,10 +12,7 @@ public BloggingContext(DbContextOptions options) public DbSet Blogs { get; set; } public DbSet Posts { get; set; } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.EnableAutoHistory(null); - } + protected override void OnModelCreating(ModelBuilder modelBuilder) => modelBuilder.EnableAutoHistory(null); } public class Blog diff --git a/samples/UnitOfWork.Host/Models/CustomBlogRepository.cs b/samples/UnitOfWork.Host/Models/CustomBlogRepository.cs index b33e932..571954d 100644 --- a/samples/UnitOfWork.Host/Models/CustomBlogRepository.cs +++ b/samples/UnitOfWork.Host/Models/CustomBlogRepository.cs @@ -1,14 +1,9 @@ -using Arch.EntityFrameworkCore.UnitOfWork; -using Arch.EntityFrameworkCore.UnitOfWork.Host.Models; -using Microsoft.EntityFrameworkCore; - -namespace Host.Models +namespace Arch.EntityFrameworkCore.UnitOfWork.Host.Models { - public class CustomBlogRepository : Repository, IRepository + public class CustomBlogRepository : Repository { public CustomBlogRepository(BloggingContext dbContext) : base(dbContext) { - } } } diff --git a/samples/UnitOfWork.Host/Startup.cs b/samples/UnitOfWork.Host/Startup.cs index 081fce3..a29ae19 100644 --- a/samples/UnitOfWork.Host/Startup.cs +++ b/samples/UnitOfWork.Host/Startup.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; -using Host.Models; namespace Arch.EntityFrameworkCore.UnitOfWork.Host { From 8521dfd796dea5c3236dfa29cfcd225da54e3ad2 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 15:24:10 +0100 Subject: [PATCH 37/40] Remove old commented out code. --- src/UnitOfWork/Repository.cs | 90 ------------------------------------ 1 file changed, 90 deletions(-) diff --git a/src/UnitOfWork/Repository.cs b/src/UnitOfWork/Repository.cs index 8d13755..1acabba 100644 --- a/src/UnitOfWork/Repository.cs +++ b/src/UnitOfWork/Repository.cs @@ -1586,96 +1586,6 @@ public static Expression> GetWhereConditionExpression(IKey key, } #endregion - /* - #region Logic33 - public static IEnumerable BuildOrderBys( - this IEnumerable source, - IEnumerable properties) - { - if (properties == null || properties.Count() == 0) return source; - - var typeOfT = typeof(T); - - Type t = typeOfT; - - IOrderedEnumerable result = null; - var thenBy = false; - - foreach (var item in properties) - { - var oExpr = Expression.Parameter(typeOfT, "o"); - - MemberExpression prop = GetMemberExpression(oExpr, item); - var propertyInfo = (PropertyInfo)prop.Member; - var propertyType = propertyInfo.PropertyType; - var isAscending = true; - - if (thenBy) - { - var prevExpr = Expression.Parameter(typeof(IOrderedEnumerable), "prevExpr"); - var expr1 = Expression.Lambda, IOrderedEnumerable>>( - Expression.Call( - (isAscending ? thenByMethod : thenByDescendingMethod).MakeGenericMethod(typeOfT, propertyType), - prevExpr, - Expression.Lambda( - typeof(Func<,>).MakeGenericType(typeOfT, propertyType), - Expression.MakeMemberAccess(oExpr, propertyInfo), - oExpr) - ), - prevExpr) - .Compile(); - result = expr1(result); - } - else - { - var prevExpr = Expression.Parameter(typeof(IEnumerable), "prevExpr"); - var expr1 = Expression.Lambda, IOrderedEnumerable>>( - Expression.Call( - (isAscending ? orderByMethod : orderByDescendingMethod).MakeGenericMethod(typeOfT, propertyType), - prevExpr, - Expression.Lambda( - typeof(Func<,>).MakeGenericType(typeOfT, propertyType), - Expression.MakeMemberAccess(oExpr, propertyInfo), - oExpr) - ), - prevExpr) - .Compile(); - result = expr1(source); - thenBy = true; - } - } - return result; - } - - - private static MethodInfo orderByMethod = - MethodOf(() => Enumerable.OrderBy(default(IEnumerable), default(Func))) - .GetGenericMethodDefinition(); - - private static MethodInfo orderByDescendingMethod = - MethodOf(() => Enumerable.OrderByDescending(default(IEnumerable), default(Func))) - .GetGenericMethodDefinition(); - - private static MethodInfo thenByMethod = - MethodOf(() => Enumerable.ThenBy(default(IOrderedEnumerable), default(Func))) - .GetGenericMethodDefinition(); - - private static MethodInfo thenByDescendingMethod = - MethodOf(() => Enumerable.ThenByDescending(default(IOrderedEnumerable), default(Func))) - .GetGenericMethodDefinition(); - - public static MethodInfo MethodOf(Expression> method) - { - MethodCallExpression mce = (MethodCallExpression)method.Body; - MethodInfo mi = mce.Method; - return mi; - } - - - #endregion - - */ - /// /// Gets all entities. This method is not recommended /// From 191791dd6b9ed6e6bb37db2925128d9ca20f7494 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 15:33:03 +0100 Subject: [PATCH 38/40] Continue Improve Arch.EntityFrameworkCore.UnitOfWork.* code readability. Unify formatting, improve consistency. --- src/UnitOfWork/Repository.cs | 108 +++++++++++++++++------------------ 1 file changed, 52 insertions(+), 56 deletions(-) diff --git a/src/UnitOfWork/Repository.cs b/src/UnitOfWork/Repository.cs index 1acabba..797f02e 100644 --- a/src/UnitOfWork/Repository.cs +++ b/src/UnitOfWork/Repository.cs @@ -149,7 +149,7 @@ public IQueryable GetAll( } /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -200,7 +200,7 @@ public virtual IPagedList GetPagedList( } /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -255,7 +255,7 @@ public virtual Task> GetPagedListAsync( } /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// The selector for projection. /// A function to test each element for a condition. @@ -309,7 +309,7 @@ public virtual IPagedList GetPagedList( } /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// The selector for projection. /// A function to test each element for a condition. @@ -367,7 +367,7 @@ public virtual Task> GetPagedListAsync( } /// - /// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method default no-tracking query. + /// Gets the first or default entity based on a predicate, orderBy delegate and include delegate. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -448,14 +448,12 @@ public virtual async Task GetFirstOrDefaultAsync( { return await orderBy(query).FirstOrDefaultAsync(); } - else - { - return await query.FirstOrDefaultAsync(); - } + + return await query.FirstOrDefaultAsync(); } /// - /// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method default no-tracking query. + /// Gets the first or default entity based on a predicate, orderBy delegate and include delegate. This method default no-tracking query. /// /// The selector for projection. /// A function to test each element for a condition. @@ -499,10 +497,8 @@ public virtual TResult GetFirstOrDefault( { return orderBy(query).Select(selector).FirstOrDefault(); } - else - { - return query.Select(selector).FirstOrDefault(); - } + + return query.Select(selector).FirstOrDefault(); } /// @@ -539,10 +535,8 @@ public virtual async Task GetFirstOrDefaultAsync( { return await orderBy(query).Select(selector).FirstOrDefaultAsync(); } - else - { - return await query.Select(selector).FirstOrDefaultAsync(); - } + + return await query.Select(selector).FirstOrDefaultAsync(); } /// @@ -551,21 +545,24 @@ public virtual async Task GetFirstOrDefaultAsync( /// The raw SQL. /// The parameters. /// An that contains elements that satisfy the condition specified by raw SQL. - public virtual IQueryable FromSql(string sql, params object[] parameters) => DbSet.FromSqlRaw(sql, parameters); + public virtual IQueryable FromSql(string sql, params object[] parameters) + => DbSet.FromSqlRaw(sql, parameters); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The values of the primary key for the entity to be found. /// The found entity or null. - public virtual TEntity Find(params object[] keyValues) => DbSet.Find(keyValues); + public virtual TEntity Find(params object[] keyValues) + => DbSet.Find(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. /// /// The values of the primary key for the entity to be found. /// A that represents the asynchronous insert operation. - public virtual ValueTask FindAsync(params object[] keyValues) => DbSet.FindAsync(keyValues); + public virtual ValueTask FindAsync(params object[] keyValues) + => DbSet.FindAsync(keyValues); /// /// Finds an entity with the given primary key values. If found, is attached to the context and returned. If no entity is found, then null is returned. @@ -580,30 +577,16 @@ public virtual async Task GetFirstOrDefaultAsync( /// /// /// - public virtual int Count(Expression> predicate = null) - { - if (predicate == null) - { - return DbSet.Count(); - } - - return DbSet.Count(predicate); - } + public virtual int Count(Expression> predicate = null) + => predicate == null ? DbSet.Count() : DbSet.Count(predicate); /// /// Gets async the count based on a predicate. /// /// /// - public virtual async Task CountAsync(Expression> predicate = null) - { - if (predicate == null) - { - return await DbSet.CountAsync(); - } - - return await DbSet.CountAsync(predicate); - } + public virtual async Task CountAsync(Expression> predicate = null) + => predicate == null ? await DbSet.CountAsync() : await DbSet.CountAsync(predicate); /// /// Gets the long count based on a predicate. @@ -713,19 +696,22 @@ public async Task ExistsAsync(Expression> selector = n /// Inserts a new entity synchronously. /// /// The entity to insert. - public virtual TEntity Insert(TEntity entity) => DbSet.Add(entity).Entity; + public virtual TEntity Insert(TEntity entity) + => DbSet.Add(entity).Entity; /// /// Inserts a range of entities synchronously. /// /// The entities to insert. - public virtual void Insert(params TEntity[] entities) => DbSet.AddRange(entities); + public virtual void Insert(params TEntity[] entities) + => DbSet.AddRange(entities); /// /// Inserts a range of entities synchronously. /// /// The entities to insert. - public virtual void Insert(IEnumerable entities) => DbSet.AddRange(entities); + public virtual void Insert(IEnumerable entities) + => DbSet.AddRange(entities); /// /// Inserts a new entity asynchronously. @@ -746,7 +732,8 @@ public virtual ValueTask> InsertAsync(TEntity entity, Cance /// /// The entities to insert. /// A that represents the asynchronous insert operation. - public virtual Task InsertAsync(params TEntity[] entities) => DbSet.AddRangeAsync(entities); + public virtual Task InsertAsync(params TEntity[] entities) + => DbSet.AddRangeAsync(entities); /// /// Inserts a range of entities asynchronously. @@ -761,31 +748,36 @@ public virtual Task InsertAsync(IEnumerable entities, CancellationToken /// Updates the specified entity. /// /// The entity. - public virtual void Update(TEntity entity) => DbSet.Update(entity); + public virtual void Update(TEntity entity) + => DbSet.Update(entity); /// /// Updates the specified entity. /// /// The entity. - public virtual void UpdateAsync(TEntity entity) => DbSet.Update(entity); + public virtual void UpdateAsync(TEntity entity) + => DbSet.Update(entity); /// /// Updates the specified entities. /// /// The entities. - public virtual void Update(params TEntity[] entities) => DbSet.UpdateRange(entities); + public virtual void Update(params TEntity[] entities) + => DbSet.UpdateRange(entities); /// /// Updates the specified entities. /// /// The entities. - public virtual void Update(IEnumerable entities) => DbSet.UpdateRange(entities); + public virtual void Update(IEnumerable entities) + => DbSet.UpdateRange(entities); /// /// Deletes the specified entity. /// /// The entity to delete. - public virtual void Delete(TEntity entity) => DbSet.Remove(entity); + public virtual void Delete(TEntity entity) + => DbSet.Remove(entity); /// /// Deletes the entity by the specified primary key. @@ -817,19 +809,22 @@ public virtual void Delete(object id) /// Deletes the specified entities. /// /// The entities. - public virtual void Delete(params TEntity[] entities) => DbSet.RemoveRange(entities); + public virtual void Delete(params TEntity[] entities) + => DbSet.RemoveRange(entities); /// /// Deletes the specified entities. /// /// The entities. - public virtual void Delete(IEnumerable entities) => DbSet.RemoveRange(entities); + public virtual void Delete(IEnumerable entities) + => DbSet.RemoveRange(entities); /// /// Gets all entities. This method is not recommended /// /// The . - public async Task> GetAllAsync() => await DbSet.ToListAsync(); + public async Task> GetAllAsync() + => await DbSet.ToListAsync(); /// /// Gets all entities. This method is not recommended @@ -909,16 +904,17 @@ public virtual void InsertOrUpdate(TEntity entity) { if (ExistsUpdateTimestamp(entity, out var entityForUpdate)) { Update(entityForUpdate); - } else { + } + else { Insert(entity); } } - public virtual void InsertOrUpdate(IEnumerable entities) => - DbContext.BulkInsertOrUpdate(entities.ToList()); + public virtual void InsertOrUpdate(IEnumerable entities) + => DbContext.BulkInsertOrUpdate(entities.ToList()); /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. @@ -964,7 +960,7 @@ public virtual List GetList( } /// - /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query. + /// Gets the based on a predicate, orderBy delegate and page information. This method default no-tracking query. /// /// A function to test each element for a condition. /// A function to order elements. From 506418e00fed00329e6e2bb1ac6145b01946f9f6 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 15:36:53 +0100 Subject: [PATCH 39/40] Fix Repository API. Expression> selector cannot be omitted in aggregation functions. --- src/UnitOfWork/IRepository.cs | 48 ++++++++++++++++++++--------------- src/UnitOfWork/Repository.cs | 46 +++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/UnitOfWork/IRepository.cs b/src/UnitOfWork/IRepository.cs index 0ccdc4a..6f96596 100644 --- a/src/UnitOfWork/IRepository.cs +++ b/src/UnitOfWork/IRepository.cs @@ -341,66 +341,74 @@ Task> GetAllAsync(Expression> sel /// /// Gets the max based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - T Max(Expression> predicate = null, Expression> selector = null); + T Max(Expression> selector, Expression> predicate = null); /// /// Gets the async max based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - Task MaxAsync(Expression> predicate = null, Expression> selector = null); + Task MaxAsync(Expression> selector, Expression> predicate = null); /// /// Gets the min based on a predicate. /// - /// /// + /// /// decimal - T Min(Expression> predicate = null, Expression> selector = null); + T Min(Expression> selector, Expression> predicate = null); /// /// Gets the async min based on a predicate. /// - /// /// + /// /// decimal - Task MinAsync(Expression> predicate = null, Expression> selector = null); + Task MinAsync(Expression> selector, Expression> predicate = null); /// /// Gets the average based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - decimal Average (Expression> predicate = null, Expression> selector = null); + decimal Average(Expression> selector, Expression> predicate = null); /// - /// Gets the async average based on a predicate. - /// - /// - /// /// - /// decimal - Task AverageAsync(Expression> predicate = null, Expression> selector = null); + /// Gets the async average based on a predicate. + /// + /// + /// + /// /// + /// decimal + Task AverageAsync(Expression> selector, + Expression> predicate = null); /// /// Gets the sum based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - decimal Sum (Expression> predicate = null, Expression> selector = null); + decimal Sum(Expression> selector, Expression> predicate = null); /// /// Gets the async sum based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - Task SumAsync (Expression> predicate = null, Expression> selector = null); + Task SumAsync(Expression> selector, + Expression> predicate = null); /// /// Gets the Exists record based on a predicate. diff --git a/src/UnitOfWork/Repository.cs b/src/UnitOfWork/Repository.cs index 797f02e..2e35e3a 100644 --- a/src/UnitOfWork/Repository.cs +++ b/src/UnitOfWork/Repository.cs @@ -607,73 +607,87 @@ public virtual async Task LongCountAsync(Expression> p /// /// Gets the max based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - public virtual T Max(Expression> predicate = null, Expression> selector = null) + public virtual T Max(Expression> selector, Expression> predicate = null) => predicate == null ? DbSet.Max(selector) : DbSet.Where(predicate).Max(selector); /// /// Gets the async max based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - public virtual async Task MaxAsync(Expression> predicate = null, Expression> selector = null) + public virtual async Task MaxAsync(Expression> selector, + Expression> predicate = null) => predicate == null ? await DbSet.MaxAsync(selector) : await DbSet.Where(predicate).MaxAsync(selector); /// /// Gets the min based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - public virtual T Min(Expression> predicate = null, Expression> selector = null) + public virtual T Min(Expression> selector, Expression> predicate = null) => predicate == null ? DbSet.Min(selector) : DbSet.Where(predicate).Min(selector); /// /// Gets the async min based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - public virtual async Task MinAsync(Expression> predicate = null, Expression> selector = null) + public virtual async Task MinAsync(Expression> selector, + Expression> predicate = null) => predicate == null ? await DbSet.MinAsync(selector) : await DbSet.Where(predicate).MinAsync(selector); /// /// Gets the average based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - public virtual decimal Average(Expression> predicate = null, Expression> selector = null) + public virtual decimal Average(Expression> selector, + Expression> predicate = null) => predicate == null ? DbSet.Average(selector) : DbSet.Where(predicate).Average(selector); /// /// Gets the async average based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - public virtual async Task AverageAsync(Expression> predicate = null, Expression> selector = null) + public virtual async Task AverageAsync(Expression> selector, + Expression> predicate = null) => predicate == null ? await DbSet.AverageAsync(selector) : await DbSet.Where(predicate).AverageAsync(selector); /// /// Gets the sum based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - public virtual decimal Sum(Expression> predicate = null, Expression> selector = null) + public virtual decimal Sum(Expression> selector, + Expression> predicate = null) => predicate == null ? DbSet.Sum(selector) : DbSet.Where(predicate).Sum(selector); /// /// Gets the async sum based on a predicate. /// + /// /// - /// /// + /// /// /// decimal - public virtual async Task SumAsync(Expression> predicate = null, Expression> selector = null) + public virtual async Task SumAsync(Expression> selector, + Expression> predicate = null) => predicate == null ? await DbSet.SumAsync(selector) : await DbSet.Where(predicate).SumAsync(selector); /// From 6915d2f10bd1e187e3c19a3e43e4230d2cdbb472 Mon Sep 17 00:00:00 2001 From: Jan Pluskal Date: Thu, 27 Jan 2022 15:48:46 +0100 Subject: [PATCH 40/40] Make Repository implement IAsyncDisposable --- src/UnitOfWork/IUnitOfWork.cs | 2 +- src/UnitOfWork/UnitOfWork.cs | 36 ++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/UnitOfWork/IUnitOfWork.cs b/src/UnitOfWork/IUnitOfWork.cs index af4fee0..06fb276 100644 --- a/src/UnitOfWork/IUnitOfWork.cs +++ b/src/UnitOfWork/IUnitOfWork.cs @@ -16,7 +16,7 @@ namespace Arch.EntityFrameworkCore.UnitOfWork /// /// Defines the interface(s) for unit of work. /// - public interface IUnitOfWork : IDisposable + public interface IUnitOfWork : IDisposable, IAsyncDisposable { /// /// Changes the database name. This require the databases in the same machine. NOTE: This only work for MySQL right now. diff --git a/src/UnitOfWork/UnitOfWork.cs b/src/UnitOfWork/UnitOfWork.cs index 502ea27..87da947 100644 --- a/src/UnitOfWork/UnitOfWork.cs +++ b/src/UnitOfWork/UnitOfWork.cs @@ -20,7 +20,7 @@ namespace Arch.EntityFrameworkCore.UnitOfWork /// Represents the default implementation of the and interface. /// /// The type of the db context. - public class UnitOfWork : IRepositoryFactory, IUnitOfWork, IUnitOfWork where TContext : DbContext + public class UnitOfWork : IRepositoryFactory, IUnitOfWork where TContext : DbContext { private bool _disposed; private Dictionary _repositories; @@ -233,7 +233,10 @@ public async Task SaveChangesAsync(Transaction transaction, bool ensureAuto return count; } + + public void TrackGraph(object rootEntity, Action callback) => DbContext.ChangeTracker.TrackGraph(rootEntity, callback); + IDbContextTransaction IUnitOfWork.BeginTransaction(System.Data.IsolationLevel isolation) => DbContext.Database.BeginTransaction(isolation); /// @@ -266,9 +269,36 @@ protected virtual void Dispose(bool disposing) _disposed = true; } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public async ValueTask DisposeAsync() + { + await DisposeAsync(true); - public void TrackGraph(object rootEntity, Action callback) => DbContext.ChangeTracker.TrackGraph(rootEntity, callback); + GC.SuppressFinalize(this); + } - IDbContextTransaction IUnitOfWork.BeginTransaction(System.Data.IsolationLevel isolation) => DbContext.Database.BeginTransaction(isolation); + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// The disposing. + protected virtual async ValueTask DisposeAsync(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + // clear repositories + _repositories?.Clear(); + + // dispose the db context. + await DbContext.DisposeAsync(); + } + } + + _disposed = true; + } } }