diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..fe1152b
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,30 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
+!**/.gitignore
+!.git/HEAD
+!.git/config
+!.git/packed-refs
+!.git/refs/heads/**
\ No newline at end of file
diff --git a/EmailLib/EmailConfiguration.cs b/EmailLib/EmailConfiguration.cs
new file mode 100644
index 0000000..5d5c669
--- /dev/null
+++ b/EmailLib/EmailConfiguration.cs
@@ -0,0 +1,12 @@
+namespace TechHelper.Features
+{
+ public static class EmailConfiguration
+ {
+ public static string EmailFrom { get; set; } = "1468441589@qq.com";
+ public static string Password { get; set; } = "pfxhtoztjimtbahc";
+ public static string SubDescribe { get; set; } = "这是你的验证凭证";
+ public static string SmtpHost { get; set; } = "smtp.qq.com";
+ public static int SmtpPort { get; set; } = 587;
+
+ }
+}
diff --git a/EmailLib/EmailLib.csproj b/EmailLib/EmailLib.csproj
new file mode 100644
index 0000000..c1c0809
--- /dev/null
+++ b/EmailLib/EmailLib.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/EmailLib/EmailTemap.cs b/EmailLib/EmailTemap.cs
new file mode 100644
index 0000000..e442cf6
--- /dev/null
+++ b/EmailLib/EmailTemap.cs
@@ -0,0 +1,109 @@
+namespace Email
+{
+ public class EmailTemap
+ {
+ public static string _email = @"
+
+
+
+
+ {{AppName}} - Verification Code
+
+
+
+
+
+
您的验证码
+
+
您好,
+
+
您收到这封邮件是因为您请求验证您在 {{AppName}} 的邮箱地址。
+
请使用以下验证码完成操作:
+
+
+ {{VerificationCode}}
+
+
请在应用程序或网站中输入此验证码。
+
为了您的账户安全,请勿将此验证码透露给任何人。
+
此验证码在 {{ExpirationMinutes}} 分钟内有效。
+
如果您没有进行此操作,请忽略本邮件。
+
+
这是一封自动发送的邮件,请勿直接回复。
+
© {{AppName}}
+
支持: Tech Helper
+
+
+
+";
+
+
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/EmailLib/IEmailSender.cs b/EmailLib/IEmailSender.cs
new file mode 100644
index 0000000..c4674b3
--- /dev/null
+++ b/EmailLib/IEmailSender.cs
@@ -0,0 +1,18 @@
+namespace TechHelper.Features
+{
+ public interface IEmailSender
+ {
+ ///
+ /// 使用 MailKit 通过邮箱发送邮件
+ ///
+ /// 收件人邮箱地址
+ /// 邮件主题
+ /// 邮件正文 (支持 HTML)
+ ///
+ public Task SendEmailAsync(string toEmail,string subject, string body);
+
+ public Task SendEmailAsync(string toEmail, string body);
+
+ public Task SendEmailAuthcodeAsync(string toEmail, string authCode);
+ }
+}
diff --git a/EmailLib/QEmailSender.cs b/EmailLib/QEmailSender.cs
new file mode 100644
index 0000000..d3e7b46
--- /dev/null
+++ b/EmailLib/QEmailSender.cs
@@ -0,0 +1,79 @@
+using MailKit.Net.Smtp;
+using MailKit.Security;
+using MimeKit;
+using Email;
+using System.Text;
+
+namespace TechHelper.Features
+{
+ public class QEmailSender : IEmailSender
+ {
+ public async Task SendEmailAsync(string toEmail, string subject, string body)
+ {
+ var secureSocketOption = SecureSocketOptions.StartTls;
+ try
+ {
+ var message = new MimeMessage();
+ message.From.Add(new MailboxAddress("TechHelper", EmailConfiguration.EmailFrom));
+ message.To.Add(new MailboxAddress("", toEmail));
+ message.Subject = subject;
+ message.Body = new TextPart(MimeKit.Text.TextFormat.Html)
+ {
+ Text = body
+ };
+
+
+ using (var client = new SmtpClient())
+ {
+ await client.ConnectAsync(EmailConfiguration.SmtpHost, EmailConfiguration.SmtpPort, secureSocketOption);
+
+ // 移除 XOAUTH2 认证机制,避免某些环境下出现异常
+ // 如果不加这行,某些情况下可能会遇到 MailKit.Security.AuthenticationException: XOAUTH2 is not supported
+ client.AuthenticationMechanisms.Remove("XOAUTH2");
+
+ await client.AuthenticateAsync(EmailConfiguration.EmailFrom, EmailConfiguration.Password);
+ await client.SendAsync(message);
+ await client.DisconnectAsync(true);
+ }
+
+ Console.WriteLine("邮件发送成功 (QQ 邮箱)!");
+ }
+ catch (TypeInitializationException ex)
+ {
+ Console.WriteLine("捕获到 SmtpClient 的 TypeInitializationException!");
+ Console.WriteLine($"异常消息: {ex.Message}");
+
+ Exception? inner = ex.InnerException; // 获取第一层内部异常
+ int innerCount = 1;
+ while (inner != null)
+ {
+ Console.WriteLine($"--> 内部异常 {innerCount}: {inner.GetType().Name}");
+ Console.WriteLine($"--> 内部异常消息: {inner.Message}");
+ inner = inner.InnerException; // 获取下一层内部异常
+ innerCount++;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"捕获到其他类型的异常: {ex.GetType().Name}");
+ Console.WriteLine($"异常消息: {ex.Message}");
+ }
+ }
+
+ public async Task SendEmailAsync(string toEmail , string body)
+ {
+ await SendEmailAsync(toEmail, EmailConfiguration.SubDescribe, body);
+ }
+
+ public async Task SendEmailAuthcodeAsync(string toEmail, string authCode)
+ {
+ string htmlTemplateString = EmailTemap._email;
+ string htmlBody = htmlTemplateString
+ .Replace("{{AppName}}", "TechHelper")
+ .Replace("{{VerificationCode}}", authCode)
+ .Replace("{{ExpirationMinutes}}", "30")
+ .Replace("{{SupportEmail}}", "TechHelper");
+ await SendEmailAsync(toEmail, htmlBody);
+ }
+ }
+}
diff --git a/Entities/Configuration/ApiConfiguration.cs b/Entities/Configuration/ApiConfiguration.cs
new file mode 100644
index 0000000..9f9582a
--- /dev/null
+++ b/Entities/Configuration/ApiConfiguration.cs
@@ -0,0 +1,7 @@
+namespace Entities.Configuration
+{
+ public class ApiConfiguration
+ {
+ public string? BaseAddress { get; set; } /*= "http://localhost:5099";*/
+ }
+}
diff --git a/Entities/Configuration/JwtConfiguration.cs b/Entities/Configuration/JwtConfiguration.cs
new file mode 100644
index 0000000..86b6ad1
--- /dev/null
+++ b/Entities/Configuration/JwtConfiguration.cs
@@ -0,0 +1,10 @@
+namespace Entities.Configuration
+{
+ public class JwtConfiguration
+ {
+ public string? SecurityKey { get; set; }
+ public string? ValidIssuer { get; set; }
+ public string? ValidAudience { get; set; }
+ public int ExpiryInMinutes { get; set; }
+ }
+}
diff --git a/Entities/Context/IPagedList.cs b/Entities/Context/IPagedList.cs
new file mode 100644
index 0000000..1318bfe
--- /dev/null
+++ b/Entities/Context/IPagedList.cs
@@ -0,0 +1,57 @@
+namespace SharedDATA.Context
+{
+
+ using System.Collections.Generic;
+ ///
+ /// Provides the interface(s) for paged list of any type.
+ /// 为任何类型的分页列表提供接口
+ ///
+ /// The type for paging.分页的类型
+ public interface IPagedList
+ {
+ ///
+ /// Gets the index start value.
+ /// 获取索引起始值
+ ///
+ /// The index start value.
+ int IndexFrom { get; }
+ ///
+ /// Gets the page index (current).
+ /// 获取页索引(当前)
+ ///
+ int PageIndex { get; }
+ ///
+ /// Gets the page size.
+ /// 获取页面大小
+ ///
+ int PageSize { get; }
+ ///
+ /// Gets the total count of the list of type
+ /// 获取类型列表的总计数
+ ///
+ int TotalCount { get; }
+ ///
+ /// Gets the total pages.
+ /// 获取页面总数
+ ///
+ int TotalPages { get; }
+ ///
+ /// Gets the current page items.
+ /// 获取当前页项
+ ///
+ IList Items { get; }
+ ///
+ /// Gets the has previous page.
+ /// 获取前一页
+ ///
+ /// The has previous page.
+ bool HasPreviousPage { get; }
+
+ ///
+ /// Gets the has next page.
+ /// 获取下一页
+ ///
+ /// The has next page.
+ bool HasNextPage { get; }
+ }
+}
diff --git a/Entities/Context/PagedList.cs b/Entities/Context/PagedList.cs
new file mode 100644
index 0000000..fe70f31
--- /dev/null
+++ b/Entities/Context/PagedList.cs
@@ -0,0 +1,238 @@
+namespace SharedDATA.Context
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ ///
+ /// Represents the default implementation of the interface.
+ ///
+ /// The type of the data to page 页类型的数据
+ public class PagedList : IPagedList
+ {
+ ///
+ /// Gets or sets the index of the page.
+ /// 获得页的起始页
+ ///
+ /// The index of the page.
+ public int PageIndex { get; set; }
+ ///
+ /// Gets or sets the size of the page.
+ /// 获得页大小
+ ///
+ /// The size of the page.
+ public int PageSize { get; set; }
+ ///
+ /// Gets or sets the total count.
+ /// 获得总数
+ ///
+ /// The total count.
+ public int TotalCount { get; set; }
+ ///
+ /// Gets or sets the total pages.
+ /// 获得总页数
+ ///
+ /// The total pages.
+ public int TotalPages { get; set; }
+ ///
+ /// Gets or sets the index from.
+ /// 从索引起
+ ///
+ /// The index from.
+ public int IndexFrom { get; set; }
+
+ ///
+ /// Gets or sets the items.
+ /// 数据
+ ///
+ /// The items.
+ public IList Items { get; set; }
+
+ ///
+ /// Gets the has previous page.
+ /// 获取前一页
+ ///
+ /// The has previous page.
+ public bool HasPreviousPage => PageIndex - IndexFrom > 0;
+
+ ///
+ /// Gets the has next page.
+ /// 获取下一页
+ ///
+ /// The has next page.
+ public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The source.
+ /// The index of the page.
+ /// The size of the page.
+ /// The index from.
+ public PagedList(IEnumerable source, int pageIndex, int pageSize, int indexFrom)
+ {
+ if (indexFrom > pageIndex)
+ {
+ throw new ArgumentException($"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex");
+ }
+
+ if (source is IQueryable querable)
+ {
+ PageIndex = pageIndex;
+ PageSize = pageSize;
+ IndexFrom = indexFrom;
+ TotalCount = querable.Count();
+ TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
+
+ Items = querable.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToList();
+ }
+ else
+ {
+ PageIndex = pageIndex;
+ PageSize = pageSize;
+ IndexFrom = indexFrom;
+ TotalCount = source.Count();
+ TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
+
+ Items = source.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToList();
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PagedList() => Items = new T[0];
+ }
+
+
+ ///
+ /// Provides the implementation of the and converter.
+ ///
+ /// The type of the source.
+ /// The type of the result.
+ public class PagedList : IPagedList
+ {
+ ///
+ /// Gets the index of the page.
+ ///
+ /// The index of the page.
+ public int PageIndex { get; }
+ ///
+ /// Gets the size of the page.
+ ///
+ /// The size of the page.
+ public int PageSize { get; }
+ ///
+ /// Gets the total count.
+ ///
+ /// The total count.
+ public int TotalCount { get; }
+ ///
+ /// Gets the total pages.
+ ///
+ /// The total pages.
+ public int TotalPages { get; }
+ ///
+ /// Gets the index from.
+ ///
+ /// The index from.
+ public int IndexFrom { get; }
+
+ ///
+ /// Gets the items.
+ ///
+ /// The items.
+ public IList Items { get; }
+
+ ///
+ /// Gets the has previous page.
+ ///
+ /// The has previous page.
+ public bool HasPreviousPage => PageIndex - IndexFrom > 0;
+
+ ///
+ /// Gets the has next page.
+ ///
+ /// The has next page.
+ public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The source.
+ /// The converter.
+ /// The index of the page.
+ /// The size of the page.
+ /// The index from.
+ public PagedList(IEnumerable source, Func, IEnumerable> converter, int pageIndex, int pageSize, int indexFrom)
+ {
+ if (indexFrom > pageIndex)
+ {
+ throw new ArgumentException($"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex");
+ }
+
+ if (source is IQueryable querable)
+ {
+ PageIndex = pageIndex;
+ PageSize = pageSize;
+ IndexFrom = indexFrom;
+ TotalCount = querable.Count();
+ TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
+
+ var items = querable.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToArray();
+
+ Items = new List(converter(items));
+ }
+ else
+ {
+ PageIndex = pageIndex;
+ PageSize = pageSize;
+ IndexFrom = indexFrom;
+ TotalCount = source.Count();
+ TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
+
+ var items = source.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToArray();
+
+ Items = new List(converter(items));
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The source.
+ /// The converter.
+ public PagedList(IPagedList source, Func, IEnumerable> converter)
+ {
+ PageIndex = source.PageIndex;
+ PageSize = source.PageSize;
+ IndexFrom = source.IndexFrom;
+ TotalCount = source.TotalCount;
+ TotalPages = source.TotalPages;
+
+ Items = new List(converter(source.Items));
+ }
+ }
+
+ ///
+ /// Provides some help methods for interface.
+ ///
+ public static class PagedList
+ {
+ ///
+ /// Creates an empty of .
+ ///
+ /// The type for paging
+ /// An empty instance of .
+ public static IPagedList Empty() => new PagedList();
+ ///
+ /// Creates a new instance of from source of instance.
+ ///
+ /// The type of the result.
+ /// The type of the source.
+ /// The source.
+ /// The converter.
+ /// An instance of .
+ public static IPagedList From(IPagedList source, Func, IEnumerable> converter) => new PagedList(source, converter);
+ }
+}
diff --git a/Entities/Context/UnitOfWork/IEnumerablePagedListExtensions.cs b/Entities/Context/UnitOfWork/IEnumerablePagedListExtensions.cs
new file mode 100644
index 0000000..a19848e
--- /dev/null
+++ b/Entities/Context/UnitOfWork/IEnumerablePagedListExtensions.cs
@@ -0,0 +1,39 @@
+namespace SharedDATA.Api
+{
+ using SharedDATA.Context;
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Provides some extension methods for to provide paging capability.
+ /// 提供一些扩展方法来提供分页功能
+ ///
+ public static class IEnumerablePagedListExtensions
+ {
+ ///
+ /// Converts the specified source to by the specified and .
+ /// 通过起始页和页大小把数据转换成页集合
+ ///
+ /// The type of the source.源的类型
+ /// The source to paging.分页的源
+ /// The index of the page.起始页
+ /// The size of the page.页大小
+ /// The start index value.开始索引值
+ /// An instance of the inherited from interface.接口继承的实例
+ public static IPagedList ToPagedList(this IEnumerable source, int pageIndex, int pageSize, int indexFrom = 0) => new PagedList(source, pageIndex, pageSize, indexFrom);
+
+ ///
+ /// Converts the specified source to by the specified , and
+ /// 通过转换器,起始页和页大小把数据转换成页集合
+ ///
+ /// The type of the source.源的类型
+ /// The type of the result.反馈的类型
+ /// The source to convert.要转换的源
+ /// The converter to change the to .转换器来改变源到反馈
+ /// The page index.起始页
+ /// The page size.页大小
+ /// The start index value.开始索引值
+ /// An instance of the inherited from interface.接口继承的实例
+ public static IPagedList ToPagedList(this IEnumerable source, Func, IEnumerable> converter, int pageIndex, int pageSize, int indexFrom = 0) => new PagedList(source, converter, pageIndex, pageSize, indexFrom);
+ }
+}
diff --git a/Entities/Context/UnitOfWork/IQueryablePageListExtensions.cs b/Entities/Context/UnitOfWork/IQueryablePageListExtensions.cs
new file mode 100644
index 0000000..046589e
--- /dev/null
+++ b/Entities/Context/UnitOfWork/IQueryablePageListExtensions.cs
@@ -0,0 +1,52 @@
+namespace SharedDATA.Api
+{
+ using System;
+ using System.Linq;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.EntityFrameworkCore;
+ using SharedDATA.Context;
+
+ public static class IQueryablePageListExtensions
+ {
+ ///
+ /// Converts the specified source to by the specified and .
+ /// 根据起始页和页大小转换成源
+ ///
+ /// The type of the source.源的类型
+ /// The source to paging.分页的源
+ /// The index of the page.起始页
+ /// The size of the page.页大小
+ ///
+ /// A to observe while waiting for the task to complete.
+ /// 在等待任务完成时观察
+ ///
+ /// 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))
+ {
+ //如果索引比起始页大,则异常
+ if (indexFrom > pageIndex)
+ {
+ throw new ArgumentException($"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex");
+ }
+ //数据源大小
+ var count = await source.CountAsync(cancellationToken).ConfigureAwait(false);
+
+ var items = await source.Skip((pageIndex - indexFrom) * pageSize)
+ .Take(pageSize).ToListAsync(cancellationToken).ConfigureAwait(false);
+
+ var pagedList = new PagedList()
+ {
+ PageIndex = pageIndex,
+ PageSize = pageSize,
+ IndexFrom = indexFrom,
+ TotalCount = count,
+ Items = items,
+ TotalPages = (int)Math.Ceiling(count / (double)pageSize)
+ };
+
+ return pagedList;
+ }
+ }
+}
diff --git a/Entities/Context/UnitOfWork/IRepository.cs b/Entities/Context/UnitOfWork/IRepository.cs
new file mode 100644
index 0000000..b397860
--- /dev/null
+++ b/Entities/Context/UnitOfWork/IRepository.cs
@@ -0,0 +1,463 @@
+namespace SharedDATA.Api
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.EntityFrameworkCore.Query;
+ using Microsoft.EntityFrameworkCore.ChangeTracking;
+ using Microsoft.EntityFrameworkCore;
+ using SharedDATA.Context;
+
+ ///
+ /// Defines the interfaces for generic repository.
+ /// 为通用存储库定义接口
+ ///
+ /// The type of the entity.实体类型
+ public interface IRepository where TEntity : class
+ {
+ ///
+ /// Changes the table name. This require the tables in the same database.
+ /// 更改表名。这需要相同数据库中的表
+ ///
+ ///
+ ///
+ /// This only been used for supporting multiple tables in the same model. This require the tables in the same database.
+ /// 这只用于支持同一个模型中的多个表。这需要相同数据库中的表。
+ ///
+ void ChangeTable(string table);
+
+ ///
+ /// Gets the based on a predicate, orderby delegate and page information. This method default no-tracking query.
+ /// 基于谓词、orderby委托和页面信息获取。此方法默认无跟踪查询。
+ ///
+ /// A function to test each element for a condition.用于测试条件的每个元素的函数
+ /// A function to order elements.对元素进行排序的函数
+ /// A function to include navigation properties 包含导航属性的函数
+ /// The index of page.起始页
+ /// The size of the page.页大小
+ /// 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.
+ 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.
+ /// 基于谓词、orderby委托和页面信息获取。此方法默认无跟踪查询。
+ ///
+ /// A function to test each element for a condition.用于测试条件的每个元素的函数
+ /// A function to order elements.对元素进行排序的函数
+ /// A function to include navigation properties 包含导航属性的函数
+ /// The index of page.起始页
+ /// The size of the page.页大小
+ /// True to disable changing tracking;禁用更改跟踪; otherwise, false. Default to true.
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// 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);
+
+ ///
+ /// 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.
+ /// A function to order elements.
+ /// A function to include navigation properties
+ /// The index of page.
+ /// The size of the page.
+ /// 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.
+ 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.
+ ///
+ /// The selector for projection.
+ /// A function to test each element for a condition.
+ /// A function to order elements.
+ /// A function to include navigation properties
+ /// The index of page.
+ /// The size of the page.
+ /// True to disable changing tracking; otherwise, false. Default to true.
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// 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;
+
+ ///
+ /// 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.
+ /// 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 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);
+
+ ///
+ /// 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.
+ /// 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 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);
+
+ ///
+ /// 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.
+ /// 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.
+ Task GetFirstOrDefaultAsync(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.
+ ///
+ /// 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.
+ Task GetFirstOrDefaultAsync(Expression> predicate = null,
+ Func, IOrderedQueryable> orderBy = null,
+ Func, IIncludableQueryable> include = null,
+ bool disableTracking = true,
+ bool ignoreQueryFilters = false);
+
+ ///
+ /// Uses raw SQL queries to fetch the specified data.
+ ///
+ /// The raw SQL.
+ /// The parameters.
+ /// An that contains elements that satisfy the condition specified by raw SQL.
+ IQueryable FromSql(string sql, params object[] 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.
+ TEntity Find(params object[] 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 find operation. The task result contains the found entity or null.
+ ValueTask FindAsync(params object[] 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 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.
+ ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken);
+
+ ///
+ /// Gets all entities. This method is not recommended
+ ///
+ /// The .
+ IQueryable GetAll();
+
+ ///
+ /// 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.
+ 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
+ ///
+ /// The .
+ Task> GetAllAsync();
+
+ ///
+ /// 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.
+ Task> GetAllAsync(Expression> predicate = null,
+ Func, IOrderedQueryable> orderBy = null,
+ Func, IIncludableQueryable> include = null,
+ bool disableTracking = true,
+ bool ignoreQueryFilters = false);
+
+ ///
+ /// Gets the count based on a predicate.
+ ///
+ ///
+ ///
+ int Count(Expression> predicate = null);
+
+ ///
+ /// Gets async the count based on a predicate.
+ ///
+ ///
+ ///
+ Task CountAsync(Expression> predicate = null);
+
+ ///
+ /// Gets the long count based on a predicate.
+ ///
+ ///
+ ///
+ long LongCount(Expression> predicate = null);
+
+ ///
+ /// Gets async the long count based on a predicate.
+ ///
+ ///
+ ///
+ Task LongCountAsync(Expression> predicate = null);
+
+ ///
+ /// Gets the max based on a predicate.
+ ///
+ ///
+ /// ///
+ /// decimal
+ T Max(Expression> predicate = null, Expression> selector = null);
+
+ ///
+ /// Gets the async max based on a predicate.
+ ///
+ ///
+ /// ///
+ /// decimal
+ Task MaxAsync(Expression> predicate = null, Expression> selector = null);
+
+ ///
+ /// Gets the min based on a predicate.
+ ///
+ ///
+ ///
+ /// decimal
+ T Min(Expression> predicate = null, Expression> selector = null);
+
+ ///
+ /// Gets the async min based on a predicate.
+ ///
+ ///
+ ///
+ /// decimal
+ Task MinAsync(Expression> predicate = null, Expression> selector = null);
+
+ ///
+ /// Gets the average based on a predicate.
+ ///
+ ///
+ /// ///
+ /// decimal
+ decimal Average(Expression> predicate = null, Expression> selector = null);
+
+ ///
+ /// Gets the async average based on a predicate.
+ ///
+ ///
+ /// ///
+ /// decimal
+ Task AverageAsync(Expression> predicate = null, Expression> selector = null);
+
+ ///
+ /// Gets the sum based on a predicate.
+ ///
+ ///
+ /// ///
+ /// decimal
+ decimal Sum(Expression> predicate = null, Expression> selector = null);
+
+ ///
+ /// Gets the async sum based on a predicate.
+ ///
+ ///
+ /// ///
+ /// decimal
+ Task SumAsync(Expression> predicate = null, Expression> selector = null);
+
+ ///
+ /// Gets the Exists record based on a predicate.
+ ///
+ ///
+ ///
+ bool Exists(Expression> selector = null);
+ ///
+ /// Gets the Async Exists record based on a predicate.
+ ///
+ ///
+ ///
+ Task ExistsAsync(Expression> selector = null);
+
+ ///
+ /// Inserts a new entity synchronously.
+ ///
+ /// The entity to insert.
+ TEntity Insert(TEntity entity);
+
+ ///
+ /// Inserts a range of entities synchronously.
+ ///
+ /// The entities to insert.
+ void Insert(params TEntity[] entities);
+
+ ///
+ /// Inserts a range of entities synchronously.
+ ///
+ /// The entities to insert.
+ void Insert(IEnumerable entities);
+
+ ///
+ /// Inserts a new entity asynchronously.
+ ///
+ /// 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));
+
+ ///
+ /// Inserts a range of entities asynchronously.
+ ///
+ /// The entities to insert.
+ /// A that represents the asynchronous insert operation.
+ Task InsertAsync(params TEntity[] entities);
+
+ ///
+ /// Inserts a range of entities asynchronously.
+ ///
+ /// 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));
+
+ ///
+ /// Updates the specified entity.
+ ///
+ /// The entity.
+ void Update(TEntity entity);
+
+ ///
+ /// Updates the specified entities.
+ ///
+ /// The entities.
+ void Update(params TEntity[] entities);
+
+ ///
+ /// Updates the specified entities.
+ ///
+ /// The entities.
+ void Update(IEnumerable entities);
+
+ ///
+ /// Deletes the entity by the specified primary key.
+ ///
+ /// The primary key value.
+ void Delete(object id);
+
+ ///
+ /// Deletes the specified entity.
+ ///
+ /// The entity to delete.
+ void Delete(TEntity entity);
+
+ ///
+ /// Deletes the specified entities.
+ ///
+ /// The entities.
+ void Delete(params TEntity[] entities);
+
+ ///
+ /// Deletes the specified entities.
+ ///
+ /// The entities.
+ void Delete(IEnumerable entities);
+
+ ///
+ /// Change entity state for patch method on web api.
+ ///
+ /// The entity.
+ /// /// The entity state.
+ void ChangeEntityState(TEntity entity, EntityState state);
+ }
+}
diff --git a/Entities/Context/UnitOfWork/IRepositoryFactory.cs b/Entities/Context/UnitOfWork/IRepositoryFactory.cs
new file mode 100644
index 0000000..127e67e
--- /dev/null
+++ b/Entities/Context/UnitOfWork/IRepositoryFactory.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SharedDATA.Api
+{
+ ///
+ /// 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/Entities/Context/UnitOfWork/IUnitOfWork.cs b/Entities/Context/UnitOfWork/IUnitOfWork.cs
new file mode 100644
index 0000000..35293f6
--- /dev/null
+++ b/Entities/Context/UnitOfWork/IUnitOfWork.cs
@@ -0,0 +1,78 @@
+
+
+namespace SharedDATA.Api
+{
+ using System;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.EntityFrameworkCore;
+ using Microsoft.EntityFrameworkCore.ChangeTracking;
+
+ ///
+ /// Defines the interface(s) for unit of work.
+ ///
+ public interface IUnitOfWork : IDisposable
+ {
+
+ ///
+ /// Changes the database name. This require the databases in the same machine. NOTE: This only work for MySQL right now.
+ ///
+ /// The database name.
+ ///
+ /// This only been used for supporting multiple databases in the same model. This require the databases in the same machine.
+ ///
+ 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.
+ IRepository GetRepository(bool hasCustomRepository = false) where TEntity : class;
+
+ ///
+ /// Gets the db context.
+ ///
+ ///
+ TContext GetDbContext() where TContext : DbContext;
+
+ ///
+ /// Saves all changes made in this context to the database.
+ ///
+ /// True if sayve changes ensure auto record the change history.
+ /// The number of state entries written to the database.
+ int SaveChanges(bool ensureAutoHistory = false);
+
+ ///
+ /// Asynchronously saves all changes made in this unit of work to the database.
+ ///
+ /// True if save changes ensure auto record the change history.
+ /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database.
+ Task SaveChangesAsync(bool ensureAutoHistory = false);
+
+ ///
+ /// Executes the specified raw SQL command.
+ ///
+ /// The raw SQL.
+ /// The parameters.
+ /// The number of state entities written to database.
+ int ExecuteSqlCommand(string sql, params object[] parameters);
+
+ ///
+ /// Uses raw SQL queries to fetch the specified data.
+ ///
+ /// The type of the entity.
+ /// The raw SQL.
+ /// The parameters.
+ /// 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);
+ }
+}
diff --git a/Entities/Context/UnitOfWork/IUnitOfWorkOfT.cs b/Entities/Context/UnitOfWork/IUnitOfWorkOfT.cs
new file mode 100644
index 0000000..cfe08b6
--- /dev/null
+++ b/Entities/Context/UnitOfWork/IUnitOfWorkOfT.cs
@@ -0,0 +1,27 @@
+
+
+namespace SharedDATA.Api
+{
+ using Microsoft.EntityFrameworkCore;
+ using System.Threading.Tasks;
+
+ ///
+ /// Defines the interface(s) for generic unit of work.
+ ///
+ public interface IUnitOfWork : IUnitOfWork where TContext : DbContext
+ {
+ ///
+ /// Gets the db context.
+ ///
+ /// The instance of type .
+ TContext DbContext { get; }
+
+ ///
+ /// 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);
+ }
+}
diff --git a/Entities/Context/UnitOfWork/Repository.cs b/Entities/Context/UnitOfWork/Repository.cs
new file mode 100644
index 0000000..06030e5
--- /dev/null
+++ b/Entities/Context/UnitOfWork/Repository.cs
@@ -0,0 +1,942 @@
+
+
+namespace SharedDATA.Api
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Reflection;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.EntityFrameworkCore;
+ using Microsoft.EntityFrameworkCore.Metadata;
+ using Microsoft.EntityFrameworkCore.Query;
+ using Microsoft.EntityFrameworkCore.ChangeTracking;
+ using SharedDATA.Context;
+
+ ///
+ /// Represents a default generic repository implements the interface.
+ ///
+ /// The type of the entity.
+ public class Repository : IRepository where TEntity : class
+ {
+ protected readonly DbContext _dbContext;
+ protected readonly DbSet _dbSet;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The database context.
+ public Repository(DbContext dbContext)
+ {
+ _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
+ _dbSet = _dbContext.Set();
+ }
+
+ ///
+ /// Changes the table name. This require the tables in the same database.
+ ///
+ ///
+ ///
+ /// This only been used for supporting multiple tables in the same model. This require the tables in the same database.
+ ///
+ public virtual void ChangeTable(string table)
+ {
+ if (_dbContext.Model.FindEntityType(typeof(TEntity)) is IConventionEntityType relational)
+ {
+ relational.SetTableName(table);
+ }
+ }
+
+ ///
+ /// Gets all entities. This method is not recommended
+ ///
+ /// The .
+ public IQueryable GetAll()
+ {
+ return _dbSet;
+ }
+
+ ///
+ /// 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 IQueryable GetAll(
+ 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 orderBy(query);
+ }
+ else
+ {
+ return 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.
+ /// A function to include navigation properties
+ /// The index of page.
+ /// The size of the page.
+ /// 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.
+ 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;
+
+ 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 orderBy(query).ToPagedList(pageIndex, pageSize);
+ }
+ else
+ {
+ return query.ToPagedList(pageIndex, pageSize);
+ }
+ }
+
+ ///
+ /// 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
+ /// The index of page.
+ /// The size of the page.
+ /// True to disable changing tracking; otherwise, false. Default to true.
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// 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)
+ {
+ 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 orderBy(query).ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken);
+ }
+ else
+ {
+ return query.ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken);
+ }
+ }
+
+ ///
+ /// 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.
+ /// A function to order elements.
+ /// A function to include navigation properties
+ /// The index of page.
+ /// The size of the page.
+ /// 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.
+ 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;
+
+ 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 orderBy(query).Select(selector).ToPagedList(pageIndex, pageSize);
+ }
+ else
+ {
+ return query.Select(selector).ToPagedList(pageIndex, pageSize);
+ }
+ }
+
+ ///
+ /// 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.
+ /// A function to order elements.
+ /// A function to include navigation properties
+ /// The index of page.
+ /// The size of the page.
+ /// True to disable changing tracking; otherwise, false. Default to true.
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// Ignore 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)
+ where TResult : class
+ {
+ 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 orderBy(query).Select(selector).ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken);
+ }
+ else
+ {
+ return query.Select(selector).ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken);
+ }
+ }
+
+ ///
+ /// 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.
+ /// 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.
+ public virtual TEntity GetFirstOrDefault(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 orderBy(query).FirstOrDefault();
+ }
+ else
+ {
+ return query.FirstOrDefault();
+ }
+ }
+
+
+ ///
+ 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;
+
+ 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).FirstOrDefaultAsync();
+ }
+ else
+ {
+ 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.
+ ///
+ /// 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 .
+ /// 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)
+ {
+ 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 orderBy(query).Select(selector).FirstOrDefault();
+ }
+ else
+ {
+ return query.Select(selector).FirstOrDefault();
+ }
+ }
+
+ ///
+ 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;
+
+ 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).FirstOrDefaultAsync();
+ }
+ else
+ {
+ return await query.Select(selector).FirstOrDefaultAsync();
+ }
+ }
+
+ ///
+ /// Uses raw SQL queries to fetch the specified data.
+ ///
+ /// 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);
+
+ ///
+ /// 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);
+
+ ///
+ /// 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);
+
+ ///
+ /// 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 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);
+
+ ///
+ /// Gets the count based on a predicate.
+ ///
+ ///
+ ///
+ public virtual int Count(Expression> predicate = null)
+ {
+ if (predicate == null)
+ {
+ return _dbSet.Count();
+ }
+ else
+ {
+ return _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();
+ }
+ else
+ {
+ return await _dbSet.CountAsync(predicate);
+ }
+ }
+
+ ///
+ /// Gets the long count based on a predicate.
+ ///
+ ///
+ ///
+ public virtual long LongCount(Expression> predicate = null)
+ {
+ if (predicate == null)
+ {
+ return _dbSet.LongCount();
+ }
+ else
+ {
+ return _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);
+ }
+ }
+
+ ///
+ /// Gets the max based on a predicate.
+ ///
+ ///
+ /// ///
+ /// 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);
+ }
+
+ ///
+ /// Gets the async max based on a predicate.
+ ///
+ ///
+ /// ///
+ /// decimal
+ public virtual async Task MaxAsync(Expression> predicate = null, Expression