开发环境:Windows 10专业版、Visual Studio Code、.Net6、SqlSugar5.x、Navicat
根据需求,对数据处理时,部分用到Sqlite进行增删改查、部分用到MySql进行增删改查
问题1:对于程序集中的模型对象映射需要根据对应连接进行初始化处理
问题2:对于需要延迟进行模型初始化的特殊处理(根据字段自动分表数据模型)
1、通过ConfigId来区分数据库连接
扩展IoC容器,自定义SqlSugar的注入,一方面是代码复用,另外就是简化配置与代码可读性
builder.Services.SetupSqlSugar(options =>
{
options.connectionConfigs = new List<ConnectionConfig>
{
new ConnectionConfig
{
ConnectionString = builder.Configuration.GetConnectionString("Sqlite"),
DbType = DbType.Sqlite,
ConfigId = "sqlite",
InitKeyType = InitKeyType.Attribute,
IsAutoCloseConnection = true
},
new ConnectionConfig
{
ConnectionString = builder.Configuration.GetConnectionString("MySql"),
DbType = DbType.MySql,
ConfigId = "mysql",
ConfigureExternalServices = new ConfigureExternalServices()
{
SplitTableService = new DefaultSplitTableService()
},
InitKeyType = InitKeyType.Attribute,
IsAutoCloseConnection = true
}
};
});
SetupSqlSugar函数原型:public static IServiceCollection SetupSqlSugar(this IServiceCollection services, Action<SqlSugarOptions> setup)
SqlSugarOptions是一个包装了ConnectionConfig的对象:
public class SqlSugarOptions
{
public List<ConnectionConfig> connectionConfigs { get; set; }
}
对于IoC容器的扩展方法主要是使用asp.netcore中常用的配置模式,将配置对象填充需要的值后放置备用的思路,具体使用
services.AddOptions();
services.Configure(setup);
2、实现简单仓储模式
现实简单仓储模式的目的就是应对增删改查有足够灵活、全面的方法实现,这点上面SqlSugar简直是太贴心已经有比较好的实现(基本在实际应用中没有遇到短板)接口ISimpleClient
与实现SimpleClient
namespace SqlSugar
{
public interface ISimpleClient<T> where T : class, new()
{
SimpleClient<ChangeType> Change<ChangeType>() where ChangeType : class, new();
RepositoryType ChangeRepository<RepositoryType>() where RepositoryType : ISugarRepository;
IDeleteable<T> AsDeleteable();
IInsertable<T> AsInsertable(List<T> insertObjs);
IInsertable<T> AsInsertable(T insertObj);
IInsertable<T> AsInsertable(T[] insertObjs);
ISugarQueryable<T> AsQueryable();
ISqlSugarClient AsSugarClient();
ITenant AsTenant();
IUpdateable<T> AsUpdateable(List<T> updateObjs);
IUpdateable<T> AsUpdateable(T updateObj);
IUpdateable<T> AsUpdateable();
IUpdateable<T> AsUpdateable(T[] updateObjs);
int Count(Expression<Func<T, bool>> whereExpression);
bool Delete(Expression<Func<T, bool>> whereExpression);
bool Delete(T deleteObj);
bool Delete(List<T> deleteObjs);
bool DeleteById(dynamic id);
bool DeleteByIds(dynamic[] ids);
T GetById(dynamic id);
List<T> GetList();
List<T> GetList(Expression<Func<T, bool>> whereExpression);
List<T> GetPageList(Expression<Func<T, bool>> whereExpression, PageModel page);
List<T> GetPageList(Expression<Func<T, bool>> whereExpression, PageModel page, Expression<Func<T, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc);
List<T> GetPageList(List<IConditionalModel> conditionalList, PageModel page);
List<T> GetPageList(List<IConditionalModel> conditionalList, PageModel page, Expression<Func<T, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc);
T GetSingle(Expression<Func<T, bool>> whereExpression);
T GetFirst(Expression<Func<T, bool>> whereExpression);
bool Insert(T insertObj);
bool InsertOrUpdate(T data);
bool InsertOrUpdate(List<T> datas);
bool InsertRange(List<T> insertObjs);
bool InsertRange(T[] insertObjs);
int InsertReturnIdentity(T insertObj);
long InsertReturnBigIdentity(T insertObj);
long InsertReturnSnowflakeId(T insertObj);
List<long> InsertReturnSnowflakeId(List<T> insertObjs);
T InsertReturnEntity(T insertObj);
bool IsAny(Expression<Func<T, bool>> whereExpression);
bool Update(Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression);
bool UpdateSetColumnsTrue(Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression);
bool Update(T updateObj);
bool UpdateRange(List<T> updateObjs);
bool UpdateRange(T[] updateObjs);
Task<int> CountAsync(Expression<Func<T, bool>> whereExpression);
Task<bool> DeleteAsync(Expression<Func<T, bool>> whereExpression);
Task<bool> DeleteAsync(T deleteObj);
Task<bool> DeleteAsync(List<T> deleteObjs);
Task<bool> DeleteByIdAsync(dynamic id);
Task<bool> DeleteByIdsAsync(dynamic[] ids);
Task<T> GetByIdAsync(dynamic id);
Task<List<T>> GetListAsync();
Task<List<T>> GetListAsync(Expression<Func<T, bool>> whereExpression);
Task<List<T>> GetPageListAsync(Expression<Func<T, bool>> whereExpression, PageModel page);
Task<List<T>> GetPageListAsync(Expression<Func<T, bool>> whereExpression, PageModel page, Expression<Func<T, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc);
Task<List<T>> GetPageListAsync(List<IConditionalModel> conditionalList, PageModel page);
Task<List<T>> GetPageListAsync(List<IConditionalModel> conditionalList, PageModel page, Expression<Func<T, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc);
Task<T> GetSingleAsync(Expression<Func<T, bool>> whereExpression);
Task<T> GetFirstAsync(Expression<Func<T, bool>> whereExpression);
Task<bool> InsertAsync(T insertObj);
Task<bool> InsertOrUpdateAsync(T data);
Task<bool> InsertOrUpdateAsync(List<T> datas);
Task<bool> InsertRangeAsync(List<T> insertObjs);
Task<bool> InsertRangeAsync(T[] insertObjs);
Task<int> InsertReturnIdentityAsync(T insertObj);
Task<long> InsertReturnBigIdentityAsync(T insertObj);
Task<long> InsertReturnSnowflakeIdAsync(T insertObj);
Task<List<long>> InsertReturnSnowflakeIdAsync(List<T> insertObjs);
Task<T> InsertReturnEntityAsync(T insertObj);
Task<bool> IsAnyAsync(Expression<Func<T, bool>> whereExpression);
Task<bool> UpdateSetColumnsTrueAsync(Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression);
Task<bool> UpdateAsync(Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression);
Task<bool> UpdateAsync(T updateObj);
Task<bool> UpdateRangeAsync(List<T> updateObjs);
Task<bool> UpdateRangeAsync(T[] updateObjs);
}
}
基于这个方便设计一个仓储基类
public interface IRepositoryBase<T> : ISimpleClient<T>
where T : class, new()
{
}
public class RepositoryBase<T> : SimpleClient<T>, IRepositoryBase<T>
where T : class, new()
{
public RepositoryBase(ISqlSugarClient context) : base(context) { }
}
3、实现SetupSqlSugar扩展方法
SqlSugar中有提供一个TenantAttribute
的Attribute,考虑到SqlSugar有可能使用此Attribute进行其他的处理,稳妥起见实现一个CodeFirstAttribute
,通过反射枚举此Attribute来进行对应数据库的InitTables来进行数据库与对象表的自动建立
public static class SqlSugarSetup
{
public static IServiceCollection SetupSqlSugar(this IServiceCollection services, Action<SqlSugarOptions> setup)
{
services.AddOptions();
services.Configure(setup);
return services.AddSingleton<ISqlSugarClient, SqlSugarScope>(provider =>
{
var logger = provider.GetRequiredService<ILoggerFactory>().CreateLogger("SqlSugar");
var options = provider.GetRequiredService<IOptions<SqlSugarOptions>>().Value;
// 将ConnectionConfig列表中未指定ConfigId值设置为"default", 与CodeFirstAttribute进行关联
foreach (var config in options.connectionConfigs.Where(config =>
config.ConfigId == null)) config.ConfigId = "default";
var sugarScope = new SqlSugarScope(options.connectionConfigs);
foreach (var item in options.connectionConfigs)
{
logger.LogInformation($"DbType: {item.DbType}, ConnectionString: {item.ConnectionString}");
}
#if DEBUG
sugarScope.Aop.OnLogExecuted = (sql, args) =>
{
logger.LogInformation(sql, args);
};
#endif
sugarScope.Aop.OnError = ex =>
{
logger.LogError(ex.InnerException?.Message);
};
sugarScope.DbMaintenance.CreateDatabase();
List<Assembly> assemblies = AppDomain.CurrentDomain.GetCurrentAppAssemblies();
var types = assemblies.SelectMany(x => x.GetExportedTypes())
.Where(x => !x.IsAbstract && x.IsClass && x.IsDefined(typeof(CodeFirstAttribute)));
foreach (var config in options.connectionConfigs)
{
var sugar = sugarScope.AsTenant().GetConnectionScope(config.ConfigId);
var configuredTypes = types.Where(x =>
x.GetCustomAttribute<CodeFirstAttribute>()?.ConfigId.Equals(config.ConfigId, StringComparison.OrdinalIgnoreCase))
.ToArray();
sugar.CodeFirst.InitTables(configuredTypes);
}
return sugarScope;
});
}
}
其中CodeFirstAttribute
实现
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class CodeFirstAttribute: Attribute
{
/// <summary>
/// 配置ID
/// 用于区分不同的连接
/// </summary>
public string ConfigId { get; set; }
public CodeFirstAttribute(string configId = "default")
{
ConfigId = configId;
}
}
主要的作用是在反射扫描程序集时枚举CodeFirstAttribute
属性,并放入集合为下一步的逻辑处理提供支持
List<Assembly> assemblies = AppDomain.CurrentDomain.GetCurrentAppAssemblies();
var types = assemblies.SelectMany(x => x.GetExportedTypes())
.Where(x => !x.IsAbstract && x.IsClass && x.IsDefined(typeof(CodeFirstAttribute)));
依据数据库的ConfigId
来对实体映射到相应的数据库
foreach (var config in options.connectionConfigs)
{
var sugar = sugarScope.AsTenant().GetConnectionScope(config.ConfigId);
var configuredTypes = types.Where(x =>
x.GetCustomAttribute<CodeFirstAttribute>()?.ConfigId.Equals(config.ConfigId, StringComparison.OrdinalIgnoreCase))
.ToArray();
sugar.CodeFirst.InitTables(configuredTypes);
}
4、将租户模式集成到简单仓储模式
所使用的配置集合时,sqlite的配置是首个元素、mysql处于下一个元素,通过调试可以了解到注入到仓储接口中的ISqlSugarClient为集合中首个元素的实列
在实际的使用过程中不可能保证到哪个Connection放在首位,鉴于这类情况,为了保证简单仓库兼容租户模式的使用,需要将ISqlSugarClient定位到对应的对象context.AsTenant().GetConnectionScope(ConfigId)
至此,不同类型数据库租户加简单仓储模式已经完成,各个类型的实体和相应的数据库进行映射,自动维护表结构
5、根据表字段自动分表、自动联表操作
在MySql数据库中存在一个数据较大的表,为了优化操作考虑用分表来优化设计,对后期的操作提供性能上的优势;SqlSugar支持以时间分表(内置)、自定义分表的功能,我们这里需要进行分表的字段是非时间性的字段,所以选用自定义分表的功能;
-
需要在配置数据库时指定
ConnectionConfig.ConfigureExternalServices.SplitTableService
ConfigureExternalServices = new ConfigureExternalServices() { SplitTableService = new DefaultSplitTableService() }
-
在需要自动分表的实体模型上面加上
[SplitTable(SplitType._Custom01)]
属性 -
在分表字段上面加上
[SplitField]
属性
另外,需要实现一个继承ISplitTableService
接口的服务,依次实现接口方法
public class DefaultSplitTableService : ISplitTableService
{
public List<SplitTableInfo> GetAllTables(ISqlSugarClient db, EntityInfo EntityInfo, List<DbTableInfo> tableInfos)
{
var splitType = EntityInfo.Type.GetCustomAttribute<SplitTableAttribute>()?.SplitType;
var tableName = EntityInfo.DbTableName;
List<SplitTableInfo> result = new List<SplitTableInfo>();
if (splitType == null || splitType != SplitType._Custom01) return result;
foreach (var tableInfo in tableInfos)
{
if (tableInfo.Name.Contains(tableName, StringComparison.OrdinalIgnoreCase))
{
SplitTableInfo info = new SplitTableInfo();
info.TableName = tableInfo.Name;
result.Add(info);
}
}
return result;
}
public object GetFieldValue(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object entityValue)
{
var splitColumn = entityInfo.Columns.FirstOrDefault(x =>
x.PropertyInfo.GetCustomAttribute<SplitFieldAttribute>() != null);
var value = splitColumn.PropertyInfo.GetValue(entityValue, null);
return value;
}
public string GetTableName(ISqlSugarClient db, EntityInfo EntityInfo)
{
throw new NotImplementedException();
}
public string GetTableName(ISqlSugarClient db, EntityInfo EntityInfo, SplitType type)
{
throw new NotImplementedException();
}
public string GetTableName(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object fieldValue)
{
return entityInfo.DbTableName + "_" + fieldValue;
}
}
在GetTableName
中返回最终生成分表名的规则.
对于有自动分表功能的表增删改查的过程中需要联表支持,需要在IUpdateable
、IDeleteable
、IInsertable
、ISugarQueryable
调用方法SplitTable(Func<List<SplitTableInfo>, IEnumerable<SplitTableInfo>> getTableNamesFunc)
6、验证自动分表
当前数据库中不存在workinfo_自动生成测试
的表,调用接口测试
使用Navicat查看数据库中表
根据新增的数据
查看并验证数据
SqlSugar ORM 5.X 官网 (donet5.com)
标签:CodeFirst,Task,映射,List,whereExpression,var,分表,Expression,public From: https://www.cnblogs.com/linxmouse/p/17148724.html