Sqlsugar 分表 CRUD demo

This commit is contained in:
Nine 2023-03-30 15:38:17 +08:00
parent d1288b103e
commit 67efee3ee1
13 changed files with 642 additions and 3 deletions

View File

@ -1249,6 +1249,50 @@
</summary>
<returns></returns>
</member>
<member name="T:Blog.Core.Api.Controllers.SplitDemoController">
<summary>
分表demo
</summary>
</member>
<member name="M:Blog.Core.Api.Controllers.SplitDemoController.Get(System.DateTime,System.DateTime,System.Int32,System.String,System.Int32)">
<summary>
分页获取数据
</summary>
<param name="beginTime"></param>
<param name="endTime"></param>
<param name="page"></param>
<param name="key"></param>
<param name="pageSize"></param>
<returns></returns>
</member>
<member name="M:Blog.Core.Api.Controllers.SplitDemoController.GetById(System.Int64)">
<summary>
根据ID获取信息
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:Blog.Core.Api.Controllers.SplitDemoController.Post(Blog.Core.Model.Models.SplitDemo)">
<summary>
添加一条测试数据
</summary>
<param name="splitDemo"></param>
<returns></returns>
</member>
<member name="M:Blog.Core.Api.Controllers.SplitDemoController.Put(Blog.Core.Model.Models.SplitDemo)">
<summary>
修改一条测试数据
</summary>
<param name="splitDemo"></param>
<returns></returns>
</member>
<member name="M:Blog.Core.Api.Controllers.SplitDemoController.Delete(System.Int64)">
<summary>
根据id删除数据
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="T:Blog.Core.Api.Controllers.Tenant.TenantByDbController">
<summary>
多租户-多库方案 测试

View File

@ -0,0 +1,199 @@
using Blog.Core.IServices;
using Blog.Core.Model;
using Blog.Core.Model.Models;
using Blog.Core.Repository.UnitOfWorks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Linq.Expressions;
namespace Blog.Core.Api.Controllers
{
/// <summary>
/// 分表demo
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
[Authorize(Permissions.Name)]
public class SplitDemoController : ControllerBase
{
readonly ISplitDemoServices splitDemoServices;
readonly IUnitOfWorkManage unitOfWorkManage;
public SplitDemoController(ISplitDemoServices _splitDemoServices, IUnitOfWorkManage _unitOfWorkManage)
{
splitDemoServices = _splitDemoServices;
unitOfWorkManage = _unitOfWorkManage;
}
/// <summary>
/// 分页获取数据
/// </summary>
/// <param name="beginTime"></param>
/// <param name="endTime"></param>
/// <param name="page"></param>
/// <param name="key"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<MessageModel<PageModel<SplitDemo>>> Get(DateTime beginTime, DateTime endTime, int page = 1, string key = "", int pageSize = 10)
{
if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
{
key = "";
}
Expression<Func<SplitDemo, bool>> whereExpression = a => (a.Name != null && a.Name.Contains(key));
var data = await splitDemoServices.QueryPageSplit(whereExpression, beginTime, endTime, page, pageSize, " Id desc ");
return MessageModel<PageModel<SplitDemo>>.Message(data.dataCount >= 0, "获取成功", data);
}
/// <summary>
/// 根据ID获取信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<MessageModel<SplitDemo>> GetById(long id)
{
var data = new MessageModel<string>();
var model = await splitDemoServices.QueryByIdSplit(id);
if (model != null)
{
return MessageModel<SplitDemo>.Success("获取成功", model);
}
else
{
return MessageModel<SplitDemo>.Fail("获取失败");
}
}
/// <summary>
/// 添加一条测试数据
/// </summary>
/// <param name="splitDemo"></param>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public async Task<MessageModel<string>> Post([FromBody] SplitDemo splitDemo)
{
var data = new MessageModel<string>();
unitOfWorkManage.BeginTran();
var id = (await splitDemoServices.AddSplit(splitDemo));
data.success = (id == null ? false : true);
try
{
if (data.success)
{
data.response = id.FirstOrDefault().ToString();
data.msg = "添加成功";
}
else
{
data.msg = "添加失败";
}
}
catch (Exception)
{
throw;
}
finally
{
if (data.success)
unitOfWorkManage.CommitTran();
else
unitOfWorkManage.RollbackTran();
}
return data;
}
/// <summary>
/// 修改一条测试数据
/// </summary>
/// <param name="splitDemo"></param>
/// <returns></returns>
[HttpPut]
[AllowAnonymous]
public async Task<MessageModel<string>> Put([FromBody] SplitDemo splitDemo)
{
var data = new MessageModel<string>();
if (splitDemo != null && splitDemo.Id > 0)
{
unitOfWorkManage.BeginTran();
data.success = await splitDemoServices.UpdateSplit(splitDemo, splitDemo.CreateTime);
try
{
if (data.success)
{
data.msg = "修改成功";
data.response = splitDemo?.Id.ObjToString();
}
else
{
data.msg = "修改失败";
}
}
catch (Exception)
{
throw;
}
finally
{
if (data.success)
unitOfWorkManage.CommitTran();
else
unitOfWorkManage.RollbackTran();
}
}
return data;
}
/// <summary>
/// 根据id删除数据
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[AllowAnonymous]
public async Task<MessageModel<string>> Delete(long id)
{
var data = new MessageModel<string>();
var model = await splitDemoServices.QueryByIdSplit(id);
if (model != null)
{
unitOfWorkManage.BeginTran();
data.success = await splitDemoServices.DeleteSplit(model,model.CreateTime);
try
{
data.response = id.ObjToString();
if (data.success)
{
data.msg = "删除成功";
}
else
{
data.msg = "删除失败";
}
}
catch (Exception)
{
throw;
}
finally
{
if (data.success)
unitOfWorkManage.CommitTran();
else
unitOfWorkManage.RollbackTran();
}
}
else
{
data.msg = "不存在";
}
return data;
}
}
}

