添加项目文件。
This commit is contained in:
7
Entities/Configuration/ApiConfiguration.cs
Normal file
7
Entities/Configuration/ApiConfiguration.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Entities.Configuration
|
||||
{
|
||||
public class ApiConfiguration
|
||||
{
|
||||
public string? BaseAddress { get; set; } /*= "http://localhost:5099";*/
|
||||
}
|
||||
}
|
10
Entities/Configuration/JwtConfiguration.cs
Normal file
10
Entities/Configuration/JwtConfiguration.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
57
Entities/Context/IPagedList.cs
Normal file
57
Entities/Context/IPagedList.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
namespace SharedDATA.Context
|
||||
{
|
||||
|
||||
using System.Collections.Generic;
|
||||
/// <summary>
|
||||
/// Provides the interface(s) for paged list of any type.
|
||||
/// 为任何类型的分页列表提供接口
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type for paging.分页的类型</typeparam>
|
||||
public interface IPagedList<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the index start value.
|
||||
/// 获取索引起始值
|
||||
/// </summary>
|
||||
/// <value>The index start value.</value>
|
||||
int IndexFrom { get; }
|
||||
/// <summary>
|
||||
/// Gets the page index (current).
|
||||
/// 获取页索引(当前)
|
||||
/// </summary>
|
||||
int PageIndex { get; }
|
||||
/// <summary>
|
||||
/// Gets the page size.
|
||||
/// 获取页面大小
|
||||
/// </summary>
|
||||
int PageSize { get; }
|
||||
/// <summary>
|
||||
/// Gets the total count of the list of type <typeparamref name="T"/>
|
||||
/// 获取类型列表的总计数
|
||||
/// </summary>
|
||||
int TotalCount { get; }
|
||||
/// <summary>
|
||||
/// Gets the total pages.
|
||||
/// 获取页面总数
|
||||
/// </summary>
|
||||
int TotalPages { get; }
|
||||
/// <summary>
|
||||
/// Gets the current page items.
|
||||
/// 获取当前页项
|
||||
/// </summary>
|
||||
IList<T> Items { get; }
|
||||
/// <summary>
|
||||
/// Gets the has previous page.
|
||||
/// 获取前一页
|
||||
/// </summary>
|
||||
/// <value>The has previous page.</value>
|
||||
bool HasPreviousPage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the has next page.
|
||||
/// 获取下一页
|
||||
/// </summary>
|
||||
/// <value>The has next page.</value>
|
||||
bool HasNextPage { get; }
|
||||
}
|
||||
}
|
238
Entities/Context/PagedList.cs
Normal file
238
Entities/Context/PagedList.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
namespace SharedDATA.Context
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the default implementation of the <see cref="IPagedList{T}"/> interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the data to page 页类型的数据</typeparam>
|
||||
public class PagedList<T> : IPagedList<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the page.
|
||||
/// 获得页的起始页
|
||||
/// </summary>
|
||||
/// <value>The index of the page.</value>
|
||||
public int PageIndex { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the page.
|
||||
/// 获得页大小
|
||||
/// </summary>
|
||||
/// <value>The size of the page.</value>
|
||||
public int PageSize { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the total count.
|
||||
/// 获得总数
|
||||
/// </summary>
|
||||
/// <value>The total count.</value>
|
||||
public int TotalCount { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the total pages.
|
||||
/// 获得总页数
|
||||
/// </summary>
|
||||
/// <value>The total pages.</value>
|
||||
public int TotalPages { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the index from.
|
||||
/// 从索引起
|
||||
/// </summary>
|
||||
/// <value>The index from.</value>
|
||||
public int IndexFrom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the items.
|
||||
/// 数据
|
||||
/// </summary>
|
||||
/// <value>The items.</value>
|
||||
public IList<T> Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the has previous page.
|
||||
/// 获取前一页
|
||||
/// </summary>
|
||||
/// <value>The has previous page.</value>
|
||||
public bool HasPreviousPage => PageIndex - IndexFrom > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the has next page.
|
||||
/// 获取下一页
|
||||
/// </summary>
|
||||
/// <value>The has next page.</value>
|
||||
public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PagedList{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="pageIndex">The index of the page.</param>
|
||||
/// <param name="pageSize">The size of the page.</param>
|
||||
/// <param name="indexFrom">The index from.</param>
|
||||
public PagedList(IEnumerable<T> source, int pageIndex, int pageSize, int indexFrom)
|
||||
{
|
||||
if (indexFrom > pageIndex)
|
||||
{
|
||||
throw new ArgumentException($"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex");
|
||||
}
|
||||
|
||||
if (source is IQueryable<T> 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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PagedList{T}" /> class.
|
||||
/// </summary>
|
||||
public PagedList() => Items = new T[0];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides the implementation of the <see cref="IPagedList{T}"/> and converter.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the source.</typeparam>
|
||||
/// <typeparam name="TResult">The type of the result.</typeparam>
|
||||
public class PagedList<TSource, TResult> : IPagedList<TResult>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the index of the page.
|
||||
/// </summary>
|
||||
/// <value>The index of the page.</value>
|
||||
public int PageIndex { get; }
|
||||
/// <summary>
|
||||
/// Gets the size of the page.
|
||||
/// </summary>
|
||||
/// <value>The size of the page.</value>
|
||||
public int PageSize { get; }
|
||||
/// <summary>
|
||||
/// Gets the total count.
|
||||
/// </summary>
|
||||
/// <value>The total count.</value>
|
||||
public int TotalCount { get; }
|
||||
/// <summary>
|
||||
/// Gets the total pages.
|
||||
/// </summary>
|
||||
/// <value>The total pages.</value>
|
||||
public int TotalPages { get; }
|
||||
/// <summary>
|
||||
/// Gets the index from.
|
||||
/// </summary>
|
||||
/// <value>The index from.</value>
|
||||
public int IndexFrom { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items.
|
||||
/// </summary>
|
||||
/// <value>The items.</value>
|
||||
public IList<TResult> Items { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the has previous page.
|
||||
/// </summary>
|
||||
/// <value>The has previous page.</value>
|
||||
public bool HasPreviousPage => PageIndex - IndexFrom > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the has next page.
|
||||
/// </summary>
|
||||
/// <value>The has next page.</value>
|
||||
public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PagedList{TSource, TResult}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="converter">The converter.</param>
|
||||
/// <param name="pageIndex">The index of the page.</param>
|
||||
/// <param name="pageSize">The size of the page.</param>
|
||||
/// <param name="indexFrom">The index from.</param>
|
||||
public PagedList(IEnumerable<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TResult>> converter, int pageIndex, int pageSize, int indexFrom)
|
||||
{
|
||||
if (indexFrom > pageIndex)
|
||||
{
|
||||
throw new ArgumentException($"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex");
|
||||
}
|
||||
|
||||
if (source is IQueryable<TSource> 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<TResult>(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<TResult>(converter(items));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PagedList{TSource, TResult}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="converter">The converter.</param>
|
||||
public PagedList(IPagedList<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TResult>> converter)
|
||||
{
|
||||
PageIndex = source.PageIndex;
|
||||
PageSize = source.PageSize;
|
||||
IndexFrom = source.IndexFrom;
|
||||
TotalCount = source.TotalCount;
|
||||
TotalPages = source.TotalPages;
|
||||
|
||||
Items = new List<TResult>(converter(source.Items));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides some help methods for <see cref="IPagedList{T}"/> interface.
|
||||
/// </summary>
|
||||
public static class PagedList
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an empty of <see cref="IPagedList{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type for paging </typeparam>
|
||||
/// <returns>An empty instance of <see cref="IPagedList{T}"/>.</returns>
|
||||
public static IPagedList<T> Empty<T>() => new PagedList<T>();
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="IPagedList{TResult}"/> from source of <see cref="IPagedList{TSource}"/> instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type of the result.</typeparam>
|
||||
/// <typeparam name="TSource">The type of the source.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="converter">The converter.</param>
|
||||
/// <returns>An instance of <see cref="IPagedList{TResult}"/>.</returns>
|
||||
public static IPagedList<TResult> From<TResult, TSource>(IPagedList<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TResult>> converter) => new PagedList<TSource, TResult>(source, converter);
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
namespace SharedDATA.Api
|
||||
{
|
||||
using SharedDATA.Context;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Provides some extension methods for <see cref="IEnumerable{T}"/> to provide paging capability.
|
||||
/// 提供一些扩展方法来提供分页功能
|
||||
/// </summary>
|
||||
public static class IEnumerablePagedListExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the specified source to <see cref="IPagedList{T}"/> by the specified <paramref name="pageIndex"/> and <paramref name="pageSize"/>.
|
||||
/// 通过起始页和页大小把数据转换成页集合
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the source.源的类型</typeparam>
|
||||
/// <param name="source">The source to paging.分页的源</param>
|
||||
/// <param name="pageIndex">The index of the page.起始页</param>
|
||||
/// <param name="pageSize">The size of the page.页大小</param>
|
||||
/// <param name="indexFrom">The start index value.开始索引值</param>
|
||||
/// <returns>An instance of the inherited from <see cref="IPagedList{T}"/> interface.接口继承的实例</returns>
|
||||
public static IPagedList<T> ToPagedList<T>(this IEnumerable<T> source, int pageIndex, int pageSize, int indexFrom = 0) => new PagedList<T>(source, pageIndex, pageSize, indexFrom);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified source to <see cref="IPagedList{T}"/> by the specified <paramref name="converter"/>, <paramref name="pageIndex"/> and <paramref name="pageSize"/>
|
||||
/// 通过转换器,起始页和页大小把数据转换成页集合
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the source.源的类型</typeparam>
|
||||
/// <typeparam name="TResult">The type of the result.反馈的类型</typeparam>
|
||||
/// <param name="source">The source to convert.要转换的源</param>
|
||||
/// <param name="converter">The converter to change the <typeparamref name="TSource"/> to <typeparamref name="TResult"/>.转换器来改变源到反馈</param>
|
||||
/// <param name="pageIndex">The page index.起始页</param>
|
||||
/// <param name="pageSize">The page size.页大小</param>
|
||||
/// <param name="indexFrom">The start index value.开始索引值</param>
|
||||
/// <returns>An instance of the inherited from <see cref="IPagedList{T}"/> interface.接口继承的实例</returns>
|
||||
public static IPagedList<TResult> ToPagedList<TSource, TResult>(this IEnumerable<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TResult>> converter, int pageIndex, int pageSize, int indexFrom = 0) => new PagedList<TSource, TResult>(source, converter, pageIndex, pageSize, indexFrom);
|
||||
}
|
||||
}
|
52
Entities/Context/UnitOfWork/IQueryablePageListExtensions.cs
Normal file
52
Entities/Context/UnitOfWork/IQueryablePageListExtensions.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the specified source to <see cref="IPagedList{T}"/> by the specified <paramref name="pageIndex"/> and <paramref name="pageSize"/>.
|
||||
/// 根据起始页和页大小转换成源
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the source.源的类型</typeparam>
|
||||
/// <param name="source">The source to paging.分页的源</param>
|
||||
/// <param name="pageIndex">The index of the page.起始页</param>
|
||||
/// <param name="pageSize">The size of the page.页大小</param>
|
||||
/// <param name="cancellationToken">
|
||||
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
|
||||
/// 在等待任务完成时观察
|
||||
/// </param>
|
||||
/// <param name="indexFrom">The start index value.值的起始索引</param>
|
||||
/// <returns>An instance of the inherited from <see cref="IPagedList{T}"/> interface.接口继承的实例</returns>
|
||||
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> 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<T>()
|
||||
{
|
||||
PageIndex = pageIndex,
|
||||
PageSize = pageSize,
|
||||
IndexFrom = indexFrom,
|
||||
TotalCount = count,
|
||||
Items = items,
|
||||
TotalPages = (int)Math.Ceiling(count / (double)pageSize)
|
||||
};
|
||||
|
||||
return pagedList;
|
||||
}
|
||||
}
|
||||
}
|
463
Entities/Context/UnitOfWork/IRepository.cs
Normal file
463
Entities/Context/UnitOfWork/IRepository.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interfaces for generic repository.
|
||||
/// 为通用存储库定义接口
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.实体类型</typeparam>
|
||||
public interface IRepository<TEntity> where TEntity : class
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes the table name. This require the tables in the same database.
|
||||
/// 更改表名。这需要相同数据库中的表
|
||||
/// </summary>
|
||||
/// <param name="table"></param>
|
||||
/// <remarks>
|
||||
/// This only been used for supporting multiple tables in the same model. This require the tables in the same database.
|
||||
/// 这只用于支持同一个模型中的多个表。这需要相同数据库中的表。
|
||||
/// </remarks>
|
||||
void ChangeTable(string table);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPagedList{TEntity}"/> based on a predicate, orderby delegate and page information. This method default no-tracking query.
|
||||
/// 基于谓词、orderby委托和页面信息获取<see cref="IPagedList{TEntity}"/>。此方法默认无跟踪查询。
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.用于测试条件的每个元素的函数</param>
|
||||
/// <param name="orderBy">A function to order elements.对元素进行排序的函数</param>
|
||||
/// <param name="include">A function to include navigation properties 包含导航属性的函数</param>
|
||||
/// <param name="pageIndex">The index of page.起始页</param>
|
||||
/// <param name="pageSize">The size of the page.页大小</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, 禁用更改跟踪<c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters 忽略查询过滤器</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by 包含满足指定条件的元素<paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
IPagedList<TEntity> GetPagedList(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
int pageIndex = 0,
|
||||
int pageSize = 20,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPagedList{TEntity}"/> based on a predicate, orderby delegate and page information. This method default no-tracking query.
|
||||
/// 基于谓词、orderby委托和页面信息获取<see cref="IPagedList{TEntity}"/>。此方法默认无跟踪查询。
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.用于测试条件的每个元素的函数</param>
|
||||
/// <param name="orderBy">A function to order elements.对元素进行排序的函数</param>
|
||||
/// <param name="include">A function to include navigation properties 包含导航属性的函数</param>
|
||||
/// <param name="pageIndex">The index of page.起始页</param>
|
||||
/// <param name="pageSize">The size of the page.页大小</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking;禁用更改跟踪; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="cancellationToken">
|
||||
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
|
||||
/// </param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters 忽略查询过滤器</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.此方法默认无跟踪查询</remarks>
|
||||
Task<IPagedList<TEntity>> GetPagedListAsync(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
int pageIndex = 0,
|
||||
int pageSize = 20,
|
||||
bool disableTracking = true,
|
||||
CancellationToken cancellationToken = default(CancellationToken),
|
||||
bool ignoreQueryFilters = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPagedList{TResult}"/> based on a predicate, orderby delegate and page information. This method default no-tracking query.
|
||||
/// </summary>
|
||||
/// <param name="selector">The selector for projection.</param>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="pageIndex">The index of page.</param>
|
||||
/// <param name="pageSize">The size of the page.</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TResult}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
IPagedList<TResult> GetPagedList<TResult>(Expression<Func<TEntity, TResult>> selector,
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
int pageIndex = 0,
|
||||
int pageSize = 20,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false) where TResult : class;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPagedList{TEntity}"/> based on a predicate, orderby delegate and page information. This method default no-tracking query.
|
||||
/// </summary>
|
||||
/// <param name="selector">The selector for projection.</param>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="pageIndex">The index of page.</param>
|
||||
/// <param name="pageSize">The size of the page.</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="cancellationToken">
|
||||
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
|
||||
/// </param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
Task<IPagedList<TResult>> GetPagedListAsync<TResult>(Expression<Func<TEntity, TResult>> selector,
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
int pageIndex = 0,
|
||||
int pageSize = 20,
|
||||
bool disableTracking = true,
|
||||
CancellationToken cancellationToken = default(CancellationToken),
|
||||
bool ignoreQueryFilters = false) where TResult : class;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>true</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method defaults to a read-only, no-tracking query.</remarks>
|
||||
TEntity GetFirstOrDefault(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="selector">The selector for projection.</param>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>true</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method defaults to a read-only, no-tracking query.</remarks>
|
||||
TResult GetFirstOrDefault<TResult>(Expression<Func<TEntity, TResult>> selector,
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="selector">The selector for projection.</param>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>true</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>Ex: This method defaults to a read-only, no-tracking query.</remarks>
|
||||
Task<TResult> GetFirstOrDefaultAsync<TResult>(Expression<Func<TEntity, TResult>> selector,
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>true</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>Ex: This method defaults to a read-only, no-tracking query. </remarks>
|
||||
Task<TEntity> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false);
|
||||
|
||||
/// <summary>
|
||||
/// Uses raw SQL queries to fetch the specified <typeparamref name="TEntity" /> data.
|
||||
/// </summary>
|
||||
/// <param name="sql">The raw SQL.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>An <see cref="IQueryable{TEntity}" /> that contains elements that satisfy the condition specified by raw SQL.</returns>
|
||||
IQueryable<TEntity> FromSql(string sql, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="keyValues">The values of the primary key for the entity to be found.</param>
|
||||
/// <returns>The found entity or null.</returns>
|
||||
TEntity Find(params object[] keyValues);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="keyValues">The values of the primary key for the entity to be found.</param>
|
||||
/// <returns>A <see cref="Task{TEntity}"/> that represents the asynchronous find operation. The task result contains the found entity or null.</returns>
|
||||
ValueTask<TEntity> FindAsync(params object[] keyValues);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="keyValues">The values of the primary key for the entity to be found.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
|
||||
/// <returns>A <see cref="Task{TEntity}"/> that represents the asynchronous find operation. The task result contains the found entity or null.</returns>
|
||||
ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities. This method is not recommended
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IQueryable{TEntity}"/>.</returns>
|
||||
IQueryable<TEntity> GetAll();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities. This method is not recommended
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>true</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>Ex: This method defaults to a read-only, no-tracking query.</remarks>
|
||||
IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities. This method is not recommended
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IQueryable{TEntity}"/>.</returns>
|
||||
Task<IList<TEntity>> GetAllAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities. This method is not recommended
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>true</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>Ex: This method defaults to a read-only, no-tracking query.</remarks>
|
||||
Task<IList<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
int Count(Expression<Func<TEntity, bool>> predicate = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets async the count based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the long count based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
long LongCount(Expression<Func<TEntity, bool>> predicate = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets async the long count based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
T Max<T>(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, T>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async max based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
Task<T> MaxAsync<T>(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, T>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the min based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
T Min<T>(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, T>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async min based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
Task<T> MinAsync<T>(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, T>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the average based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
decimal Average(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, decimal>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async average based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
Task<decimal> AverageAsync(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, decimal>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sum based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
decimal Sum(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, decimal>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async sum based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
Task<decimal> SumAsync(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, decimal>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Exists record based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
bool Exists(Expression<Func<TEntity, bool>> selector = null);
|
||||
/// <summary>
|
||||
/// Gets the Async Exists record based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> selector = null);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new entity synchronously.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to insert.</param>
|
||||
TEntity Insert(TEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of entities synchronously.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to insert.</param>
|
||||
void Insert(params TEntity[] entities);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of entities synchronously.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to insert.</param>
|
||||
void Insert(IEnumerable<TEntity> entities);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new entity asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to insert.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous insert operation.</returns>
|
||||
ValueTask<EntityEntry<TEntity>> InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of entities asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to insert.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous insert operation.</returns>
|
||||
Task InsertAsync(params TEntity[] entities);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of entities asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to insert.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous insert operation.</returns>
|
||||
Task InsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
void Update(TEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified entities.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities.</param>
|
||||
void Update(params TEntity[] entities);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified entities.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities.</param>
|
||||
void Update(IEnumerable<TEntity> entities);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the entity by the specified primary key.
|
||||
/// </summary>
|
||||
/// <param name="id">The primary key value.</param>
|
||||
void Delete(object id);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to delete.</param>
|
||||
void Delete(TEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified entities.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities.</param>
|
||||
void Delete(params TEntity[] entities);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified entities.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities.</param>
|
||||
void Delete(IEnumerable<TEntity> entities);
|
||||
|
||||
/// <summary>
|
||||
/// Change entity state for patch method on web api.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// /// <param name="state">The entity state.</param>
|
||||
void ChangeEntityState(TEntity entity, EntityState state);
|
||||
}
|
||||
}
|
20
Entities/Context/UnitOfWork/IRepositoryFactory.cs
Normal file
20
Entities/Context/UnitOfWork/IRepositoryFactory.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedDATA.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the interfaces for <see cref="IRepository{TEntity}"/> interfaces.
|
||||
/// </summary>
|
||||
public interface IRepositoryFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified repository for the <typeparamref name="TEntity"/>.
|
||||
/// </summary>
|
||||
/// <param name="hasCustomRepository"><c>True</c> if providing custom repositry</param>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <returns>An instance of type inherited from <see cref="IRepository{TEntity}"/> interface.</returns>
|
||||
IRepository<TEntity> GetRepository<TEntity>(bool hasCustomRepository = false) where TEntity : class;
|
||||
}
|
||||
}
|
78
Entities/Context/UnitOfWork/IUnitOfWork.cs
Normal file
78
Entities/Context/UnitOfWork/IUnitOfWork.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
|
||||
|
||||
namespace SharedDATA.Api
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface(s) for unit of work.
|
||||
/// </summary>
|
||||
public interface IUnitOfWork : IDisposable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Changes the database name. This require the databases in the same machine. NOTE: This only work for MySQL right now.
|
||||
/// </summary>
|
||||
/// <param name="database">The database name.</param>
|
||||
/// <remarks>
|
||||
/// This only been used for supporting multiple databases in the same model. This require the databases in the same machine.
|
||||
/// </remarks>
|
||||
void ChangeDatabase(string database);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified repository for the <typeparamref name="TEntity"/>.
|
||||
/// </summary>
|
||||
/// <param name="hasCustomRepository"><c>True</c> if providing custom repositry</param>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <returns>An instance of type inherited from <see cref="IRepository{TEntity}"/> interface.</returns>
|
||||
IRepository<TEntity> GetRepository<TEntity>(bool hasCustomRepository = false) where TEntity : class;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the db context.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
TContext GetDbContext<TContext>() where TContext : DbContext;
|
||||
|
||||
/// <summary>
|
||||
/// Saves all changes made in this context to the database.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if sayve changes ensure auto record the change history.</param>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
int SaveChanges(bool ensureAutoHistory = false);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves all changes made in this unit of work to the database.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if save changes ensure auto record the change history.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous save operation. The task result contains the number of state entities written to database.</returns>
|
||||
Task<int> SaveChangesAsync(bool ensureAutoHistory = false);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified raw SQL command.
|
||||
/// </summary>
|
||||
/// <param name="sql">The raw SQL.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>The number of state entities written to database.</returns>
|
||||
int ExecuteSqlCommand(string sql, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Uses raw SQL queries to fetch the specified <typeparamref name="TEntity"/> data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <param name="sql">The raw SQL.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>An <see cref="IQueryable{T}"/> that contains elements that satisfy the condition specified by raw SQL.</returns>
|
||||
IQueryable<TEntity> FromSql<TEntity>(string sql, params object[] parameters) where TEntity : class;
|
||||
|
||||
/// <summary>
|
||||
/// Uses TrakGrap Api to attach disconnected entities
|
||||
/// </summary>
|
||||
/// <param name="rootEntity"> Root entity</param>
|
||||
/// <param name="callback">Delegate to convert Object's State properities to Entities entry state.</param>
|
||||
void TrackGraph(object rootEntity, Action<EntityEntryGraphNode> callback);
|
||||
}
|
||||
}
|
27
Entities/Context/UnitOfWork/IUnitOfWorkOfT.cs
Normal file
27
Entities/Context/UnitOfWork/IUnitOfWorkOfT.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
namespace SharedDATA.Api
|
||||
{
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface(s) for generic unit of work.
|
||||
/// </summary>
|
||||
public interface IUnitOfWork<TContext> : IUnitOfWork where TContext : DbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the db context.
|
||||
/// </summary>
|
||||
/// <returns>The instance of type <typeparamref name="TContext"/>.</returns>
|
||||
TContext DbContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Saves all changes made in this context to the database with distributed transaction.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if save changes ensure auto record the change history.</param>
|
||||
/// <param name="unitOfWorks">An optional <see cref="IUnitOfWork"/> array.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous save operation. The task result contains the number of state entities written to database.</returns>
|
||||
Task<int> SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks);
|
||||
}
|
||||
}
|
942
Entities/Context/UnitOfWork/Repository.cs
Normal file
942
Entities/Context/UnitOfWork/Repository.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a default generic repository implements the <see cref="IRepository{TEntity}"/> interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
|
||||
{
|
||||
protected readonly DbContext _dbContext;
|
||||
protected readonly DbSet<TEntity> _dbSet;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Repository{TEntity}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dbContext">The database context.</param>
|
||||
public Repository(DbContext dbContext)
|
||||
{
|
||||
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||
_dbSet = _dbContext.Set<TEntity>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the table name. This require the tables in the same database.
|
||||
/// </summary>
|
||||
/// <param name="table"></param>
|
||||
/// <remarks>
|
||||
/// This only been used for supporting multiple tables in the same model. This require the tables in the same database.
|
||||
/// </remarks>
|
||||
public virtual void ChangeTable(string table)
|
||||
{
|
||||
if (_dbContext.Model.FindEntityType(typeof(TEntity)) is IConventionEntityType relational)
|
||||
{
|
||||
relational.SetTableName(table);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities. This method is not recommended
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IQueryable{TEntity}"/>.</returns>
|
||||
public IQueryable<TEntity> GetAll()
|
||||
{
|
||||
return _dbSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities. This method is not recommended
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>true</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>Ex: This method defaults to a read-only, no-tracking query.</remarks>
|
||||
public IQueryable<TEntity> GetAll(
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null, bool disableTracking = true, bool ignoreQueryFilters = false)
|
||||
{
|
||||
IQueryable<TEntity> 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;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPagedList{TEntity}"/> based on a predicate, orderby delegate and page information. This method default no-tracking query.
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="pageIndex">The index of page.</param>
|
||||
/// <param name="pageSize">The size of the page.</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
public virtual IPagedList<TEntity> GetPagedList(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
int pageIndex = 0,
|
||||
int pageSize = 20,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false)
|
||||
{
|
||||
IQueryable<TEntity> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPagedList{TEntity}"/> based on a predicate, orderby delegate and page information. This method default no-tracking query.
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="pageIndex">The index of page.</param>
|
||||
/// <param name="pageSize">The size of the page.</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="cancellationToken">
|
||||
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
|
||||
/// </param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
public virtual Task<IPagedList<TEntity>> GetPagedListAsync(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
int pageIndex = 0,
|
||||
int pageSize = 20,
|
||||
bool disableTracking = true,
|
||||
CancellationToken cancellationToken = default(CancellationToken),
|
||||
bool ignoreQueryFilters = false)
|
||||
{
|
||||
IQueryable<TEntity> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPagedList{TResult}"/> based on a predicate, orderby delegate and page information. This method default no-tracking query.
|
||||
/// </summary>
|
||||
/// <param name="selector">The selector for projection.</param>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="pageIndex">The index of page.</param>
|
||||
/// <param name="pageSize">The size of the page.</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TResult}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
public virtual IPagedList<TResult> GetPagedList<TResult>(Expression<Func<TEntity, TResult>> selector,
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
int pageIndex = 0,
|
||||
int pageSize = 20,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false)
|
||||
where TResult : class
|
||||
{
|
||||
IQueryable<TEntity> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPagedList{TEntity}"/> based on a predicate, orderby delegate and page information. This method default no-tracking query.
|
||||
/// </summary>
|
||||
/// <param name="selector">The selector for projection.</param>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="pageIndex">The index of page.</param>
|
||||
/// <param name="pageSize">The size of the page.</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="cancellationToken">
|
||||
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
|
||||
/// </param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
public virtual Task<IPagedList<TResult>> GetPagedListAsync<TResult>(Expression<Func<TEntity, TResult>> selector,
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
int pageIndex = 0,
|
||||
int pageSize = 20,
|
||||
bool disableTracking = true,
|
||||
CancellationToken cancellationToken = default(CancellationToken),
|
||||
bool ignoreQueryFilters = false)
|
||||
where TResult : class
|
||||
{
|
||||
IQueryable<TEntity> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method default no-tracking query.
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
public virtual TEntity GetFirstOrDefault(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false)
|
||||
{
|
||||
IQueryable<TEntity> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<TEntity> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false)
|
||||
{
|
||||
IQueryable<TEntity> 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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method default no-tracking query.
|
||||
/// </summary>
|
||||
/// <param name="selector">The selector for projection.</param>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>This method default no-tracking query.</remarks>
|
||||
public virtual TResult GetFirstOrDefault<TResult>(Expression<Func<TEntity, TResult>> selector,
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true,
|
||||
bool ignoreQueryFilters = false)
|
||||
{
|
||||
IQueryable<TEntity> 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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<TResult> GetFirstOrDefaultAsync<TResult>(Expression<Func<TEntity, TResult>> selector,
|
||||
Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true, bool ignoreQueryFilters = false)
|
||||
{
|
||||
IQueryable<TEntity> 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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses raw SQL queries to fetch the specified <typeparamref name="TEntity" /> data.
|
||||
/// </summary>
|
||||
/// <param name="sql">The raw SQL.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>An <see cref="IQueryable{TEntity}" /> that contains elements that satisfy the condition specified by raw SQL.</returns>
|
||||
public virtual IQueryable<TEntity> FromSql(string sql, params object[] parameters) => _dbSet.FromSqlRaw(sql, parameters);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="keyValues">The values of the primary key for the entity to be found.</param>
|
||||
/// <returns>The found entity or null.</returns>
|
||||
public virtual TEntity Find(params object[] keyValues) => _dbSet.Find(keyValues);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="keyValues">The values of the primary key for the entity to be found.</param>
|
||||
/// <returns>A <see cref="Task{TEntity}" /> that represents the asynchronous insert operation.</returns>
|
||||
public virtual ValueTask<TEntity> FindAsync(params object[] keyValues) => _dbSet.FindAsync(keyValues);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="keyValues">The values of the primary key for the entity to be found.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
|
||||
/// <returns>A <see cref="Task{TEntity}"/> that represents the asynchronous find operation. The task result contains the found entity or null.</returns>
|
||||
public virtual ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken) => _dbSet.FindAsync(keyValues, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public virtual int Count(Expression<Func<TEntity, bool>> predicate = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
{
|
||||
return _dbSet.Count();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _dbSet.Count(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets async the count based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
{
|
||||
return await _dbSet.CountAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await _dbSet.CountAsync(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the long count based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public virtual long LongCount(Expression<Func<TEntity, bool>> predicate = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
{
|
||||
return _dbSet.LongCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _dbSet.LongCount(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets async the long count based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
{
|
||||
return await _dbSet.LongCountAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await _dbSet.LongCountAsync(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
public virtual T Max<T>(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, T>> selector = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return _dbSet.Max(selector);
|
||||
else
|
||||
return _dbSet.Where(predicate).Max(selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async max based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
public virtual async Task<T> MaxAsync<T>(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, T>> selector = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return await _dbSet.MaxAsync(selector);
|
||||
else
|
||||
return await _dbSet.Where(predicate).MaxAsync(selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the min based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
public virtual T Min<T>(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, T>> selector = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return _dbSet.Min(selector);
|
||||
else
|
||||
return _dbSet.Where(predicate).Min(selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async min based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
public virtual async Task<T> MinAsync<T>(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, T>> selector = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return await _dbSet.MinAsync(selector);
|
||||
else
|
||||
return await _dbSet.Where(predicate).MinAsync(selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the average based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
public virtual decimal Average(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, decimal>> selector = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return _dbSet.Average(selector);
|
||||
else
|
||||
return _dbSet.Where(predicate).Average(selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async average based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
public virtual async Task<decimal> AverageAsync(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, decimal>> selector = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return await _dbSet.AverageAsync(selector);
|
||||
else
|
||||
return await _dbSet.Where(predicate).AverageAsync(selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sum based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
public virtual decimal Sum(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, decimal>> selector = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return _dbSet.Sum(selector);
|
||||
else
|
||||
return _dbSet.Where(predicate).Sum(selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async sum based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// /// <param name="selector"></param>
|
||||
/// <returns>decimal</returns>
|
||||
public virtual async Task<decimal> SumAsync(Expression<Func<TEntity, bool>> predicate = null, Expression<Func<TEntity, decimal>> selector = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return await _dbSet.SumAsync(selector);
|
||||
else
|
||||
return await _dbSet.Where(predicate).SumAsync(selector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exists based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
public bool Exists(Expression<Func<TEntity, bool>> selector = null)
|
||||
{
|
||||
if (selector == null)
|
||||
{
|
||||
return _dbSet.Any();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _dbSet.Any(selector);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the async exists based on a predicate.
|
||||
/// </summary>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> selector = null)
|
||||
{
|
||||
if (selector == null)
|
||||
{
|
||||
return await _dbSet.AnyAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await _dbSet.AnyAsync(selector);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Inserts a new entity synchronously.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to insert.</param>
|
||||
public virtual TEntity Insert(TEntity entity)
|
||||
{
|
||||
return _dbSet.Add(entity).Entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of entities synchronously.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to insert.</param>
|
||||
public virtual void Insert(params TEntity[] entities) => _dbSet.AddRange(entities);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of entities synchronously.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to insert.</param>
|
||||
public virtual void Insert(IEnumerable<TEntity> entities) => _dbSet.AddRange(entities);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new entity asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to insert.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous insert operation.</returns>
|
||||
public virtual ValueTask<EntityEntry<TEntity>> 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;
|
||||
//}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of entities asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to insert.</param>
|
||||
/// <returns>A <see cref="Task" /> that represents the asynchronous insert operation.</returns>
|
||||
public virtual Task InsertAsync(params TEntity[] entities) => _dbSet.AddRangeAsync(entities);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of entities asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to insert.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous insert operation.</returns>
|
||||
public virtual Task InsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default(CancellationToken)) => _dbSet.AddRangeAsync(entities, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
public virtual void Update(TEntity entity)
|
||||
{
|
||||
_dbSet.Update(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
public virtual void UpdateAsync(TEntity entity)
|
||||
{
|
||||
_dbSet.Update(entity);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified entities.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities.</param>
|
||||
public virtual void Update(params TEntity[] entities) => _dbSet.UpdateRange(entities);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified entities.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities.</param>
|
||||
public virtual void Update(IEnumerable<TEntity> entities) => _dbSet.UpdateRange(entities);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to delete.</param>
|
||||
public virtual void Delete(TEntity entity) => _dbSet.Remove(entity);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the entity by the specified primary key.
|
||||
/// </summary>
|
||||
/// <param name="id">The primary key value.</param>
|
||||
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 property = typeInfo.GetProperty(key?.Name);
|
||||
if (property != null)
|
||||
{
|
||||
var entity = Activator.CreateInstance<TEntity>();
|
||||
property.SetValue(entity, id);
|
||||
_dbContext.Entry(entity).State = EntityState.Deleted;
|
||||
}
|
||||
else
|
||||
{
|
||||
var entity = _dbSet.Find(id);
|
||||
if (entity != null)
|
||||
{
|
||||
Delete(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified entities.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities.</param>
|
||||
public virtual void Delete(params TEntity[] entities) => _dbSet.RemoveRange(entities);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified entities.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities.</param>
|
||||
public virtual void Delete(IEnumerable<TEntity> entities) => _dbSet.RemoveRange(entities);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities. This method is not recommended
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IQueryable{TEntity}"/>.</returns>
|
||||
public async Task<IList<TEntity>> GetAllAsync()
|
||||
{
|
||||
return await _dbSet.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities. This method is not recommended
|
||||
/// </summary>
|
||||
/// <param name="predicate">A function to test each element for a condition.</param>
|
||||
/// <param name="orderBy">A function to order elements.</param>
|
||||
/// <param name="include">A function to include navigation properties</param>
|
||||
/// <param name="disableTracking"><c>true</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
|
||||
/// <param name="ignoreQueryFilters">Ignore query filters</param>
|
||||
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
|
||||
/// <remarks>Ex: This method defaults to a read-only, no-tracking query.</remarks>
|
||||
public async Task<IList<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> predicate = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
|
||||
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
|
||||
bool disableTracking = true, bool ignoreQueryFilters = false)
|
||||
{
|
||||
IQueryable<TEntity> 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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change entity state for patch method on web api.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// /// <param name="state">The entity state.</param>
|
||||
public void ChangeEntityState(TEntity entity, EntityState state)
|
||||
{
|
||||
_dbContext.Entry(entity).State = state;
|
||||
}
|
||||
}
|
||||
}
|
225
Entities/Context/UnitOfWork/UnitOfWork.cs
Normal file
225
Entities/Context/UnitOfWork/UnitOfWork.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
|
||||
namespace SharedDATA.Api
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the default implementation of the <see cref="IUnitOfWork"/> and <see cref="IUnitOfWork{TContext}"/> interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The type of the db context.</typeparam>
|
||||
public class UnitOfWork<TContext> : IRepositoryFactory, IUnitOfWork<TContext>, IUnitOfWork where TContext : DbContext
|
||||
{
|
||||
private readonly TContext _context;
|
||||
private bool disposed = false;
|
||||
private Dictionary<Type, object> repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnitOfWork{TContext}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
public UnitOfWork(TContext context)
|
||||
{
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the db context.
|
||||
/// </summary>
|
||||
/// <returns>The instance of type <typeparamref name="TContext"/>.</returns>
|
||||
public TContext DbContext => _context;
|
||||
|
||||
/// <summary>
|
||||
/// Changes the database name. This require the databases in the same machine. NOTE: This only work for MySQL right now.
|
||||
/// </summary>
|
||||
/// <param name="database">The database name.</param>
|
||||
/// <remarks>
|
||||
/// This only been used for supporting multiple databases in the same model. This require the databases in the same machine.
|
||||
/// </remarks>
|
||||
public void ChangeDatabase(string database)
|
||||
{
|
||||
var connection = _context.Database.GetDbConnection();
|
||||
if (connection.State.HasFlag(ConnectionState.Open))
|
||||
{
|
||||
connection.ChangeDatabase(database);
|
||||
}
|
||||
else
|
||||
{
|
||||
var connectionString = Regex.Replace(connection.ConnectionString.Replace(" ", ""), @"(?<=[Dd]atabase=)\w+(?=;)", database, RegexOptions.Singleline);
|
||||
connection.ConnectionString = connectionString;
|
||||
}
|
||||
|
||||
// Following code only working for mysql.
|
||||
var items = _context.Model.GetEntityTypes();
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item is IConventionEntityType entityType)
|
||||
{
|
||||
entityType.SetSchema(database);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the db context.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TDbContext GetDbContext<TDbContext>() where TDbContext : DbContext
|
||||
{
|
||||
if (_context == null)
|
||||
return null;
|
||||
return _context as TDbContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified repository for the <typeparamref name="TEntity"/>.
|
||||
/// </summary>
|
||||
/// <param name="hasCustomRepository"><c>True</c> if providing custom repositry</param>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <returns>An instance of type inherited from <see cref="IRepository{TEntity}"/> interface.</returns>
|
||||
public IRepository<TEntity> GetRepository<TEntity>(bool hasCustomRepository = false) where TEntity : class
|
||||
{
|
||||
if (repositories == null)
|
||||
{
|
||||
repositories = new Dictionary<Type, object>();
|
||||
}
|
||||
|
||||
// what's the best way to support custom reposity?
|
||||
if (hasCustomRepository)
|
||||
{
|
||||
var customRepo = _context.GetService<IRepository<TEntity>>();
|
||||
if (customRepo != null)
|
||||
{
|
||||
return customRepo;
|
||||
}
|
||||
}
|
||||
|
||||
var type = typeof(TEntity);
|
||||
if (!repositories.ContainsKey(type))
|
||||
{
|
||||
repositories[type] = new Repository<TEntity>(_context);
|
||||
}
|
||||
|
||||
return (IRepository<TEntity>)repositories[type];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified raw SQL command.
|
||||
/// </summary>
|
||||
/// <param name="sql">The raw SQL.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>The number of state entities written to database.</returns>
|
||||
public int ExecuteSqlCommand(string sql, params object[] parameters) => _context.Database.ExecuteSqlRaw(sql, parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Uses raw SQL queries to fetch the specified <typeparamref name="TEntity" /> data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <param name="sql">The raw SQL.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>An <see cref="IQueryable{T}" /> that contains elements that satisfy the condition specified by raw SQL.</returns>
|
||||
public IQueryable<TEntity> FromSql<TEntity>(string sql, params object[] parameters) where TEntity : class => _context.Set<TEntity>().FromSqlRaw(sql, parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Saves all changes made in this context to the database.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if save changes ensure auto record the change history.</param>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public int SaveChanges(bool ensureAutoHistory = false)
|
||||
{
|
||||
if (ensureAutoHistory)
|
||||
{
|
||||
_context.EnsureAutoHistory();
|
||||
}
|
||||
|
||||
return _context.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves all changes made in this unit of work to the database.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if save changes ensure auto record the change history.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous save operation. The task result contains the number of state entities written to database.</returns>
|
||||
public async Task<int> SaveChangesAsync(bool ensureAutoHistory = false)
|
||||
{
|
||||
if (ensureAutoHistory)
|
||||
{
|
||||
_context.EnsureAutoHistory();
|
||||
}
|
||||
|
||||
return await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves all changes made in this context to the database with distributed transaction.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if save changes ensure auto record the change history.</param>
|
||||
/// <param name="unitOfWorks">An optional <see cref="IUnitOfWork"/> array.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous save operation. The task result contains the number of state entities written to database.</returns>
|
||||
public async Task<int> SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks)
|
||||
{
|
||||
using (var ts = new TransactionScope())
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var unitOfWork in unitOfWorks)
|
||||
{
|
||||
count += await unitOfWork.SaveChangesAsync(ensureAutoHistory);
|
||||
}
|
||||
|
||||
count += await SaveChangesAsync(ensureAutoHistory);
|
||||
|
||||
ts.Complete();
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">The disposing.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// clear repositories
|
||||
if (repositories != null)
|
||||
{
|
||||
repositories.Clear();
|
||||
}
|
||||
|
||||
// dispose the db context.
|
||||
_context.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public void TrackGraph(object rootEntity, Action<EntityEntryGraphNode> callback)
|
||||
{
|
||||
_context.ChangeTracker.TrackGraph(rootEntity, callback);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
|
||||
|
||||
namespace SharedDATA.Api
|
||||
{
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for setting up unit of work related services in an <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
public static class UnitOfWorkServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers the unit of work given context as a service in the <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The type of the db context.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
|
||||
/// <returns>The same service collection so that multiple calls can be chained.</returns>
|
||||
/// <remarks>
|
||||
/// This method only support one db context, if been called more than once, will throw exception.
|
||||
/// </remarks>
|
||||
public static IServiceCollection AddUnitOfWork<TContext>(this IServiceCollection services) where TContext : DbContext
|
||||
{
|
||||
services.AddScoped<IRepositoryFactory, UnitOfWork<TContext>>();
|
||||
// Following has a issue: IUnitOfWork cannot support multiple dbcontext/database,
|
||||
// that means cannot call AddUnitOfWork<TContext> multiple times.
|
||||
// Solution: check IUnitOfWork whether or null
|
||||
services.AddScoped<IUnitOfWork, UnitOfWork<TContext>>();
|
||||
services.AddScoped<IUnitOfWork<TContext>, UnitOfWork<TContext>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the unit of work given context as a service in the <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext1">The type of the db context.</typeparam>
|
||||
/// <typeparam name="TContext2">The type of the db context.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
|
||||
/// <returns>The same service collection so that multiple calls can be chained.</returns>
|
||||
/// <remarks>
|
||||
/// This method only support one db context, if been called more than once, will throw exception.
|
||||
/// </remarks>
|
||||
public static IServiceCollection AddUnitOfWork<TContext1, TContext2>(this IServiceCollection services)
|
||||
where TContext1 : DbContext
|
||||
where TContext2 : DbContext
|
||||
{
|
||||
services.AddScoped<IUnitOfWork<TContext1>, UnitOfWork<TContext1>>();
|
||||
services.AddScoped<IUnitOfWork<TContext2>, UnitOfWork<TContext2>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the unit of work given context as a service in the <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext1">The type of the db context.</typeparam>
|
||||
/// <typeparam name="TContext2">The type of the db context.</typeparam>
|
||||
/// <typeparam name="TContext3">The type of the db context.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
|
||||
/// <returns>The same service collection so that multiple calls can be chained.</returns>
|
||||
/// <remarks>
|
||||
/// This method only support one db context, if been called more than once, will throw exception.
|
||||
/// </remarks>
|
||||
public static IServiceCollection AddUnitOfWork<TContext1, TContext2, TContext3>(this IServiceCollection services)
|
||||
where TContext1 : DbContext
|
||||
where TContext2 : DbContext
|
||||
where TContext3 : DbContext
|
||||
{
|
||||
services.AddScoped<IUnitOfWork<TContext1>, UnitOfWork<TContext1>>();
|
||||
services.AddScoped<IUnitOfWork<TContext2>, UnitOfWork<TContext2>>();
|
||||
services.AddScoped<IUnitOfWork<TContext3>, UnitOfWork<TContext3>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the unit of work given context as a service in the <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext1">The type of the db context.</typeparam>
|
||||
/// <typeparam name="TContext2">The type of the db context.</typeparam>
|
||||
/// <typeparam name="TContext3">The type of the db context.</typeparam>
|
||||
/// <typeparam name="TContext4">The type of the db context.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
|
||||
/// <returns>The same service collection so that multiple calls can be chained.</returns>
|
||||
/// <remarks>
|
||||
/// This method only support one db context, if been called more than once, will throw exception.
|
||||
/// </remarks>
|
||||
public static IServiceCollection AddUnitOfWork<TContext1, TContext2, TContext3, TContext4>(this IServiceCollection services)
|
||||
where TContext1 : DbContext
|
||||
where TContext2 : DbContext
|
||||
where TContext3 : DbContext
|
||||
where TContext4 : DbContext
|
||||
{
|
||||
services.AddScoped<IUnitOfWork<TContext1>, UnitOfWork<TContext1>>();
|
||||
services.AddScoped<IUnitOfWork<TContext2>, UnitOfWork<TContext2>>();
|
||||
services.AddScoped<IUnitOfWork<TContext3>, UnitOfWork<TContext3>>();
|
||||
services.AddScoped<IUnitOfWork<TContext4>, UnitOfWork<TContext4>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the custom repository as a service in the <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TRepository">The type of the custom repositry.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
|
||||
/// <returns>The same service collection so that multiple calls can be chained.</returns>
|
||||
public static IServiceCollection AddCustomRepository<TEntity, TRepository>(this IServiceCollection services)
|
||||
where TEntity : class
|
||||
where TRepository : class, IRepository<TEntity>
|
||||
{
|
||||
services.AddScoped<IRepository<TEntity>, TRepository>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
63
Entities/Contracts/Assignment.cs
Normal file
63
Entities/Contracts/Assignment.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("assignments")]
|
||||
public class Assignment
|
||||
{
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("title")]
|
||||
[StringLength(255)]
|
||||
public string Title { get; set; }
|
||||
|
||||
[Column("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("due_date")]
|
||||
public DateTime DueDate { get; set; }
|
||||
|
||||
[Column("total_points")]
|
||||
public decimal? TotalPoints { get; set; }
|
||||
|
||||
[Column("created_by")]
|
||||
[ForeignKey("Creator")]
|
||||
public Guid CreatedBy { get; set; }
|
||||
|
||||
[Column("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Column("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public User Creator { get; set; }
|
||||
public ICollection<AssignmentClass> AssignmentClasses { get; set; }
|
||||
public ICollection<AssignmentGroup> AssignmentGroups { get; set; }
|
||||
public ICollection<AssignmentAttachment> AssignmentAttachments { get; set; }
|
||||
public ICollection<Submission> Submissions { get; set; }
|
||||
|
||||
public Assignment()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
|
||||
Submissions = new HashSet<Submission>();
|
||||
AssignmentGroups = new HashSet<AssignmentGroup>();
|
||||
AssignmentClasses = new HashSet<AssignmentClass>();
|
||||
AssignmentAttachments = new HashSet<AssignmentAttachment>();
|
||||
}
|
||||
}
|
||||
}
|
49
Entities/Contracts/AssignmentAttachment.cs
Normal file
49
Entities/Contracts/AssignmentAttachment.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("assignment_attachments")]
|
||||
public class AssignmentAttachment
|
||||
{
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("assignment_id")]
|
||||
[ForeignKey("Assignment")]
|
||||
public Guid AssignmentId { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("file_path")]
|
||||
[StringLength(255)]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("file_name")]
|
||||
[StringLength(255)]
|
||||
public string FileName { get; set; }
|
||||
|
||||
[Column("uploaded_at")]
|
||||
public DateTime UploadedAt { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public Assignment Assignment { get; set; }
|
||||
|
||||
public AssignmentAttachment()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
36
Entities/Contracts/AssignmentClass.cs
Normal file
36
Entities/Contracts/AssignmentClass.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("assignment_class")]
|
||||
public class AssignmentClass
|
||||
{
|
||||
[Key]
|
||||
[Column("assignment_id", Order = 0)]
|
||||
[ForeignKey("Assignment")]
|
||||
public Guid AssignmentId { get; set; }
|
||||
|
||||
[Key]
|
||||
[Column("class_id", Order = 1)]
|
||||
[ForeignKey("Class")]
|
||||
public Guid ClassId { get; set; }
|
||||
|
||||
[Column("assigned_at")]
|
||||
public DateTime AssignedAt { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public Assignment Assignment { get; set; }
|
||||
public Class Class { get; set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
59
Entities/Contracts/AssignmentGroup.cs
Normal file
59
Entities/Contracts/AssignmentGroup.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("assignment_group")]
|
||||
public class AssignmentGroup
|
||||
{
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("assignment")]
|
||||
[ForeignKey("Assignment")]
|
||||
public Guid AssignmentId { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("title")]
|
||||
[MaxLength(65535)]
|
||||
public string Title { get; set; }
|
||||
|
||||
[Column("descript")]
|
||||
[MaxLength(65535)]
|
||||
public string Descript { get; set; }
|
||||
|
||||
|
||||
[Column("total_points")]
|
||||
public decimal? TotalPoints { get; set; }
|
||||
|
||||
[Column("number")]
|
||||
public byte Number { get; set; }
|
||||
|
||||
[Column("parent_group")]
|
||||
public Guid? ParentGroup { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public Assignment Assignment { get; set; }
|
||||
public AssignmentGroup ParentAssignmentGroup { get; set;}
|
||||
public ICollection<AssignmentGroup> ChildAssignmentGroups { get; set; }
|
||||
public ICollection<AssignmentQuestion> AssignmentQuestions { get; set; }
|
||||
|
||||
|
||||
public AssignmentGroup()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
ChildAssignmentGroups = new HashSet<AssignmentGroup>();
|
||||
AssignmentQuestions = new HashSet<AssignmentQuestion>();
|
||||
}
|
||||
}
|
||||
}
|
50
Entities/Contracts/AssignmentQuestion.cs
Normal file
50
Entities/Contracts/AssignmentQuestion.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("assignment_questions")]
|
||||
public class AssignmentQuestion
|
||||
{
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("question_id")]
|
||||
[ForeignKey("Question")]
|
||||
public Guid QuestionId { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("question_number")]
|
||||
public uint QuestionNumber { get; set; }
|
||||
|
||||
[Column("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("detail_id")]
|
||||
[ForeignKey("AssignmentGroup")]
|
||||
public Guid AssignmentGroupId { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
|
||||
public Question Question { get; set; }
|
||||
public ICollection<SubmissionDetail> SubmissionDetails { get; set; }
|
||||
public AssignmentGroup AssignmentGroup { get; set; }
|
||||
|
||||
public AssignmentQuestion()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
SubmissionDetails = new HashSet<SubmissionDetail>();
|
||||
}
|
||||
}
|
||||
}
|
55
Entities/Contracts/Class.cs
Normal file
55
Entities/Contracts/Class.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("classes")]
|
||||
public class Class
|
||||
{
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Column("grade")]
|
||||
public byte Grade { get; set; }
|
||||
|
||||
[Column("class")]
|
||||
public byte Number { get; set; }
|
||||
|
||||
[Column("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Column("head_teacher_id")]
|
||||
public Guid? HeadTeacherId { get; set; }
|
||||
public User HeadTeacher { get; set; }
|
||||
|
||||
[Column("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Column("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public ICollection<ClassTeacher> ClassTeachers { get; set; }
|
||||
public ICollection<ClassStudent> ClassStudents { get; set; }
|
||||
public ICollection<AssignmentClass> AssignmentClasses { get; set; }
|
||||
|
||||
public Class()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
Grade = 0;
|
||||
Number = 0;
|
||||
ClassStudents = new HashSet<ClassStudent>();
|
||||
ClassTeachers = new HashSet<ClassTeacher>();
|
||||
AssignmentClasses = new HashSet<AssignmentClass>();
|
||||
}
|
||||
}
|
||||
}
|
34
Entities/Contracts/ClassStudent.cs
Normal file
34
Entities/Contracts/ClassStudent.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("class_student")]
|
||||
public class ClassStudent
|
||||
{
|
||||
[Key]
|
||||
[Column("class_id", Order = 0)]
|
||||
[ForeignKey("Class")]
|
||||
public Guid ClassId { get; set; }
|
||||
|
||||
[Key]
|
||||
[Column("student_id", Order = 1)]
|
||||
[ForeignKey("Student")]
|
||||
public Guid StudentId { get; set; }
|
||||
|
||||
[Column("enrollment_date")]
|
||||
public DateTime EnrollmentDate { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public Class Class { get; set; }
|
||||
public User Student { get; set; }
|
||||
}
|
||||
}
|
27
Entities/Contracts/ClassTeacher.cs
Normal file
27
Entities/Contracts/ClassTeacher.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("class_teachers")]
|
||||
public class ClassTeacher
|
||||
{
|
||||
[Key]
|
||||
[Column("class_id")]
|
||||
public Guid ClassId { get; set; }
|
||||
public Class Class { get; set; }
|
||||
|
||||
[Key]
|
||||
[Column("teacher_id")]
|
||||
public Guid TeacherId { get; set; }
|
||||
public User Teacher { get; set; }
|
||||
|
||||
[Column("subject_taught")]
|
||||
public string SubjectTaught { get; set; }
|
||||
}
|
||||
}
|
99
Entities/Contracts/Question.cs
Normal file
99
Entities/Contracts/Question.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("questions")]
|
||||
public class Question
|
||||
{
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("question_text")]
|
||||
[MaxLength(65535)]
|
||||
public string QuestionText { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("question_type")]
|
||||
[MaxLength(20)]
|
||||
public QuestionType QuestionType { get; set; }
|
||||
|
||||
[Column("correct_answer")]
|
||||
[MaxLength(65535)]
|
||||
public string CorrectAnswer { get; set; }
|
||||
|
||||
[Column("difficulty_level")]
|
||||
[MaxLength(10)]
|
||||
public DifficultyLevel DifficultyLevel { get; set; }
|
||||
|
||||
[Column("subject_area")]
|
||||
public SubjectAreaEnum SubjectArea { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("created_by")]
|
||||
[ForeignKey("Creator")]
|
||||
public Guid CreatedBy { get; set; }
|
||||
|
||||
[Column("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Column("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public User Creator { get; set; }
|
||||
public ICollection<AssignmentQuestion> AssignmentQuestions { get; set; }
|
||||
|
||||
public Question()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
AssignmentQuestions = new HashSet<AssignmentQuestion>();
|
||||
}
|
||||
}
|
||||
|
||||
public enum DifficultyLevel
|
||||
{
|
||||
easy,
|
||||
medium,
|
||||
hard
|
||||
}
|
||||
|
||||
public enum QuestionType
|
||||
{
|
||||
Unknown, // 可以有一个未知类型或作为默认
|
||||
Spelling, // 拼写
|
||||
Pronunciation, // 给带点字选择正确读音
|
||||
WordFormation, // 组词
|
||||
FillInTheBlanks, // 选词填空 / 补充词语
|
||||
SentenceDictation, // 默写句子
|
||||
SentenceRewriting, // 仿句 / 改写句子
|
||||
ReadingComprehension, // 阅读理解
|
||||
Composition // 作文
|
||||
// ... 添加您其他题目类型
|
||||
}
|
||||
|
||||
public enum SubjectAreaEnum // 建议命名为 SubjectAreaEnum 以避免与属性名冲突
|
||||
{
|
||||
Unknown, // 未知或默认
|
||||
Mathematics, // 数学
|
||||
Physics, // 物理
|
||||
Chemistry, // 化学
|
||||
Biology, // 生物
|
||||
History, // 历史
|
||||
Geography, // 地理
|
||||
Literature, // 语文/文学
|
||||
English, // 英语
|
||||
ComputerScience, // 计算机科学
|
||||
// ... 你可以根据需要添加更多科目
|
||||
}
|
||||
}
|
80
Entities/Contracts/Submission.cs
Normal file
80
Entities/Contracts/Submission.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("submissions")]
|
||||
public class Submission
|
||||
{
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("assignment_id")]
|
||||
[ForeignKey("Assignment")]
|
||||
public Guid AssignmentId { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("student_id")]
|
||||
[ForeignKey("Student")]
|
||||
public Guid StudentId { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("attempt_number")]
|
||||
public Guid AttemptNumber { get; set; }
|
||||
|
||||
[Column("submission_time")]
|
||||
public DateTime SubmissionTime { get; set; }
|
||||
|
||||
[Column("overall_grade")]
|
||||
[Precision(5, 2)]
|
||||
public decimal? OverallGrade { get; set; }
|
||||
|
||||
[Column("overall_feedback")]
|
||||
public string OverallFeedback { get; set; }
|
||||
|
||||
[Column("graded_by")]
|
||||
[ForeignKey("Grader")]
|
||||
public Guid? GradedBy { get; set; }
|
||||
|
||||
[Column("graded_at")]
|
||||
public DateTime? GradedAt { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("status")]
|
||||
public SubmissionStatus Status { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public Assignment Assignment { get; set; }
|
||||
public User Student { get; set; }
|
||||
public User Grader { get; set; }
|
||||
public ICollection<SubmissionDetail> SubmissionDetails { get; set; }
|
||||
|
||||
public Submission()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
SubmissionDetails = new HashSet<SubmissionDetail>();
|
||||
}
|
||||
}
|
||||
|
||||
public enum SubmissionStatus
|
||||
{
|
||||
Pending, // 待提交/未开始
|
||||
Submitted, // 已提交
|
||||
Graded, // 已批改
|
||||
Resubmission, // 待重新提交 (如果允许)
|
||||
Late, // 迟交
|
||||
Draft, // 草稿
|
||||
// ... 添加你需要的其他状态
|
||||
}
|
||||
}
|
66
Entities/Contracts/SubmissionDetail.cs
Normal file
66
Entities/Contracts/SubmissionDetail.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
[Table("submission_details")]
|
||||
public class SubmissionDetail
|
||||
{
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("submission_id")]
|
||||
[ForeignKey("Submission")]
|
||||
public Guid SubmissionId { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("student_id")]
|
||||
[ForeignKey("User")]
|
||||
public Guid StudentId { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("assignment_question_id")]
|
||||
[ForeignKey("AssignmentQuestion")]
|
||||
public Guid AssignmentQuestionId { get; set; }
|
||||
|
||||
[Column("student_answer")]
|
||||
public string StudentAnswer { get; set; }
|
||||
|
||||
[Column("is_correct")]
|
||||
public bool? IsCorrect { get; set; }
|
||||
|
||||
[Column("points_awarded")]
|
||||
[Precision(5, 2)]
|
||||
public decimal? PointsAwarded { get; set; }
|
||||
|
||||
[Column("teacher_feedback")]
|
||||
public string TeacherFeedback { get; set; }
|
||||
|
||||
[Column("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Column("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public Submission Submission { get; set; }
|
||||
public User User { get; set; }
|
||||
public AssignmentQuestion AssignmentQuestion { get; set; }
|
||||
|
||||
public SubmissionDetail()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
}
|
||||
}
|
47
Entities/Contracts/User.cs
Normal file
47
Entities/Contracts/User.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Entities.Contracts
|
||||
{
|
||||
public enum UserRoles
|
||||
{
|
||||
Student,
|
||||
Teacher,
|
||||
Administrator
|
||||
}
|
||||
|
||||
public class User : IdentityUser<Guid>
|
||||
{
|
||||
public string? RefreshToken { get; set; }
|
||||
public DateTime? RefreshTokenExpiryTime { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
|
||||
[Column("deleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
public ICollection<ClassTeacher> TaughtClassesLink { get; set; }
|
||||
public ICollection<ClassStudent> EnrolledClassesLink { get; set; }
|
||||
|
||||
public ICollection<Question> CreatedQuestions { get; set; }
|
||||
public ICollection<Assignment> CreatedAssignments { get; set; }
|
||||
|
||||
public ICollection<SubmissionDetail> SubmissionDetails { get; set; }
|
||||
public ICollection<Submission> SubmissionsAsStudent { get; set; }
|
||||
public ICollection<Submission> GradedSubmissions { get; set; }
|
||||
|
||||
public User()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
SecurityStamp = Guid.NewGuid().ToString();
|
||||
|
||||
CreatedQuestions = new HashSet<Question>();
|
||||
TaughtClassesLink = new HashSet<ClassTeacher>();
|
||||
EnrolledClassesLink = new HashSet<ClassStudent>();
|
||||
CreatedAssignments = new HashSet<Assignment>();
|
||||
GradedSubmissions = new HashSet<Submission>();
|
||||
SubmissionsAsStudent = new HashSet<Submission>();
|
||||
}
|
||||
}
|
||||
}
|
23
Entities/DTO/ApiResponse.cs
Normal file
23
Entities/DTO/ApiResponse.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public class ApiResponse
|
||||
{
|
||||
public ApiResponse(string message, bool status = false)
|
||||
{
|
||||
this.Message = message;
|
||||
this.Status = status;
|
||||
}
|
||||
|
||||
public ApiResponse(bool status, object result)
|
||||
{
|
||||
this.Status = status;
|
||||
this.Result = result;
|
||||
}
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public bool Status { get; set; }
|
||||
|
||||
public object Result { get; set; }
|
||||
}
|
||||
}
|
12
Entities/DTO/AuthResponseDto.cs
Normal file
12
Entities/DTO/AuthResponseDto.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class AuthResponseDto
|
||||
{
|
||||
public bool IsAuthSuccessful { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
public string? Token { get; set; }
|
||||
public string? RefreshToken { get; set; }
|
||||
public bool Is2StepVerificationRequired { get; set; }
|
||||
public string Provider { get; set; }
|
||||
}
|
||||
}
|
21
Entities/DTO/ClassDto.cs
Normal file
21
Entities/DTO/ClassDto.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class ClassDto
|
||||
{
|
||||
public byte Class { get; set; }
|
||||
|
||||
[StringLength(50, ErrorMessage = "班级名称不能超过 50 个字符。")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "年级是必填项。")]
|
||||
[Range(1, 12, ErrorMessage = "年级编号必须在 1 到 12 之间。")]
|
||||
public byte Grade { get; set; }
|
||||
|
||||
public string Description { get; set; } = "HELLO WORLD";
|
||||
|
||||
|
||||
public int? HeadTeacherId { get; set; }
|
||||
}
|
||||
}
|
17
Entities/DTO/ForgotPasswordDto.cs
Normal file
17
Entities/DTO/ForgotPasswordDto.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class ForgotPasswordDto
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
public string ClientURI { get; set; }
|
||||
}
|
||||
}
|
8
Entities/DTO/RefreshTokenDto.cs
Normal file
8
Entities/DTO/RefreshTokenDto.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class RefreshTokenDto
|
||||
{
|
||||
public string? Token { get; set; }
|
||||
public string? RefreshToken { get; set; }
|
||||
}
|
||||
}
|
22
Entities/DTO/ResetPasswordDto.cs
Normal file
22
Entities/DTO/ResetPasswordDto.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class ResetPasswordDto
|
||||
{
|
||||
[Required(ErrorMessage = "Password is required")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[Compare(nameof(Password), ErrorMessage = "The password and confirmation password do not match")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
14
Entities/DTO/ResetPasswordResponseDto.cs
Normal file
14
Entities/DTO/ResetPasswordResponseDto.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class ResetPasswordResponseDto
|
||||
{
|
||||
public bool IsResetPasswordSuccessful { get; set; }
|
||||
public IEnumerable<string> Errors { get; set; } = Enumerable.Empty<string>();
|
||||
}
|
||||
}
|
14
Entities/DTO/ResponseDto.cs
Normal file
14
Entities/DTO/ResponseDto.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class ResponseDto
|
||||
{
|
||||
public bool IsSuccessfulRegistration { get; set; }
|
||||
public IEnumerable<string> Errors { get; set; } = Enumerable.Empty<string>();
|
||||
}
|
||||
}
|
17
Entities/DTO/TwoFactorVerificationDto.cs
Normal file
17
Entities/DTO/TwoFactorVerificationDto.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class TwoFactorVerificationDto
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string Provider { get; set; }
|
||||
[Required(ErrorMessage = "Token is required")]
|
||||
public string TwoFactorToken { get; set; }
|
||||
}
|
||||
}
|
12
Entities/DTO/UserForAuthenticationDto.cs
Normal file
12
Entities/DTO/UserForAuthenticationDto.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class UserForAuthenticationDto
|
||||
{
|
||||
[Required(ErrorMessage = "Email is required")]
|
||||
public string? Email { get; set; }
|
||||
[Required(ErrorMessage = "Password is required")]
|
||||
public string? Password { get; set; }
|
||||
}
|
||||
}
|
51
Entities/DTO/UserForRegistrationDto.cs
Normal file
51
Entities/DTO/UserForRegistrationDto.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Entities.Contracts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class UserForRegistrationDto
|
||||
{
|
||||
[Required(ErrorMessage = "姓名是必填项。")]
|
||||
[StringLength(50, MinimumLength = 2, ErrorMessage = "姓名长度必须在 2 到 50 个字符之间。")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "电子邮件是必填项。")]
|
||||
[EmailAddress(ErrorMessage = "电子邮件格式不正确。")] // 添加 Email 格式验证
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "密码是必填项。")]
|
||||
[StringLength(100, MinimumLength = 6, ErrorMessage = "密码长度至少为 6 个字符。")] // 确保长度验证
|
||||
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&.])[A-Za-z\d@$!%*?&.]{6,}$",
|
||||
ErrorMessage = "密码必须包含至少一个大写字母、一个小写字母、一个数字和一个特殊字符。特殊字符包含 @$!%*?&. ")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[Compare(nameof(Password), ErrorMessage = "密码和确认密码不匹配。")]
|
||||
[Required(ErrorMessage = "确认密码是必填项。")] // 确保确认密码也必须填写
|
||||
public string ConfirmPassword { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "至少选择一个角色。")]
|
||||
public UserRoles Roles { get; set; } = UserRoles.Student; // 根据你当前的 MudRadioGroup 设定,这里是单选
|
||||
|
||||
[Required(ErrorMessage = "班级是必填项。")]
|
||||
[Range(1, 14, ErrorMessage = "班级编号必须在 1 到 14 之间。")] // 班级范围已调整为 1-14
|
||||
public int Class { get; set; } = 1;
|
||||
|
||||
[Required(ErrorMessage = "年级是必填项。")]
|
||||
[Range(1, 6, ErrorMessage = "年级编号必须在 1 到 6 之间。")] // 年级范围已调整为 1-6
|
||||
public int Grade { get; set; } = 1;
|
||||
|
||||
[Phone(ErrorMessage = "电话号码格式不正确。")]
|
||||
[StringLength(11, MinimumLength = 11, ErrorMessage = "电话号码必须是 11 位数字。")] // 电话号码长度已调整为固定 11 位
|
||||
public string PhoneNumber { get; set; }
|
||||
|
||||
[StringLength(200, ErrorMessage = "家庭住址不能超过 200 个字符。")]
|
||||
public string HomeAddress { get; set; }
|
||||
|
||||
public string ClientURI { get; set; }
|
||||
}
|
||||
}
|
18
Entities/DTO/UserRegistrationToClassDto.cs
Normal file
18
Entities/DTO/UserRegistrationToClassDto.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Entities.Contracts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Entities.DTO
|
||||
{
|
||||
public class UserRegistrationToClassDto
|
||||
{
|
||||
public string User { get; set; }
|
||||
public byte ClassId { get; set; }
|
||||
public byte GradeId { get; set; }
|
||||
public UserRoles Roles { get; set; } = UserRoles.Student;
|
||||
public SubjectAreaEnum SubjectArea { get; set; } = SubjectAreaEnum.Unknown;
|
||||
}
|
||||
}
|
15
Entities/Entities.csproj
Normal file
15
Entities/Entities.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.16" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.16" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.AutoHistory" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
12
Entities/RequestFeatures/MetaData.cs
Normal file
12
Entities/RequestFeatures/MetaData.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Entities.RequestFeatures
|
||||
{
|
||||
public class MetaData
|
||||
{
|
||||
public int CurrentPage { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalCount { get; set; }
|
||||
public bool HasPrevious => CurrentPage > 1;
|
||||
public bool HasNext => CurrentPage < TotalPages;
|
||||
}
|
||||
}
|
22
Entities/RequestFeatures/ProductParameters.cs
Normal file
22
Entities/RequestFeatures/ProductParameters.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Entities.RequestFeatures
|
||||
{
|
||||
public class ProductParameters
|
||||
{
|
||||
const int maxPageSize = 50;
|
||||
public int PageNumber { get; set; } = 1;
|
||||
private int _pageSize = 4;
|
||||
public int PageSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _pageSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
_pageSize = (value > maxPageSize) ? maxPageSize : value;
|
||||
}
|
||||
}
|
||||
public string? SearchTerm { get; set; }
|
||||
public string OrderBy { get; set; } = "name";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user