Files
WCS/Cowain.Base/Services/BaseService.cs
2026-03-02 09:08:20 +08:00

493 lines
20 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Cowain.Base.DBContext;
using Microsoft.EntityFrameworkCore;
using MySqlConnector;
using System.Data;
using System.Linq.Expressions;
using Dapper;
using Cowain.Base.IServices;
namespace Cowain.Base.Services;
public class BaseService : IBaseService
{
// 替换为 IDbContextFactory<SqlDbContext>
protected readonly IDbContextFactory<SqlDbContext> _dbContextFactory;
public BaseService(IDbContextFactory<SqlDbContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
/// <summary>
/// 根据ID删除实体
/// </summary>
public int Delete<T>(int Id) where T : class
{
// 每次操作创建独立上下文using 自动释放
using var dbContext = _dbContextFactory.CreateDbContext();
T? t = dbContext.Set<T>().Find(Id);
if (t == null) throw new KeyNotFoundException($"未找到类型 {typeof(T).Name}Id={Id}");
dbContext.Set<T>().Remove(t);
return dbContext.SaveChanges();
}
/// <summary>
/// 根据ID异步删除实体支持取消
/// </summary>
public async Task<int> DeleteAsync<T>(int Id, CancellationToken cancellationToken = default) where T : class
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
var t = await dbContext.Set<T>().FindAsync(Id, cancellationToken).ConfigureAwait(false);
if (t == null) throw new KeyNotFoundException($"未找到类型 {typeof(T).Name}Id={Id}");
dbContext.Set<T>().Remove(t);
return await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 删除实体
/// </summary>
public int Delete<T>(T t) where T : class
{
ArgumentNullException.ThrowIfNull(t);
using var dbContext = _dbContextFactory.CreateDbContext();
var entry = dbContext.Entry(t);
if (entry.State == EntityState.Detached)
{
dbContext.Set<T>().Attach(t);
}
dbContext.Set<T>().Remove(t);
return dbContext.SaveChanges();
}
/// <summary>
/// 删除实体(异步,支持取消)
/// </summary>
public async Task<int> DeleteAsync<T>(T t, CancellationToken cancellationToken = default) where T : class
{
ArgumentNullException.ThrowIfNull(t);
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
var entry = dbContext.Entry(t);
if (entry.State == EntityState.Detached)
{
dbContext.Set<T>().Attach(t);
}
dbContext.Set<T>().Remove(t);
return await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 批量读取实体
/// </summary>
public List<T> Find<T>() where T : class
{
using var dbContext = _dbContextFactory.CreateDbContext();
return dbContext.Set<T>().ToList();
}
/// <summary>
/// 异步、安全的批量读取(支持取消)
/// </summary>
public async Task<List<T>> FindAsync<T>(CancellationToken cancellationToken = default) where T : class
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
return await dbContext.Set<T>().ToListAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 根据ID查询实体
/// </summary>
public T? Find<T>(int id) where T : class
{
using var dbContext = _dbContextFactory.CreateDbContext();
return dbContext.Set<T>().Find(id);
}
/// <summary>
/// 根据ID异步查询实体支持取消
/// </summary>
public async Task<T?> FindAsync<T>(int id, CancellationToken cancellationToken = default) where T : class
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
return await dbContext.Set<T>().FindAsync(new object[] { id }, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 带表达式的异步查询(支持取消)
/// </summary>
public async Task<T?> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> funcWhere, CancellationToken cancellationToken = default) where T : class
{
using var dbContext = _dbContextFactory.CreateDbContext();
return await dbContext.Set<T>().Where(funcWhere).FirstOrDefaultAsync(cancellationToken);
}
/// <summary>
/// 插入实体
/// </summary>
public int Insert<T>(T t) where T : class
{
ArgumentNullException.ThrowIfNull(t);
using var dbContext = _dbContextFactory.CreateDbContext();
dbContext.Set<T>().Add(t);
return dbContext.SaveChanges();
}
/// <summary>
/// 异步插入实体
/// </summary>
public async Task<int> InsertAsync<T>(T t, CancellationToken cancellationToken = default) where T : class
{
ArgumentNullException.ThrowIfNull(t);
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
dbContext.Set<T>().Add(t);
return await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 批量插入实体
/// </summary>
public int Insert<T>(IEnumerable<T> tList) where T : class
{
using var dbContext = _dbContextFactory.CreateDbContext();
dbContext.Set<T>().AddRange(tList);
return dbContext.SaveChanges();
}
/// <summary>
/// 批量异步插入实体
/// </summary>
public async Task<int> InsertAsync<T>(IEnumerable<T> tList, CancellationToken cancellationToken = default) where T : class
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
dbContext.Set<T>().AddRange(tList);
return await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 带表达式的查询
/// </summary>
public List<T> Query<T>(Expression<Func<T, bool>>? funcWhere = null) where T : class
{
// 注意Query 返回 IQueryable 时上下文生命周期需由调用方管理或改为立即执行ToList/First 等)
var dbContext = _dbContextFactory.CreateDbContext();
IQueryable<T> query = dbContext.Set<T>();
if (funcWhere is not null)
{
query = query.Where(funcWhere);
}
return query.ToList();
}
/// <summary>
/// 带表达式的异步查询(支持取消、空条件查全部)
/// </summary>
public async Task<List<T>> QueryAsync<T>(
Expression<Func<T, bool>>? funcWhere = null, // 设置默认值,调用更便捷
CancellationToken cancellationToken = default)
where T : class
{
using var dbContext = _dbContextFactory.CreateDbContext();
IQueryable<T> query = dbContext.Set<T>();
if (funcWhere is not null)
{
query = query.Where(funcWhere);
}
return await query.ToListAsync(cancellationToken);
}
/// <summary>
/// 基础版:返回实体列表+总条数(无投影)
/// </summary>
public async Task<(List<T> Data, int TotalCount)> QueryAsync<T, TKey>(
Expression<Func<T, bool>>? funcWhere = null,
Expression<Func<T, TKey>>? orderBy = null,
bool isAscending = true,
int pageIndex = 1,
int pageSize = 20,
CancellationToken cancellationToken = default)
where T : class
{
pageIndex = pageIndex < 1 ? 1 : pageIndex;
pageSize = pageSize < 0 ? 0 : pageSize;
using var dbContext = _dbContextFactory.CreateDbContext();
IQueryable<T> query = dbContext.Set<T>();
if (funcWhere is not null) query = query.Where(funcWhere);
if (orderBy is not null) query = isAscending ? query.OrderBy(orderBy) : query.OrderByDescending(orderBy);
if (pageSize > 0)
{
long totalCountLong = await query.LongCountAsync(cancellationToken);
int totalCount = (int)Math.Min(totalCountLong, int.MaxValue);
var data = await query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync(cancellationToken);
return (data, totalCount);
}
else
{
var data = await query.ToListAsync(cancellationToken);
return (data, data.Count);
}
}
/// <summary>
/// 增强版支持投影Select映射直接返回ViewModel列表+总条数
/// </summary>
/// <typeparam name="T">原实体类型</typeparam>
/// <typeparam name="TKey">排序字段类型</typeparam>
/// <typeparam name="TViewModel">目标ViewModel类型</typeparam>
/// <param name="selectExpression">投影表达式EF会解析为SQL的SELECT字段</param>
public async Task<(List<TViewModel> Data, int TotalCount)> QueryAsync<T, TKey, TViewModel>(
Expression<Func<T, TViewModel>> selectExpression, // 新增:投影映射表达式
Expression<Func<T, bool>>? funcWhere = null,
Expression<Func<T, TKey>>? orderBy = null,
bool isAscending = true,
int pageIndex = 1,
int pageSize = 20,
CancellationToken cancellationToken = default)
where T : class
{
pageIndex = pageIndex < 1 ? 1 : pageIndex;
pageSize = pageSize < 0 ? 0 : pageSize;
using var dbContext = _dbContextFactory.CreateDbContext();
IQueryable<T> query = dbContext.Set<T>();
// 1. 过滤
if (funcWhere is not null) query = query.Where(funcWhere);
// 2. 排序
if (orderBy is not null) query = isAscending ? query.OrderBy(orderBy) : query.OrderByDescending(orderBy);
// 3. 先查总条数(注意:总条数要在投影前查,避免投影影响计数)
long totalCountLong = await query.LongCountAsync(cancellationToken);
int totalCount = (int)Math.Min(totalCountLong, int.MaxValue);
// 4. 分页+投影EF会转换为SQLSELECT Id, Name, LayOutX, LayOutY FROM ... LIMIT ...
IQueryable<TViewModel> viewModelQuery = query
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.Select(selectExpression); // 内置投影替代手动Select
// 5. 转为List
var data = await viewModelQuery.ToListAsync(cancellationToken);
return (data, totalCount);
}
/// <summary>
/// 更新实体
/// </summary>
public int Update<T>(T t) where T : class
{
ArgumentNullException.ThrowIfNull(t);
using var dbContext = _dbContextFactory.CreateDbContext();
var entry = dbContext.Entry(t);
if (entry.State == EntityState.Detached)
{
dbContext.Set<T>().Attach(t);
}
dbContext.Entry(t).State = EntityState.Modified;
return dbContext.SaveChanges();
}
/// <summary>
/// 异步更新实体(支持取消)
/// </summary>
public async Task<int> UpdateAsync<T>(T t, CancellationToken cancellationToken = default) where T : class
{
ArgumentNullException.ThrowIfNull(t);
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
var entry = dbContext.Entry(t);
if (entry.State == EntityState.Detached)
{
dbContext.Set<T>().Attach(t);
}
dbContext.Entry(t).State = EntityState.Modified;
return await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 批量更新实体
/// </summary>
public int Update<T>(IEnumerable<T> tList) where T : class
{
ArgumentNullException.ThrowIfNull(tList);
var list = tList as IList<T> ?? tList.ToList();
if (list.Count == 0) return 0;
using var dbContext = _dbContextFactory.CreateDbContext();
dbContext.Set<T>().UpdateRange(list);
return dbContext.SaveChanges();
}
/// <summary>
/// 异步批量更新实体(支持取消)
/// </summary>
public async Task<int> UpdateAsync<T>(IEnumerable<T> tList, CancellationToken cancellationToken = default) where T : class
{
ArgumentNullException.ThrowIfNull(tList);
var list = tList as IList<T> ?? tList.ToList();
if (list.Count == 0) return 0;
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
dbContext.Set<T>().UpdateRange(list);
return await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 使用sql语句获取DataTable
/// </summary>
public DataTable GetDataTable(string sql, IEnumerable<MySqlParameter>? parameters = null)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
// 从工厂创建上下文获取连接字符串
using var dbContext = _dbContextFactory.CreateDbContext();
using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
using var cmd = new MySqlCommand(sql, conn)
{
CommandType = CommandType.Text
};
if (parameters != null)
{
foreach (var p in parameters) cmd.Parameters.Add(p);
}
using var adapter = new MySqlDataAdapter(cmd);
var table = new DataTable();
adapter.Fill(table);
return table;
}
/// <summary>
/// 异步使用sql语句获取DataTable支持取消
/// </summary>
public async Task<DataTable> GetDataTableAsync(string sql, IEnumerable<MySqlParameter>? parameters = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
await conn.OpenAsync(cancellationToken).ConfigureAwait(false);
using var cmd = new MySqlCommand(sql, conn) { CommandType = CommandType.Text };
if (parameters != null) cmd.Parameters.AddRange(parameters.ToArray());
using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
var table = new DataTable();
table.Load(reader);
return table;
}
// ================= Dapper 辅助方法 =================
/// <summary>
/// 使用 Dapper 查询并映射到 POCO同步
/// </summary>
public IEnumerable<T> QueryByDapper<T>(string sql, object? param = null)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
using var dbContext = _dbContextFactory.CreateDbContext();
using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
conn.Open();
return conn.Query<T>(sql, param);
}
/// <summary>
/// 使用 Dapper 查询并映射到 POCO异步支持 CancellationToken
/// </summary>
public async Task<IEnumerable<T>> QueryByDapperAsync<T>(string sql, object? param = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
await conn.OpenAsync(cancellationToken).ConfigureAwait(false);
var command = new CommandDefinition(sql, param, cancellationToken: cancellationToken);
var result = await conn.QueryAsync<T>(command).ConfigureAwait(false);
return result;
}
/// <summary>
/// 使用 Dapper 查询单条记录(同步)
/// </summary>
public T? QueryFirstOrDefaultByDapper<T>(string sql, object? param = null)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
using var dbContext = _dbContextFactory.CreateDbContext();
using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
conn.Open();
return conn.QueryFirstOrDefault<T>(sql, param);
}
/// <summary>
/// 使用 Dapper 查询单条记录(异步)
/// </summary>
public async Task<T?> QueryFirstOrDefaultByDapperAsync<T>(string sql, object? param = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
await conn.OpenAsync(cancellationToken).ConfigureAwait(false);
var command = new CommandDefinition(sql, param, cancellationToken: cancellationToken);
return await conn.QueryFirstOrDefaultAsync<T>(command).ConfigureAwait(false);
}
/// <summary>
/// 使用 Dapper 执行非查询 SQL同步返回受影响行数
/// </summary>
public int ExecuteByDapper(string sql, object? param = null)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
using var dbContext = _dbContextFactory.CreateDbContext();
using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
conn.Open();
return conn.Execute(sql, param);
}
/// <summary>
/// 使用 Dapper 执行非查询 SQL异步返回受影响行数
/// </summary>
public async Task<int> ExecuteByDapperAsync(string sql, object? param = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
await conn.OpenAsync(cancellationToken).ConfigureAwait(false);
var command = new CommandDefinition(sql, param, cancellationToken: cancellationToken);
return await conn.ExecuteAsync(command).ConfigureAwait(false);
}
/// <summary>
/// 使用 Dapper 执行 SQL 并返回 DataTable异步支持 CancellationToken
/// </summary>
public async Task<DataTable> QueryToDataTableByDapperAsync(string sql, object? param = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
await conn.OpenAsync(cancellationToken).ConfigureAwait(false);
var command = new CommandDefinition(sql, param, cancellationToken: cancellationToken, flags: CommandFlags.Buffered);
await using var reader = await conn.ExecuteReaderAsync(command).ConfigureAwait(false);
var table = new DataTable();
table.Load(reader);
return table;
}
/// <summary>
/// 使用 Dapper 执行 SQL 并返回 DataTable同步
/// </summary>
public DataTable QueryToDataTableByDapper(string sql, object? param = null)
{
if (string.IsNullOrWhiteSpace(sql)) throw new ArgumentException("sql 不能为空", nameof(sql));
using var dbContext = _dbContextFactory.CreateDbContext();
using var conn = new MySqlConnection(dbContext.Database.GetConnectionString());
conn.Open();
using var reader = conn.ExecuteReader(sql, param);
var table = new DataTable();
table.Load(reader);
return table;
}
}