View File

@ -7,6 +7,7 @@ using Autofac.Extensions.DependencyInjection;
using Blog.Core;
using Blog.Core.Common;
using Blog.Core.Common.Core;
using Blog.Core.Common.Helper;
using Blog.Core.Common.LogHelper;
using Blog.Core.Extensions;
using Blog.Core.Extensions.Apollo;
@ -14,6 +15,7 @@ using Blog.Core.Extensions.Middlewares;
using Blog.Core.Filter;
using Blog.Core.Hubs;
using Blog.Core.IServices;
using Blog.Core.Model;
using Blog.Core.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
@ -111,6 +113,8 @@ builder.Services.AddControllers(o =>
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
options.SerializerSettings.Converters.Add(new StringEnumConverter());
//将long类型转为string
options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64));
})
//.AddFluentValidation(config =>
//{

View File

@ -3,6 +3,7 @@ using System.Reflection;
using System.Text;
using Autofac;
using Blog.Core.Common;
using Blog.Core.Common.Helper;
using Blog.Core.Common.LogHelper;
using Blog.Core.Common.Seed;
using Blog.Core.Extensions;
@ -10,6 +11,7 @@ using Blog.Core.Extensions.Middlewares;
using Blog.Core.Filter;
using Blog.Core.Hubs;
using Blog.Core.IServices;
using Blog.Core.Model;
using Blog.Core.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
@ -123,6 +125,8 @@ namespace Blog.Core
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
//添加Enum转string
options.SerializerSettings.Converters.Add(new StringEnumConverter());
//将long类型转为string
options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64));
});
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());

View File

