1187 lines
33 KiB
C#
1187 lines
33 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
using xCommons.Extensions;
|
|
using xDataService.Configuration;
|
|
using xDataService.Db;
|
|
using xDataService.Extensions;
|
|
using xDataService.Interfaces;
|
|
using xDataService.Models;
|
|
using xExceptions.Constants;
|
|
using xModels.Base;
|
|
using xModels.Dtos;
|
|
|
|
namespace xDataService.EFRepositories
|
|
{
|
|
/// <summary>
|
|
/// Base EFCore Base Entity Repository Pattern implementation ...
|
|
/// Only Used on EFCore ...
|
|
/// </summary>
|
|
/// <typeparam name="T">is the Entity type</typeparam>
|
|
/// <typeparam name="TKey">is the Entity Key Type</typeparam>
|
|
/// <typeparam name="TDbContext">is the DbContext Type</typeparam>
|
|
public abstract class XBaseEFRepository<T, TKey, TDbContext> : IXBaseRepository<T, TKey>
|
|
where T : XBaseEntity<TKey>
|
|
where TDbContext : XDbContext
|
|
{
|
|
//
|
|
public readonly DbSet<T> dbSet;
|
|
private readonly IXKeyGenerator<T, TKey> keyGenerator;
|
|
public readonly IXUnitOfWorks<TDbContext> unitOfWorks;
|
|
public readonly XDataServiceConfiguration configuration;
|
|
private readonly IXBaseRepositoryEvents<T> baseRepositoryEvents;
|
|
|
|
//
|
|
public abstract IQueryable<T> GetFullDbSet();
|
|
|
|
//
|
|
#region Constructor ...
|
|
public XBaseEFRepository(
|
|
IXUnitOfWorks<TDbContext> unitOfWorks,
|
|
XDataServiceConfiguration configuration,
|
|
IXKeyGenerator<T, TKey> keyGenerator = null,
|
|
IXBaseRepositoryEvents<T> baseRepositoryEvents = null
|
|
)
|
|
{
|
|
this.keyGenerator = keyGenerator;
|
|
//
|
|
this.unitOfWorks = unitOfWorks;
|
|
this.configuration = configuration;
|
|
this.dbSet = unitOfWorks.GetDbSet<T, TKey>();
|
|
this.baseRepositoryEvents = baseRepositoryEvents;
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Add ...
|
|
public async Task<T> AddAsync(
|
|
T item,
|
|
bool saveChanges = true
|
|
)
|
|
{
|
|
//
|
|
// Handle Key ...
|
|
item = await HandleKeyAsync(item);
|
|
|
|
//
|
|
var entry = await dbSet.AddAsync(item);
|
|
|
|
//
|
|
// Check action is Succeeded or not ...
|
|
var isSucceed = false;
|
|
if (saveChanges)
|
|
{
|
|
//
|
|
// Get Modified Count ...
|
|
var qResult = await SaveChangesAsync();
|
|
|
|
//
|
|
// set isSucceed Value based on Changes ...
|
|
isSucceed = qResult > 0;
|
|
}
|
|
|
|
//
|
|
// Notify Event ...
|
|
if (
|
|
isSucceed &&
|
|
!baseRepositoryEvents.IsNull()
|
|
)
|
|
{
|
|
baseRepositoryEvents
|
|
.AddEvent(new XBaseEventModel<T>(entry.Entity));
|
|
}
|
|
|
|
//
|
|
// Return result base on action Succeed ...
|
|
return isSucceed ?
|
|
entry.Entity :
|
|
null;
|
|
}
|
|
|
|
public async Task<T> AddOrUpdateAsync(
|
|
T item,
|
|
bool saveChanges = true
|
|
)
|
|
{
|
|
//
|
|
var isExists = await IsExistsAsync(GetKey(item));
|
|
if (!isExists)
|
|
{
|
|
return await AddAsync(
|
|
item,
|
|
saveChanges
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return await UpdateAsync(
|
|
GetKey(item),
|
|
item,
|
|
saveChanges
|
|
);
|
|
}
|
|
}
|
|
|
|
public async Task AddRangeAsync(
|
|
IEnumerable<T> items,
|
|
bool saveChanges = true
|
|
)
|
|
{
|
|
//
|
|
await dbSet.AddRangeAsync(
|
|
await Task
|
|
.WhenAll(
|
|
items
|
|
.Select(async i => await HandleKeyAsync(i))
|
|
)
|
|
);
|
|
|
|
//
|
|
// Check action is Succeeded or not ...
|
|
var isSucceed = false;
|
|
if (saveChanges)
|
|
{
|
|
//
|
|
// Get Modified Count ...
|
|
var qResult = await SaveChangesAsync();
|
|
|
|
//
|
|
// set isSucceed Value based on Changes ...
|
|
isSucceed = qResult > 0;
|
|
}
|
|
|
|
//
|
|
// Notify Event ...
|
|
if (
|
|
isSucceed &&
|
|
!baseRepositoryEvents.IsNull()
|
|
)
|
|
{
|
|
baseRepositoryEvents
|
|
.AddManyEvent(new XBaseEventModel<IEnumerable<T>>(null));
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Remove ...
|
|
public async Task<T> RemoveAsync(
|
|
TKey id,
|
|
bool softDelete = true,
|
|
bool saveChanges = true
|
|
)
|
|
{
|
|
//
|
|
// Retrieve Item ...
|
|
var item = await GetAsync(
|
|
id,
|
|
ignoreSoftDeleteds: false
|
|
);
|
|
if (item.IsNull())
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//
|
|
// Handle Remove ...
|
|
EntityEntry<T> entry = null;
|
|
if (softDelete && configuration.EnableSoftDelete)
|
|
{
|
|
//
|
|
item.Deleted = true;
|
|
entry = dbSet.Update(item);
|
|
}
|
|
else
|
|
{
|
|
entry = dbSet.Remove(item);
|
|
}
|
|
|
|
//
|
|
// Check action is Succeeded or not ...
|
|
var isSucceed = false;
|
|
if (saveChanges)
|
|
{
|
|
//
|
|
// Get Modified Count ...
|
|
var qResult = await SaveChangesAsync();
|
|
|
|
//
|
|
// set isSucceed Value based on Changes ...
|
|
isSucceed = qResult > 0;
|
|
}
|
|
|
|
//
|
|
// Notify Event ...
|
|
if (
|
|
isSucceed &&
|
|
!baseRepositoryEvents.IsNull()
|
|
)
|
|
{
|
|
baseRepositoryEvents
|
|
.RemoveEvent(new XBaseEventModel<T>(entry.Entity));
|
|
}
|
|
|
|
//
|
|
// Return result base on action Succeed ...
|
|
return isSucceed ?
|
|
entry.Entity :
|
|
null;
|
|
}
|
|
|
|
public async Task<T> RemoveAsync(
|
|
T item,
|
|
bool softDelete = true,
|
|
bool saveChanges = true
|
|
)
|
|
{
|
|
//
|
|
// Check item Exists ...
|
|
var isExists = await IsExistsAsync(
|
|
GetKey(item),
|
|
ignoreSoftDeleteds: false
|
|
);
|
|
if (!isExists)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//
|
|
// Handle Remove ...
|
|
EntityEntry<T> entry = null;
|
|
if (softDelete && configuration.EnableSoftDelete)
|
|
{
|
|
//
|
|
item.Deleted = true;
|
|
entry = dbSet.Update(item);
|
|
}
|
|
else
|
|
{
|
|
entry = dbSet.Remove(item);
|
|
}
|
|
|
|
//
|
|
// Check action is Succeeded or not ...
|
|
var isSucceed = false;
|
|
if (saveChanges)
|
|
{
|
|
//
|
|
// Get Modified Count ...
|
|
var qResult = await SaveChangesAsync();
|
|
|
|
//
|
|
// set isSucceed Value based on Changes ...
|
|
isSucceed = qResult > 0;
|
|
}
|
|
|
|
//
|
|
// Notify Event ...
|
|
if (
|
|
isSucceed &&
|
|
!baseRepositoryEvents.IsNull()
|
|
)
|
|
{
|
|
baseRepositoryEvents
|
|
.RemoveEvent(new XBaseEventModel<T>(entry.Entity));
|
|
}
|
|
|
|
//
|
|
// Return result base on action Succeed ...
|
|
return isSucceed ?
|
|
entry.Entity :
|
|
null;
|
|
}
|
|
|
|
public async Task RemoveRangeAsync(
|
|
IEnumerable<T> items,
|
|
bool softDelete = true,
|
|
bool saveChanges = true
|
|
)
|
|
{
|
|
//
|
|
// Handle Remove ...
|
|
if (softDelete && configuration.EnableSoftDelete)
|
|
{
|
|
//
|
|
foreach (var item in items)
|
|
{
|
|
item.Deleted = true;
|
|
}
|
|
|
|
//
|
|
dbSet.UpdateRange(items);
|
|
}
|
|
else
|
|
{
|
|
dbSet.RemoveRange(items);
|
|
}
|
|
|
|
//
|
|
// Check action is Succeeded or not ...
|
|
var isSucceed = false;
|
|
if (saveChanges)
|
|
{
|
|
//
|
|
// Get Modified Count ...
|
|
var qResult = await SaveChangesAsync();
|
|
|
|
//
|
|
// set isSucceed Value based on Changes ...
|
|
isSucceed = qResult > 0;
|
|
}
|
|
|
|
//
|
|
// Notify Event ...
|
|
if (
|
|
isSucceed &&
|
|
!baseRepositoryEvents.IsNull()
|
|
)
|
|
{
|
|
baseRepositoryEvents
|
|
.RemoveManyEvent(new XBaseEventModel<IEnumerable<T>>(null));
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Retrieve ...
|
|
public IQueryable<T> AsQueryable()
|
|
{
|
|
return dbSet.AsQueryable();
|
|
}
|
|
|
|
public async Task<T> GetAsync(
|
|
TKey id,
|
|
bool ignoreSoftDeleteds = true,
|
|
bool containsDetail = false
|
|
)
|
|
{
|
|
//
|
|
var result = await FindOneAsync(i =>
|
|
GetKey(i)
|
|
.ToString() ==
|
|
id
|
|
.ToString(),
|
|
ignoreSoftDeleteds: ignoreSoftDeleteds,
|
|
containsDetail: containsDetail
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
public async Task<IEnumerable<T>> GetAllAsync(
|
|
bool ignoreSoftDeleteds = true,
|
|
bool containsDetail = false
|
|
)
|
|
{
|
|
return await Task.Run(() =>
|
|
{
|
|
//
|
|
var result = GetDbSet(
|
|
containsDetail: containsDetail
|
|
);
|
|
|
|
//
|
|
// Handle Soft Deleted Items ...
|
|
if (ignoreSoftDeleteds && configuration.EnableSoftDelete)
|
|
{
|
|
result = result.Where(x => x.Deleted == false);
|
|
}
|
|
|
|
//
|
|
return result.AsEnumerable();
|
|
});
|
|
}
|
|
|
|
public async Task<T> FindOneAsync(
|
|
Expression<Func<T, bool>> whereClause,
|
|
bool ignoreSoftDeleteds = true,
|
|
bool containsDetail = false
|
|
)
|
|
{
|
|
//
|
|
// Generate Where Function ...
|
|
var whereFunc = whereClause.Compile();
|
|
|
|
//
|
|
// Handle Soft Deleted Items ...
|
|
Func<T, bool> ignoreSoftDeletedsWhereFunc = null;
|
|
if (ignoreSoftDeleteds && configuration.EnableSoftDelete)
|
|
{
|
|
//
|
|
Expression<Func<T, bool>> ignoreSoftDeletedsWhereClause = x => x.Deleted == false;
|
|
ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile();
|
|
}
|
|
|
|
//
|
|
// Get Enumerable ...
|
|
var enumerator = GetDbSet(
|
|
containsDetail: containsDetail
|
|
)
|
|
.AsAsyncEnumerable();
|
|
|
|
//
|
|
T result = null;
|
|
await
|
|
foreach (var entity in enumerator)
|
|
{
|
|
//
|
|
var isApproved = whereFunc(entity) &&
|
|
(ignoreSoftDeletedsWhereFunc.IsNull() ?
|
|
true :
|
|
ignoreSoftDeletedsWhereFunc(entity));
|
|
if (isApproved)
|
|
{
|
|
//
|
|
result = entity;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
public async Task<IEnumerable<T>> FindManyAsync(
|
|
Expression<Func<T, bool>> whereClause,
|
|
bool ignoreSoftDeleteds = true,
|
|
bool containsDetail = false
|
|
)
|
|
{
|
|
//
|
|
// Generate Where Function ...
|
|
var whereFunc = whereClause.Compile();
|
|
|
|
//
|
|
// Handle Soft Deleted Items ...
|
|
Func<T, bool> ignoreSoftDeletedsWhereFunc = null;
|
|
if (ignoreSoftDeleteds && configuration.EnableSoftDelete)
|
|
{
|
|
//
|
|
Expression<Func<T, bool>> ignoreSoftDeletedsWhereClause = x => x.Deleted == false;
|
|
ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile();
|
|
}
|
|
|
|
//
|
|
// Get Enumerable ...
|
|
var enumerator = GetDbSet(
|
|
containsDetail: containsDetail
|
|
)
|
|
.AsAsyncEnumerable();
|
|
|
|
//
|
|
var result = new List<T>();
|
|
await
|
|
foreach (var entity in enumerator)
|
|
{
|
|
//
|
|
var isApproved = whereFunc(entity) &&
|
|
(ignoreSoftDeletedsWhereFunc.IsNull() ?
|
|
true :
|
|
ignoreSoftDeletedsWhereFunc(entity));
|
|
if (isApproved)
|
|
{
|
|
result.Add(entity);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result.AsEnumerable();
|
|
}
|
|
|
|
public async Task<XQueryResult<T>> QueryAsync(
|
|
XQuery query,
|
|
bool ignoreSoftDeleteds = true
|
|
)
|
|
{
|
|
//
|
|
// Validate ...
|
|
if (query.IsNull())
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Normalize ...
|
|
query = query.NormalizeQuery(configuration);
|
|
|
|
//
|
|
// Handle Soft Deleted Items ...
|
|
Func<T, bool> ignoreSoftDeletedsWhereFunc = null;
|
|
if (ignoreSoftDeleteds && configuration.EnableSoftDelete)
|
|
{
|
|
//
|
|
Expression<Func<T, bool>> ignoreSoftDeletedsWhereClause = x => x.Deleted == false;
|
|
ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile();
|
|
}
|
|
|
|
//
|
|
// Get Enumerable ...
|
|
var enumerator = GetDbSet(
|
|
containsDetail: query.ContainsDetail
|
|
)
|
|
.AsAsyncEnumerable();
|
|
|
|
//
|
|
var totalEntities = new List<T>();
|
|
await
|
|
foreach (var entity in enumerator)
|
|
{
|
|
//
|
|
var isApproved = (ignoreSoftDeletedsWhereFunc.IsNull() ?
|
|
true :
|
|
ignoreSoftDeletedsWhereFunc(entity));
|
|
if (isApproved)
|
|
{
|
|
totalEntities.Add(entity);
|
|
}
|
|
}
|
|
|
|
//
|
|
var items = totalEntities.AsEnumerable();
|
|
var totalItemsCount = items.Count();
|
|
|
|
//
|
|
// Apply Filter ...
|
|
if (!query.Filter.IsNullOrEmpty())
|
|
{
|
|
//
|
|
items = items
|
|
.ApplyFilter(query.Filter);
|
|
}
|
|
int filteredItemsCount = items.Count();
|
|
|
|
//
|
|
// Count Pages ...
|
|
var totalPagesCount = query.CountPages(totalItemsCount);
|
|
var filteredPagesCount = query.CountPages(filteredItemsCount);
|
|
|
|
//
|
|
// Apply Paging and Sorting ...
|
|
if (totalItemsCount > 0 &&
|
|
filteredItemsCount > 0)
|
|
{
|
|
//
|
|
// Apply Sorting ...
|
|
items = items
|
|
.ToList()
|
|
.ApplySorting(
|
|
query.SortBy,
|
|
query.IsAscending
|
|
);
|
|
|
|
//
|
|
// Apply Paging ...
|
|
items = items
|
|
.ToList()
|
|
.ApplyPaging(
|
|
query.Page,
|
|
query.PageSize
|
|
);
|
|
}
|
|
|
|
//
|
|
// Prepare Result ...
|
|
var result = new XQueryResult<T>
|
|
{
|
|
Page = query.Page,
|
|
Items = items.ToList(),
|
|
PageSize = query.PageSize,
|
|
TotalPages = totalPagesCount,
|
|
TotalItems = totalItemsCount,
|
|
TotalFilteredPages = filteredPagesCount,
|
|
TotalFilteredItems = filteredItemsCount
|
|
};
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
public async Task<XQueryResult<T>> ConditionalQueryAsync(
|
|
Expression<Func<T, bool>> condition,
|
|
XQuery query,
|
|
bool ignoreSoftDeleteds = true
|
|
)
|
|
{
|
|
//
|
|
// Validate ...
|
|
if (query.IsNull() || condition.IsNull())
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Normalize ...
|
|
query = query.NormalizeQuery(configuration);
|
|
|
|
//
|
|
// Handle Soft Deleted Items ...
|
|
Func<T, bool> ignoreSoftDeletedsWhereFunc = null;
|
|
if (ignoreSoftDeleteds && configuration.EnableSoftDelete)
|
|
{
|
|
//
|
|
Expression<Func<T, bool>> ignoreSoftDeletedsWhereClause = x => x.Deleted == false;
|
|
ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile();
|
|
}
|
|
|
|
//
|
|
// Compile Condition ...
|
|
Func<T, bool> conditionFunc = condition.Compile();
|
|
|
|
//
|
|
// Get Enumerable ...
|
|
var enumerator = GetDbSet(
|
|
containsDetail: query.ContainsDetail
|
|
)
|
|
.AsAsyncEnumerable();
|
|
|
|
//
|
|
var totalEntities = new List<T>();
|
|
await
|
|
foreach (var entity in enumerator)
|
|
{
|
|
//
|
|
var isApproved = (ignoreSoftDeletedsWhereFunc.IsNull() ?
|
|
true :
|
|
ignoreSoftDeletedsWhereFunc(entity)) &&
|
|
conditionFunc(entity);
|
|
if (isApproved)
|
|
{
|
|
totalEntities.Add(entity);
|
|
}
|
|
}
|
|
|
|
//
|
|
var items = totalEntities.AsEnumerable();
|
|
var totalItemsCount = items.Count();
|
|
|
|
//
|
|
// Apply Filter ...
|
|
if (!query.Filter.IsNullOrEmpty())
|
|
{
|
|
//
|
|
items = items
|
|
.ApplyFilter(query.Filter);
|
|
}
|
|
int filteredItemsCount = items.Count();
|
|
|
|
//
|
|
// Count Pages ...
|
|
var totalPagesCount = query.CountPages(totalItemsCount);
|
|
var filteredPagesCount = query.CountPages(filteredItemsCount);
|
|
|
|
//
|
|
// Apply Paging and Sorting ...
|
|
if (totalItemsCount > 0 &&
|
|
filteredItemsCount > 0)
|
|
{
|
|
//
|
|
// Apply Sorting ...
|
|
items = items
|
|
.ToList()
|
|
.ApplySorting(
|
|
query.SortBy,
|
|
query.IsAscending
|
|
);
|
|
|
|
//
|
|
// Apply Paging ...
|
|
items = items
|
|
.ToList()
|
|
.ApplyPaging(
|
|
query.Page,
|
|
query.PageSize
|
|
);
|
|
}
|
|
|
|
//
|
|
// Prepare Result ...
|
|
var result = new XQueryResult<T>
|
|
{
|
|
Page = query.Page,
|
|
Items = items.ToList(),
|
|
PageSize = query.PageSize,
|
|
TotalPages = totalPagesCount,
|
|
TotalItems = totalItemsCount,
|
|
TotalFilteredPages = filteredPagesCount,
|
|
TotalFilteredItems = filteredItemsCount
|
|
};
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TODO: Fix this ...
|
|
// public Task<XPageResponse<T>> RequestPageAsync (
|
|
// XPageRequest request,
|
|
// bool ignoreSoftDeleteds = true,
|
|
// bool containsDetail = true
|
|
// ) {
|
|
// //
|
|
// var result = GetDbSet (
|
|
// containsDetail: containsDetail
|
|
// );
|
|
|
|
// //
|
|
// // Handle Soft Deleted Items ...
|
|
// if (ignoreSoftDeleteds) {
|
|
// result = result.Where (x => x.Deleted == false);
|
|
// }
|
|
|
|
// //
|
|
// if (request.First.HasValue) {
|
|
// //
|
|
// if (!request.After.IsNullOrEmpty ()) {
|
|
// //
|
|
// TKey lastId = XCursorHelper.FromCursor<TKey> (request.After);
|
|
// result = result.Where (x => GetKey (x).ToString() > lastId.ToString());
|
|
// }
|
|
|
|
// //
|
|
// result = result.Take (request.First.Value);
|
|
// }
|
|
|
|
// //
|
|
// // Apply Sorting ...
|
|
// List<T> nodes = null;
|
|
// if (!request.SortBy.IsNullOrEmpty ()) {
|
|
// nodes = result
|
|
// .ApplySorting<T, TKey> (
|
|
// request.SortBy, !request.DescendingSort
|
|
// ).ToList ();
|
|
// }
|
|
|
|
// //
|
|
// // Claculate Required Info ...
|
|
// int totalCount = result.CountAsync ().Result;
|
|
// int maxId = nodes.Max (x => Convert.ToInt32 (GetKey (x)));
|
|
// int minId = nodes.Min (x => Convert.ToInt32 (GetKey (x)));
|
|
// bool hasNextPage = nodes.Any (x => Convert.ToInt32 (GetKey (x)) > maxId);
|
|
// bool hasPreviousPage = nodes.Any (x => Convert.ToInt32 (GetKey (x)) < minId);
|
|
|
|
// //
|
|
// return Task.FromResult (new XPageResponse<T> {
|
|
// Nodes = nodes,
|
|
// TotalCount = totalCount,
|
|
// HasNextPage = hasNextPage,
|
|
// HasPreviousPage = hasPreviousPage
|
|
// });
|
|
// }
|
|
|
|
//
|
|
// TODO: Fix this ...
|
|
// public Task<XPageResponse<T>> RequestConditionalPageAsync (
|
|
// Expression<Func<T, bool>> condition,
|
|
// XPageRequest request,
|
|
// bool ignoreSoftDeleteds = true,
|
|
// bool containsDetail = true
|
|
// ) {
|
|
// //
|
|
// var result = GetDbSet (
|
|
// containsDetail: containsDetail
|
|
// )
|
|
// .Where (condition);
|
|
|
|
// //
|
|
// // Handle Soft Deleted Items ...
|
|
// if (ignoreSoftDeleteds) {
|
|
// result = result.Where (x => x.Deleted == false);
|
|
// }
|
|
|
|
// //
|
|
// if (request.First.HasValue) {
|
|
// //
|
|
// if (!request.After.IsNullOrEmpty ()) {
|
|
// //
|
|
// int lastId = XCursorHelper.FromCursor (request.After);
|
|
// result = result.Where (x => Convert.ToInt32 (GetKey (x)) > lastId);
|
|
// }
|
|
|
|
// //
|
|
// result = result.Take (request.First.Value);
|
|
// }
|
|
|
|
// //
|
|
// // Apply Sorting ...
|
|
// List<T> nodes = null;
|
|
// if (!request.SortBy.IsNullOrEmpty ()) {
|
|
// nodes = result
|
|
// .ApplySorting<T, TKey> (
|
|
// request.SortBy, !request.DescendingSort)
|
|
// .ToList ();
|
|
// }
|
|
|
|
// //
|
|
// // Claculate Required Info ...
|
|
// int totalCount = result.CountAsync ().Result;
|
|
// int maxId = nodes.Max (x => Convert.ToInt32 (GetKey (x)));
|
|
// int minId = nodes.Min (x => Convert.ToInt32 (GetKey (x)));
|
|
// bool hasNextPage = result.Any (x => Convert.ToInt32 (GetKey (x)) > maxId);
|
|
// bool hasPreviousPage = result.Any (x => Convert.ToInt32 (GetKey (x)) < minId);
|
|
|
|
// //
|
|
// return Task.FromResult (new XPageResponse<T> {
|
|
// Nodes = nodes,
|
|
// TotalCount = totalCount,
|
|
// HasNextPage = hasNextPage,
|
|
// HasPreviousPage = hasPreviousPage
|
|
// });
|
|
// }
|
|
#endregion
|
|
|
|
//
|
|
#region Update ...
|
|
public async Task<T> UpdateAsync(
|
|
TKey id,
|
|
T item,
|
|
bool saveChanges = true
|
|
)
|
|
{
|
|
//
|
|
// Get Exists Entity ...
|
|
var existsEntity = await GetAsync(
|
|
id,
|
|
containsDetail: true,
|
|
ignoreSoftDeleteds: false
|
|
);
|
|
|
|
//
|
|
// Update Data ...
|
|
existsEntity.UpdateData(item);
|
|
var entry = dbSet.Attach(existsEntity);
|
|
entry.State = EntityState.Modified;
|
|
|
|
//
|
|
// Check action is Succeeded or not ...
|
|
var isSucceed = false;
|
|
if (saveChanges)
|
|
{
|
|
//
|
|
// Get Modified Count ...
|
|
var qResult = await SaveChangesAsync();
|
|
|
|
//
|
|
// set isSucceed Value based on Changes ...
|
|
isSucceed = qResult > 0;
|
|
}
|
|
|
|
//
|
|
// Notify Event ...
|
|
if (
|
|
isSucceed &&
|
|
!baseRepositoryEvents.IsNull()
|
|
)
|
|
{
|
|
baseRepositoryEvents
|
|
.UpdateEvent(new XBaseEventModel<T>(entry.Entity));
|
|
}
|
|
|
|
//
|
|
// Return result base on action Succeed ...
|
|
return isSucceed ?
|
|
entry.Entity :
|
|
null;
|
|
}
|
|
|
|
public async Task<bool> UpdateRangeAsync(
|
|
IEnumerable<T> items,
|
|
bool saveChanges = true
|
|
)
|
|
{
|
|
//
|
|
items
|
|
.ToList()
|
|
.ForEach(i =>
|
|
{
|
|
var entry = dbSet.Attach(i);
|
|
entry.State = EntityState.Modified;
|
|
});
|
|
|
|
//
|
|
// Check action is Succeeded or not ...
|
|
var isSucceed = false;
|
|
if (saveChanges)
|
|
{
|
|
//
|
|
// Get Modified Count ...
|
|
var qResult = await SaveChangesAsync();
|
|
|
|
//
|
|
// set isSucceed Value based on Changes ...
|
|
isSucceed = qResult > 0;
|
|
}
|
|
|
|
//
|
|
// Notify Event ...
|
|
if (
|
|
isSucceed &&
|
|
!baseRepositoryEvents.IsNull()
|
|
)
|
|
{
|
|
baseRepositoryEvents
|
|
.UpdateManyEvent(new XBaseEventModel<IEnumerable<T>>(null));
|
|
}
|
|
|
|
//
|
|
// Return result base on action Succeed ...
|
|
return isSucceed;
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Count ...
|
|
public async Task<int> CountAsync(bool ignoreSoftDeleteds = true)
|
|
{
|
|
//
|
|
var dbSet = GetDbSet();
|
|
|
|
//
|
|
if (ignoreSoftDeleteds && configuration.EnableSoftDelete)
|
|
{
|
|
return await dbSet.CountAsync(i => i.Deleted == false);
|
|
}
|
|
else
|
|
{
|
|
return await dbSet.CountAsync();
|
|
}
|
|
}
|
|
|
|
public async Task<int> PagesCountAsync(
|
|
int pageSize,
|
|
int? totalItems = null
|
|
)
|
|
{
|
|
//
|
|
int count = totalItems.HasValue ? totalItems.Value : await CountAsync();
|
|
int pagesCount = count / pageSize;
|
|
|
|
//
|
|
if (count % pageSize > 0)
|
|
{
|
|
pagesCount++;
|
|
}
|
|
|
|
//
|
|
return pagesCount;
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Exists ...
|
|
public async Task<bool> IsExistsAsync(
|
|
TKey id,
|
|
bool ignoreSoftDeleteds = true
|
|
)
|
|
{
|
|
//
|
|
var result = await FindOneAsync(i =>
|
|
GetKey(i)
|
|
.ToString() == id
|
|
.ToString(),
|
|
ignoreSoftDeleteds: ignoreSoftDeleteds,
|
|
containsDetail: false
|
|
);
|
|
|
|
//
|
|
Detach(result);
|
|
|
|
//
|
|
return !result.IsNull();
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Unit Of Work ...
|
|
public async Task<int> SaveChangesAsync()
|
|
{
|
|
return await unitOfWorks.SaveChangesAsync();
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Keys ...
|
|
public void SetKey(
|
|
ref T item,
|
|
TKey id
|
|
)
|
|
{
|
|
//
|
|
var props = item.GetType().GetProperties();
|
|
var keyProp = props.FirstOrDefault(p => p.Name == "Id");
|
|
if (keyProp.IsNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType;
|
|
object safeValue = (id == null) ? null : Convert.ChangeType(id, t);
|
|
keyProp.SetValue(item, safeValue, null);
|
|
}
|
|
|
|
public TKey GetKey(T item)
|
|
{
|
|
//
|
|
var props = item.GetType().GetProperties();
|
|
var keyProp = props.FirstOrDefault(p => p.Name == "Id");
|
|
|
|
//
|
|
var keyString = string.Empty;
|
|
if (keyProp.IsNull())
|
|
{
|
|
keyString = string.Empty;
|
|
}
|
|
else
|
|
{
|
|
keyString = keyProp.GetValue(item).ToString();
|
|
}
|
|
|
|
//
|
|
if (keyString.IsNullOrEmpty())
|
|
{
|
|
return default(TKey);
|
|
}
|
|
|
|
//
|
|
// Prevent Deserializing issues throug JsonReader ...
|
|
if (keyString.IsGuid() && typeof(TKey) == typeof(Guid))
|
|
{
|
|
return item.Id;
|
|
}
|
|
|
|
//
|
|
return keyString.FromJSON<TKey>();
|
|
}
|
|
|
|
public async Task<T> HandleKeyAsync(T item)
|
|
{
|
|
//
|
|
var keyType = typeof(TKey);
|
|
|
|
//
|
|
// Handle Guid Key Type ...
|
|
// Since EFCore has AutoIncrement on int Ids, there is no need to handle int Key types ...
|
|
if (
|
|
(
|
|
keyType == typeof(Guid) ||
|
|
keyType == typeof(string)
|
|
) &&
|
|
keyGenerator.IsEmpty(item.Id)
|
|
)
|
|
{
|
|
//
|
|
var newKey = await keyGenerator.GenerateKey(this);
|
|
|
|
//
|
|
SetKey(ref item, newKey);
|
|
}
|
|
|
|
//
|
|
return item;
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Detach ...
|
|
public void Detach(T item)
|
|
{
|
|
//
|
|
if (item.IsNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Entry(item).State = EntityState.Detached;
|
|
}
|
|
|
|
public void Detach(IEnumerable<T> items)
|
|
{
|
|
//
|
|
if (items.IsNull() || !items.HasChild())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
items
|
|
.ToList()
|
|
.ForEach(item =>
|
|
{
|
|
Detach(item);
|
|
});
|
|
}
|
|
|
|
public void Detach(XQueryResult<T> query)
|
|
{
|
|
//
|
|
if (query.IsNull() || query.Items.HasChild())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Detach(query.Items);
|
|
}
|
|
|
|
public void Detach(XPageResponse<T> page)
|
|
{
|
|
//
|
|
if (page.IsNull() || !page.Nodes.HasChild())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Detach(page.Nodes);
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Others ...
|
|
public void Dispose()
|
|
{
|
|
unitOfWorks.Dispose();
|
|
}
|
|
|
|
public string GetPropValues(T item)
|
|
{
|
|
//
|
|
var props = item.GetType().GetProperties();
|
|
var vals = props.Select(p => p.GetValue(p.Name));
|
|
|
|
//
|
|
return vals.ToJSON();
|
|
}
|
|
|
|
public EntityEntry<T> Entry(T item)
|
|
{
|
|
return unitOfWorks.DbContext.Entry(item);
|
|
}
|
|
|
|
public IQueryable<T> GetDbSet(
|
|
bool containsDetail = false
|
|
)
|
|
{
|
|
//
|
|
IQueryable<T> result = null;
|
|
if (!containsDetail)
|
|
{
|
|
result = dbSet;
|
|
}
|
|
else
|
|
{
|
|
result = GetFullDbSet();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
#endregion
|
|
}
|
|
} |