@ -0,0 +1,174 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Blog.Core.Common.Helper
{
/// <inheritdoc />
/// <summary>
/// 大数据json序列化重写
/// </summary>
public sealed class NumberConverter : JsonConverter
{
/// <summary>
/// 转换成字符串的类型
/// </summary>
private readonly NumberConverterShip _ship;
/// <summary>
/// 大数据json序列化重写实例化
/// </summary>
public NumberConverter()
{
_ship = (NumberConverterShip)0xFF;
}
/// <summary>
/// 大数据json序列化重写实例化
/// </summary>
/// <param name="ship">转换成字符串的类型</param>
public NumberConverter(NumberConverterShip ship)
{
_ship = ship;
}
/// <inheritdoc />
/// <summary>
/// 确定此实例是否可以转换指定的对象类型。
/// </summary>
/// <param name="objectType">对象的类型。</param>
/// <returns>如果此实例可以转换指定的对象类型,则为:<c>true</c>,否则为:<c>false</c></returns>
public override bool CanConvert(Type objectType)
{
var typecode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType);
switch (typecode)
{
case TypeCode.Decimal:
return (_ship & NumberConverterShip.Decimal) == NumberConverterShip.Decimal;
case TypeCode.Double:
return (_ship & NumberConverterShip.Double) == NumberConverterShip.Double;
case TypeCode.Int64:
return (_ship & NumberConverterShip.Int64) == NumberConverterShip.Int64;
case TypeCode.UInt64:
return (_ship & NumberConverterShip.UInt64) == NumberConverterShip.UInt64;
case TypeCode.Single:
return (_ship & NumberConverterShip.Single) == NumberConverterShip.Single;
default: return false;
}
}
/// <inheritdoc />
/// <summary>
/// 读取对象的JSON表示。
/// </summary>
/// <param name="reader">从 <see cref="T:Newtonsoft.Json.JsonReader" /> 中读取。</param>
/// <param name="objectType">对象的类型。</param>
/// <param name="existingValue">正在读取的对象的现有值。</param>
/// <param name="serializer">调用的序列化器实例。</param>
/// <returns>对象值。</returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return AsType(reader.Value.ToString(), objectType);
}
/// <summary>
/// 字符串格式数据转其他类型数据
/// </summary>
/// <param name="input">输入的字符串</param>
/// <param name="destinationType">目标格式</param>
/// <returns>转换结果</returns>
public static object AsType(string input, Type destinationType)
{
try
{
var converter = TypeDescriptor.GetConverter(destinationType);
if (converter.CanConvertFrom(typeof(string)))
{
return converter.ConvertFrom(null, null, input);
}
converter = TypeDescriptor.GetConverter(typeof(string));
if (converter.CanConvertTo(destinationType))
{
return converter.ConvertTo(null, null, input, destinationType);
}
}
catch
{
return null;
}
return null;
}
/// <inheritdoc />
/// <summary>
/// 写入对象的JSON表示形式。
/// </summary>
/// <param name="writer">要写入的 <see cref="T:Newtonsoft.Json.JsonWriter" /> 。</param>
/// <param name="value">要写入对象值</param>
/// <param name="serializer">调用的序列化器实例。</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
writer.WriteNull();
}
else
{
var objectType = value.GetType();
var typeCode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType);
switch (typeCode)
{
case TypeCode.Decimal:
writer.WriteValue(((decimal)value).ToString("f6"));
break;
case TypeCode.Double:
writer.WriteValue(((double)value).ToString("f4"));
break;
case TypeCode.Single:
writer.WriteValue(((float)value).ToString("f2"));
break;
default:
writer.WriteValue(value.ToString());
break;
}
}
}
}
/// <summary>
/// 转换成字符串的类型
/// </summary>
[Flags]
public enum NumberConverterShip
{
/// <summary>
/// 长整数
/// </summary>
Int64 = 1,
/// <summary>
/// 无符号长整数
/// </summary>
UInt64 = 2,
/// <summary>
/// 浮点数
/// </summary>
Single = 4,
/// <summary>
/// 双精度浮点数
/// </summary>
Double = 8,
/// <summary>
/// 大数字
/// </summary>
Decimal =16
}
}

View File

@ -109,7 +109,7 @@ namespace Blog.Core.Common.Seed
if (!myContext.Db.DbMaintenance.IsAnyTable(t.Name))
{
Console.WriteLine(t.Name);
myContext.Db.CodeFirst.InitTables(t);
myContext.Db.CodeFirst.SplitTables().InitTables(t);
}
});
ConsoleHelper.WriteSuccessLine($"Tables created successfully!");

View File

@ -23,7 +23,7 @@ namespace Blog.Core.IServices.BASE
Task<bool> DeleteById(object id);
Task<bool> Delete(TEntity model);
Task<bool> DeleteByIds(object[] ids);
Task<bool> Update(TEntity model);
@ -59,6 +59,14 @@ namespace Blog.Core.IServices.BASE
Expression<Func<T, T2, T3, TResult>> selectExpression,
Expression<Func<T, T2, T3, bool>> whereLambda = null) where T : class, new();
Task<PageModel<TEntity>> QueryPage(PaginationModel pagination);
#region
Task<TEntity> QueryByIdSplit(object objId);
Task<List<long>> AddSplit(TEntity entity);
Task<bool> DeleteSplit(TEntity entity, DateTime dateTime);
Task<bool> UpdateSplit(TEntity entity, DateTime dateTime);
Task<PageModel<TEntity>> QueryPageSplit(Expression<Func<TEntity, bool>> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null);
#endregion
}
}

View File

@ -0,0 +1,15 @@
using Blog.Core.IServices.BASE;
using Blog.Core.Model.Models;
using System.Threading.Tasks;
namespace Blog.Core.IServices
{
/// <summary>
/// sysUserInfoServices
/// </summary>
public interface ISplitDemoServices : IBaseServices<SplitDemo>
{
}
}

View File

@ -0,0 +1,27 @@
using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Blog.Core.Model.Models
{
[SplitTable(SplitType.Day)]//按年分表 (自带分表支持 年、季、月、周、日)
[SugarTable("SplitDemo_{year}{month}{day}")]//3个变量必须要有这么设计为了兼容开始按年后面改成按月、按日
public class SplitDemo
{
[SugarColumn(IsPrimaryKey = true)]
public long Id { get; set; }
public string Name { get; set; }
[SugarColumn(IsNullable = true)]//设置为可空字段 (更多用法看文档 迁移)
public DateTime UpdateTime { get; set; }
[SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表
public DateTime CreateTime { get; set; }
}
}

View File

@ -5,6 +5,7 @@ using Blog.Core.Model;
using Blog.Core.Model.Models;
using Blog.Core.Model.Tenants;
using Blog.Core.Repository.UnitOfWorks;
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
using SqlSugar;
using System;
using System.Collections.Generic;
@ -127,7 +128,6 @@ namespace Blog.Core.Repository.Base
return await insert.ExecuteReturnIdentityAsync();
}
/// <summary>
/// 写入实体数据
/// </summary>
@ -557,5 +557,75 @@ namespace Blog.Core.Repository.Base
// groupName = s.groupName,
// jobName = s.jobName
// }, exp, s => new { s.uID, s.uRealName, s.groupName, s.jobName }, model.currentPage, model.pageSize, model.orderField + " " + model.orderType);
#region Split分表基础接口 CRUD
/// <summary>
/// 分页查询[使用版本,其他分页未测试]
/// </summary>
/// <param name="whereExpression">条件表达式</param>
/// <param name="pageIndex">页码下标0</param>
/// <param name="pageSize">页大小</param>
/// <param name="orderByFields">排序字段如name asc,age desc</param>
/// <returns></returns>
public async Task<PageModel<TEntity>> QueryPageSplit(Expression<Func<TEntity, bool>> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null)
{
RefAsync<int> totalCount = 0;
var list = await _db.Queryable<TEntity>().SplitTable(beginTime, endTime)
.OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields)
.WhereIF(whereExpression != null, whereExpression)
.ToPageListAsync(pageIndex, pageSize, totalCount);
var data= new PageModel<TEntity>(pageIndex, totalCount, pageSize, list);
return data;
}
/// <summary>
/// 写入实体数据
/// </summary>
/// <param name="entity">数据实体</param>
/// <returns></returns>
public async Task<List<long>> AddSplit(TEntity entity)
{
var insert = _db.Insertable(entity).SplitTable();
//插入并返回雪花ID并且自动赋值ID 
return await insert.ExecuteReturnSnowflakeIdListAsync();
}
/// <summary>
/// 更新实体数据
/// </summary>
/// <param name="entity">数据实体</param>
/// <returns></returns>
public async Task<bool> UpdateSplit(TEntity entity, DateTime dateTime)
{
//直接根据实体集合更新 (全自动 找表更新)
//return await _db.Updateable(entity).SplitTable().ExecuteCommandAsync();//,SplitTable不能少
//精准找单个表
var tableName = _db.SplitHelper<TEntity>().GetTableName(dateTime);//根据时间获取表名
return await _db.Updateable(entity).AS(tableName).ExecuteCommandHasChangeAsync();
}
/// <summary>
/// 删除数据
/// </summary>
/// <param name="entity"></param>
/// <param name="dateTime"></param>
/// <returns></returns>
public async Task<bool> DeleteSplit(TEntity entity,DateTime dateTime)
{
////直接根据实体集合删除 (全自动 找表插入),返回受影响数
//return await _db.Deleteable(entity).SplitTable().ExecuteCommandAsync();//,SplitTable不能少
//精准找单个表
var tableName = _db.SplitHelper<TEntity>().GetTableName(dateTime);//根据时间获取表名
return await _db.Deleteable<TEntity>().AS(tableName).Where(entity).ExecuteCommandHasChangeAsync();
}
/// <summary>
/// 根据ID查找数据
/// </summary>
/// <param name="objId"></param>
/// <returns></returns>
public async Task<TEntity> QueryByIdSplit(object objId)
{
return await _db.Queryable<TEntity>().In(objId).SplitTable(tabs => tabs).SingleAsync();
}
#endregion
}
}

View File

@ -208,5 +208,45 @@ namespace Blog.Core.IRepository.Base
int pageIndex = 1,
int pageSize = 20,
string orderByFields = null);
#region
/// <summary>
/// 通过ID查询
/// </summary>
/// <param name="objId"></param>
/// <returns></returns>
Task<TEntity> QueryByIdSplit(object objId);
/// <summary>
/// 自动分表插入
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
Task<List<long>> AddSplit(TEntity entity);
/// <summary>
/// 删除
/// </summary>
/// <param name="entity"></param>
/// <param name="dateTime"></param>
/// <returns></returns>
Task<bool> DeleteSplit(TEntity entity, DateTime dateTime);
/// <summary>
/// 更新
/// </summary>
/// <param name="entity"></param>
/// <param name="dateTime"></param>
/// <returns></returns>
Task<bool> UpdateSplit(TEntity entity, DateTime dateTime);
/// <summary>
/// 分页查询
/// </summary>
/// <param name="whereExpression"></param>
/// <param name="beginTime"></param>
/// <param name="endTime"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="orderByFields"></param>
/// <returns></returns>
Task<PageModel<TEntity>> QueryPageSplit(Expression<Func<TEntity, bool>> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null);
#endregion
}
}

View File

@ -332,5 +332,36 @@ namespace Blog.Core.Services.BASE
var express = DynamicLinqFactory.CreateLambda<TEntity>(pagination.Conditions);
return await QueryPage(express, pagination.PageIndex, pagination.PageSize, pagination.OrderByFileds);
}
#region
public async Task<List<long>> AddSplit(TEntity entity)
{
return await BaseDal.AddSplit(entity);
}
public async Task<bool> UpdateSplit(TEntity entity, DateTime dateTime)
{
return await BaseDal.UpdateSplit(entity, dateTime);
}
/// <summary>
/// 根据实体删除一条数据
/// </summary>
/// <param name="entity">博文实体类</param>
/// <returns></returns>
public async Task<bool> DeleteSplit(TEntity entity, DateTime dateTime)
{
return await BaseDal.DeleteSplit(entity, dateTime);
}
public async Task<TEntity> QueryByIdSplit(object objId)
{
return await BaseDal.QueryByIdSplit(objId);
}
public async Task<PageModel<TEntity>> QueryPageSplit(Expression<Func<TEntity, bool>> whereExpression, DateTime beginTime, DateTime endTime,
int pageIndex = 1, int pageSize = 20, string orderByFields = null)
{
return await BaseDal.QueryPageSplit(whereExpression, beginTime, endTime,
pageIndex, pageSize, orderByFields);
}
#endregion
}
}

View File

@ -0,0 +1,23 @@
using Blog.Core.IRepository.Base;
using Blog.Core.IServices;
using Blog.Core.Model.Models;
using Blog.Core.Services.BASE;
using System.Linq;
using System.Threading.Tasks;
namespace Blog.Core.FrameWork.Services
{
/// <summary>
/// sysUserInfoServices
/// </summary>
public class SplitDemoServices : BaseServices<SplitDemo>, ISplitDemoServices
{
private readonly IBaseRepository<SplitDemo> _splitDemoRepository;
public SplitDemoServices(IBaseRepository<SplitDemo> splitDemoRepository)
{
_splitDemoRepository = splitDemoRepository;
}
}
}