diff --git a/sample/Util.Samples.Angular.AntDesign/Startup.cs b/sample/Util.Samples.Angular.AntDesign/Startup.cs index 5fb97dc7..c59b1ec5 100644 --- a/sample/Util.Samples.Angular.AntDesign/Startup.cs +++ b/sample/Util.Samples.Angular.AntDesign/Startup.cs @@ -57,6 +57,8 @@ namespace Util.Samples { //services.AddUnitOfWork( Configuration.GetConnectionString( "PgSqlConnection" ) ); //======= 支持MySql ======= //services.AddUnitOfWork( Configuration.GetConnectionString( "MySqlConnection" ) ); + //======= 支持Oracle ======= + //services.AddUnitOfWork( Configuration.GetConnectionString( "OracleConnection" ) ); //添加Swagger services.AddSwaggerGen( options => { diff --git a/sample/Util.Samples.Angular.AntDesign/appsettings.json b/sample/Util.Samples.Angular.AntDesign/appsettings.json index 53aecd7a..52ce8a6c 100644 --- a/sample/Util.Samples.Angular.AntDesign/appsettings.json +++ b/sample/Util.Samples.Angular.AntDesign/appsettings.json @@ -2,7 +2,8 @@ "ConnectionStrings": { "DefaultConnection": "Server=.\\sql2014;Database=Sample;uid=sa;pwd=sa;MultipleActiveResultSets=true", "PgSqlConnection": "server=192.168.244.138;User Id=postgres;password=sa;database=Sample", - "MySqlConnection": "server=192.168.244.138;user id=root;password=sa;database=Sample;port=3306;CharSet=utf8;" + "MySqlConnection": "server=192.168.244.138;user id=root;password=sa;database=Sample;port=3306;CharSet=utf8;", + "OracleConnection": "Data Source=localhost/ORCL;User Id=system;Password=admin;" }, "Logging": { "IncludeScopes": false, diff --git a/sample/Util.Samples.Angular.Material/Startup.cs b/sample/Util.Samples.Angular.Material/Startup.cs index 5d549ca4..44a2b2fd 100644 --- a/sample/Util.Samples.Angular.Material/Startup.cs +++ b/sample/Util.Samples.Angular.Material/Startup.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Swashbuckle.AspNetCore.Swagger; using Util.Datas.Ef; +using Util.Datas.Ef.Configs; using Util.Logs.Extensions; using Util.Samples.Data; using Util.Webs.Extensions; @@ -52,6 +53,8 @@ namespace Util.Samples { //services.AddUnitOfWork( Configuration.GetConnectionString( "PgSqlConnection" ) ); //======= 支持MySql ======= //services.AddUnitOfWork( Configuration.GetConnectionString( "MySqlConnection" ) ); + //======= 支持Oracle 12g ======= + //services.AddUnitOfWork( Configuration.GetConnectionString( "OracleConnection" ) ); //添加Swagger services.AddSwaggerGen( options => { diff --git a/sample/Util.Samples.Angular.Material/appsettings.json b/sample/Util.Samples.Angular.Material/appsettings.json index 7de22617..481bffe5 100644 --- a/sample/Util.Samples.Angular.Material/appsettings.json +++ b/sample/Util.Samples.Angular.Material/appsettings.json @@ -2,7 +2,8 @@ "ConnectionStrings": { "DefaultConnection": "Server=.\\sql2014;Database=Sample;uid=sa;pwd=sa;MultipleActiveResultSets=true", "PgSqlConnection": "server=192.168.244.138;User Id=postgres;password=sa;database=Sample", - "MySqlConnection": "server=192.168.244.138;user id=root;password=sa;database=Sample;port=3306;CharSet=utf8;" + "MySqlConnection": "server=192.168.244.138;user id=root;password=sa;database=Sample;port=3306;CharSet=utf8;", + "OracleConnection": "Data Source=localhost/ORCL;User Id=system;Password=admin;" }, "Logging": { "IncludeScopes": false, diff --git a/sample/Util.Samples.Data/DbScripts/Oracle/Sample.sql b/sample/Util.Samples.Data/DbScripts/Oracle/Sample.sql new file mode 100644 index 00000000..d9a1cac9 --- /dev/null +++ b/sample/Util.Samples.Data/DbScripts/Oracle/Sample.sql @@ -0,0 +1,55 @@ +/*========================================================== 创建架构 ===========================================================*/ +CREATE USER "Systems" +IDENTIFIED BY system; + +/*========================================================== 创建表 ===========================================================*/ + +/*==============================================================*/ +/* Table: Application */ +/*==============================================================*/ + +create table "Systems"."Application" +( + "ApplicationId" RAW(16) not null, + "Code" NVARCHAR2(60) not null, + "Name" NVARCHAR2(200) not null, + "Comment" NVARCHAR2(500), + "Enabled" SMALLINT not null, + "RegisterEnabled" SMALLINT not null, + "CreationTime" DATE, + "CreatorId" RAW(16), + "LastModificationTime" DATE, + "LastModifierId" RAW(16), + "IsDeleted" SMALLINT not null, + "Version" RAW(72), + constraint PK_APPLICATION primary key ("ApplicationId") +); + + +/*==============================================================*/ +/* Table: "Role" */ +/*==============================================================*/ +create table "Systems"."Role" +( + "RoleId" RAW(16) not null, + "Code" NVARCHAR2(256) not null, + "Name" NVARCHAR2(256) not null, + "NormalizedName" NVARCHAR2(256) not null, + "Type" NVARCHAR2(80) not null, + "IsAdmin" SMALLINT not null, + "ParentId" RAW(16), + "Path" NVARCHAR2(800) not null, + "Level" INTEGER not null, + "SortId" INTEGER, + "Enabled" SMALLINT not null, + "Comment" NVARCHAR2(500), + "PinYin" NVARCHAR2(200), + "Sign" NVARCHAR2(256), + "CreationTime" DATE, + "CreatorId" RAW(16), + "LastModificationTime" DATE, + "LastModifierId" RAW(16), + "IsDeleted" SMALLINT not null, + "Version" RAW(72), + constraint PK_ROLE primary key ("RoleId") +); diff --git a/sample/Util.Samples.Data/Mappings/Systems/Oracle/ApplicationMap.cs b/sample/Util.Samples.Data/Mappings/Systems/Oracle/ApplicationMap.cs new file mode 100644 index 00000000..3503f154 --- /dev/null +++ b/sample/Util.Samples.Data/Mappings/Systems/Oracle/ApplicationMap.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Samples.Domain.Models; + +namespace Util.Samples.Data.Mappings.Systems.Oracle { + /// + /// 应用程序映射配置 + /// + public class ApplicationMap : Util.Datas.Ef.Oracle.AggregateRootMap { + /// + /// 映射表 + /// + protected override void MapTable( EntityTypeBuilder builder ) { + builder.ToTable( "Application", "Systems" ); + } + + /// + /// 映射属性 + /// + protected override void MapProperties( EntityTypeBuilder builder ) { + //应用程序编号 + builder.Property( t => t.Id ) + .HasColumnName( "ApplicationId" ); + builder.HasQueryFilter( t => t.IsDeleted == false ); + } + } +} diff --git a/sample/Util.Samples.Data/Mappings/Systems/Oracle/RoleMap.cs b/sample/Util.Samples.Data/Mappings/Systems/Oracle/RoleMap.cs new file mode 100644 index 00000000..87a13a37 --- /dev/null +++ b/sample/Util.Samples.Data/Mappings/Systems/Oracle/RoleMap.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Samples.Domain.Models; + +namespace Util.Samples.Data.Mappings.Systems.Oracle { + /// + /// 角色映射配置 + /// + public class RoleMap : Util.Datas.Ef.Oracle.AggregateRootMap { + /// + /// 映射表 + /// + protected override void MapTable( EntityTypeBuilder builder ) { + builder.ToTable( "Role", "Systems" ); + } + + /// + /// 映射属性 + /// + protected override void MapProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ).HasColumnName( "RoleId" ); + builder.Property( t => t.Path ).HasColumnName( "Path" ); + builder.Property( t => t.Level ).HasColumnName( "Level" ); + builder.Property( t => t.IsAdmin ).HasColumnName( "IsAdmin" ); + builder.HasQueryFilter( t => t.IsDeleted == false ); + } + } +} diff --git a/sample/Util.Samples.Data/Mappings/Systems/PgSql/ApplicationMap.cs b/sample/Util.Samples.Data/Mappings/Systems/PgSql/ApplicationMap.cs index 55d53c3c..f3266cd7 100644 --- a/sample/Util.Samples.Data/Mappings/Systems/PgSql/ApplicationMap.cs +++ b/sample/Util.Samples.Data/Mappings/Systems/PgSql/ApplicationMap.cs @@ -13,14 +13,14 @@ namespace Util.Samples.Data.Mappings.Systems.PgSql { protected override void MapTable( EntityTypeBuilder builder ) { builder.ToTable( "Application", "Systems" ); } - + /// /// 映射属性 /// protected override void MapProperties( EntityTypeBuilder builder ) { //应用程序编号 - builder.Property(t => t.Id) - .HasColumnName("ApplicationId"); + builder.Property( t => t.Id ) + .HasColumnName( "ApplicationId" ); builder.HasQueryFilter( t => t.IsDeleted == false ); } } diff --git a/sample/Util.Samples.Data/UnitOfWorks/Oracle/SampleUnitOfWork.cs b/sample/Util.Samples.Data/UnitOfWorks/Oracle/SampleUnitOfWork.cs new file mode 100644 index 00000000..fb785e60 --- /dev/null +++ b/sample/Util.Samples.Data/UnitOfWorks/Oracle/SampleUnitOfWork.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace Util.Samples.Data.UnitOfWorks.Oracle { + /// + /// 工作单元 + /// + public class SampleUnitOfWork : Util.Datas.Ef.Oracle.UnitOfWork,ISampleUnitOfWork { + /// + /// 初始化工作单元 + /// + /// 配置项 + public SampleUnitOfWork( DbContextOptions options ) : base( options ) { + } + } +} \ No newline at end of file diff --git a/sample/Util.Samples.Service/Implements/Systems/ApplicationService.cs b/sample/Util.Samples.Service/Implements/Systems/ApplicationService.cs index f21658cc..aa8a8e24 100644 --- a/sample/Util.Samples.Service/Implements/Systems/ApplicationService.cs +++ b/sample/Util.Samples.Service/Implements/Systems/ApplicationService.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Threading.Tasks; +using System.Threading.Tasks; using Util.Applications; using Util.Datas.Sql; using Util.Domains.Repositories; @@ -59,18 +58,6 @@ namespace Util.Samples.Service.Implements.Systems { return dto.MapTo( new Application( dto.Id.ToGuid() ) ); } - /// - /// 查询 - /// - /// 查询参数 - public override async Task> QueryAsync( ApplicationQuery query ) { - return await SqlQuery - .Select( true ) - .From( "a" ) - .OrIfNotEmpty( t => t.Code.Contains( query.Keyword ), t => t.Name.Contains( query.Keyword ) ) - .ToListAsync(); - } - /// /// 分页查询 /// diff --git a/src/Util.Caches/04-Util.Caches.csproj b/src/Util.Caches/04-Util.Caches.csproj index 5fe03f71..362c84e8 100644 --- a/src/Util.Caches/04-Util.Caches.csproj +++ b/src/Util.Caches/04-Util.Caches.csproj @@ -33,10 +33,10 @@ Util是一个.net core平台下的应用框架,旨在提升小型团队的开 - - - - + + + + diff --git a/src/Util.Datas/07-Util.Datas.csproj b/src/Util.Datas/07-Util.Datas.csproj index 3a932e77..188e093d 100644 --- a/src/Util.Datas/07-Util.Datas.csproj +++ b/src/Util.Datas/07-Util.Datas.csproj @@ -36,12 +36,13 @@ Util是一个.net core平台下的应用框架,旨在提升小型团队的开 - + - - - - + + + + + diff --git a/src/Util.Datas/Dapper/Extensions.Service.cs b/src/Util.Datas/Dapper/Extensions.Service.cs index 1b891ccd..38270c45 100644 --- a/src/Util.Datas/Dapper/Extensions.Service.cs +++ b/src/Util.Datas/Dapper/Extensions.Service.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Util.Datas.Dapper.Handlers; using Util.Datas.Dapper.MySql; +using Util.Datas.Dapper.Oracle; using Util.Datas.Dapper.PgSql; using Util.Datas.Dapper.SqlServer; using Util.Datas.Enums; @@ -85,6 +86,9 @@ namespace Util.Datas.Dapper { case DatabaseType.MySql: services.TryAddTransient(); return; + case DatabaseType.Oracle: + services.TryAddTransient(); + return; default: throw new NotImplementedException( $"Sql生成器未实现 {config.DatabaseType.Description()} 数据库" ); } diff --git a/src/Util.Datas/Dapper/Oracle/OracleBuilder.cs b/src/Util.Datas/Dapper/Oracle/OracleBuilder.cs new file mode 100644 index 00000000..6b7da05d --- /dev/null +++ b/src/Util.Datas/Dapper/Oracle/OracleBuilder.cs @@ -0,0 +1,65 @@ +using Util.Datas.Sql; +using Util.Datas.Sql.Builders; +using Util.Datas.Sql.Builders.Core; +using Util.Datas.Sql.Matedatas; + +namespace Util.Datas.Dapper.Oracle { + /// + /// Oracle Sql生成器 + /// + public class OracleBuilder : SqlBuilderBase { + /// + /// 初始化Sql生成器 + /// + /// 实体元数据解析器 + /// 表数据库 + /// 参数管理器 + public OracleBuilder( IEntityMatedata matedata = null, ITableDatabase tableDatabase = null, IParameterManager parameterManager = null ) + : base( matedata, tableDatabase, parameterManager ) { + } + + /// + /// 复制Sql生成器 + /// + public override ISqlBuilder Clone() { + var sqlBuilder = new OracleBuilder(); + sqlBuilder.Clone( this ); + return sqlBuilder; + } + + /// + /// 获取Sql方言 + /// + protected override IDialect GetDialect() { + return new OracleDialect(); + } + + /// + /// 创建Sql生成器 + /// + public override ISqlBuilder New() { + return new OracleBuilder( EntityMatedata, TableDatabase, ParameterManager ); + } + + /// + /// 创建From子句 + /// + protected override IFromClause CreateFromClause() { + return new OracleFromClause( this, GetDialect(), EntityResolver, AliasRegister,TableDatabase ); + } + + /// + /// 创建Join子句 + /// + protected override IJoinClause CreateJoinClause() { + return new OracleJoinClause( this, GetDialect(), EntityResolver, AliasRegister, ParameterManager, TableDatabase ); + } + + /// + /// 创建分页Sql + /// + protected override string CreateLimitSql() { + return $"Limit {GetLimitParam()} OFFSET {GetOffsetParam()}"; + } + } +} \ No newline at end of file diff --git a/src/Util.Datas/Dapper/Oracle/OracleDialect.cs b/src/Util.Datas/Dapper/Oracle/OracleDialect.cs new file mode 100644 index 00000000..2bd3d21e --- /dev/null +++ b/src/Util.Datas/Dapper/Oracle/OracleDialect.cs @@ -0,0 +1,51 @@ +using Util.Datas.Sql.Builders.Core; + +namespace Util.Datas.Dapper.Oracle { + /// + /// Oracle方言 + /// + public class OracleDialect : DialectBase { + /// + /// 起始转义标识符 + /// + public override string OpeningIdentifier => "\""; + + /// + /// 结束转义标识符 + /// + public override string ClosingIdentifier => "\""; + + /// + /// 获取安全名称 + /// + /// 名称 + protected override string GetSafeName( string name ) { + return $"\"{name}\""; + } + + /// + /// 获取参数前缀 + /// + public override string GetPrefix() { + return ":"; + } + + /// + /// 创建参数名 + /// + /// 参数索引 + public override string GenerateName( int paramIndex ) { + return $"{GetPrefix()}p_{paramIndex}"; + } + + /// + /// 获取参数名 + /// + /// 参数名 + public override string GetParamName( string paramName ) { + if ( paramName.StartsWith( ":" ) ) + return paramName.TrimStart( ':' ); + return paramName; + } + } +} \ No newline at end of file diff --git a/src/Util.Datas/Dapper/Oracle/OracleFromClause.cs b/src/Util.Datas/Dapper/Oracle/OracleFromClause.cs new file mode 100644 index 00000000..d8f1c2cb --- /dev/null +++ b/src/Util.Datas/Dapper/Oracle/OracleFromClause.cs @@ -0,0 +1,46 @@ +using Util.Datas.Sql; +using Util.Datas.Sql.Builders; +using Util.Datas.Sql.Builders.Clauses; +using Util.Datas.Sql.Builders.Core; +using Util.Datas.Sql.Matedatas; + +namespace Util.Datas.Dapper.Oracle { + /// + /// Oracle From子句 + /// + public class OracleFromClause : FromClause { + /// + /// 初始化From子句 + /// + /// Sql生成器 + /// 方言 + /// 实体解析器 + /// 实体别名注册器 + /// 表数据库 + /// 表 + public OracleFromClause( ISqlBuilder builder, IDialect dialect, IEntityResolver resolver, IEntityAliasRegister register, ITableDatabase tableDatabase, SqlItem table = null ) + : base( builder, dialect, resolver, register, tableDatabase, table ) { + } + + /// + /// 创建Sql项 + /// + /// 表名 + /// 架构名 + /// 别名 + protected override SqlItem CreateSqlItem( string table, string schema, string alias ) { + return new OracleSqlItem( table, schema, alias ); + } + + /// + /// 复制From子句 + /// + /// Sql生成器 + /// 实体别名注册器 + public override IFromClause Clone( ISqlBuilder builder, IEntityAliasRegister register ) { + if( register != null ) + register.FromType = Register.FromType; + return new OracleFromClause( builder, Dialect, Resolver, register, TableDatabase, Table ); + } + } +} diff --git a/src/Util.Datas/Dapper/Oracle/OracleJoinClause.cs b/src/Util.Datas/Dapper/Oracle/OracleJoinClause.cs new file mode 100644 index 00000000..70a321f5 --- /dev/null +++ b/src/Util.Datas/Dapper/Oracle/OracleJoinClause.cs @@ -0,0 +1,39 @@ +using System; +using Util.Datas.Sql; +using Util.Datas.Sql.Builders; +using Util.Datas.Sql.Builders.Clauses; +using Util.Datas.Sql.Builders.Core; +using Util.Datas.Sql.Matedatas; + +namespace Util.Datas.Dapper.Oracle { + /// + /// Oracle 表连接子句 + /// + public class OracleJoinClause : JoinClause { + /// + /// 初始化Oracle 表连接子句 + /// + /// Sql生成器 + /// 方言 + /// 实体解析器 + /// 实体注册器 + /// 参数管理器 + /// 表数据库 + public OracleJoinClause( ISqlBuilder sqlBuilder, IDialect dialect, IEntityResolver resolver, IEntityAliasRegister register, + IParameterManager parameterManager, ITableDatabase tableDatabase ) + : base( sqlBuilder, dialect, resolver, register, parameterManager, tableDatabase ) { + } + + /// + /// 创建连接项 + /// + /// 连接类型 + /// 表名 + /// 架构名 + /// 别名 + /// 实体类型 + protected override JoinItem CreateJoinItem( string joinType, string table, string schema, string alias, Type type = null ) { + return new JoinItem( joinType, table, schema, alias, false, false, type ); + } + } +} diff --git a/src/Util.Datas/Dapper/Oracle/OracleSqlItem.cs b/src/Util.Datas/Dapper/Oracle/OracleSqlItem.cs new file mode 100644 index 00000000..8fdc99e4 --- /dev/null +++ b/src/Util.Datas/Dapper/Oracle/OracleSqlItem.cs @@ -0,0 +1,29 @@ +using Util.Datas.Sql.Builders; +using Util.Datas.Sql.Builders.Core; +using Util.Datas.Sql.Matedatas; + +namespace Util.Datas.Dapper.Oracle { + /// + /// Sql项 + /// + public class OracleSqlItem : SqlItem{ + /// + /// 初始化Sql项 + /// + /// 名称 + /// 前缀 + /// 别名 + /// 使用原始值 + /// 是否用句点分割名称 + public OracleSqlItem( string name, string prefix = null, string alias = null, bool raw = false, bool isSplit = true ) + :base( name, prefix, alias, raw, isSplit ) { + } + + /// + /// 获取列名和别名 + /// + protected override string GetColumnAlias( IDialect dialect, ITableDatabase tableDatabase ) { + return $"{GetColumn( dialect, tableDatabase )} {GetSafeName( dialect, Alias )}"; + } + } +} diff --git a/src/Util.Datas/Ef/Extensions.Service.cs b/src/Util.Datas/Ef/Extensions.Service.cs index 1ff70539..042a2cd7 100644 --- a/src/Util.Datas/Ef/Extensions.Service.cs +++ b/src/Util.Datas/Ef/Extensions.Service.cs @@ -29,7 +29,7 @@ namespace Util.Datas.Ef { where TImplementation : UnitOfWorkBase, TService { services.AddDbContext( configAction ); var efConfig = new EfConfig(); - if ( efConfigAction != null ) { + if( efConfigAction != null ) { services.Configure( efConfigAction ); efConfigAction.Invoke( efConfig ); } @@ -55,6 +55,8 @@ namespace Util.Datas.Ef { return DatabaseType.MySql; if( type == typeof( Util.Datas.Ef.PgSql.UnitOfWork ) ) return DatabaseType.PgSql; + if( type == typeof( Util.Datas.Ef.Oracle.UnitOfWork ) ) + return DatabaseType.Oracle; return DatabaseType.SqlServer; } @@ -88,6 +90,9 @@ namespace Util.Datas.Ef { case DatabaseType.PgSql: builder.UseNpgsql( connection ); return; + case DatabaseType.Oracle: + builder.UseOracle( connection ); + return; } } diff --git a/src/Util.Datas/Ef/Oracle/AggregateRootMap.cs b/src/Util.Datas/Ef/Oracle/AggregateRootMap.cs new file mode 100644 index 00000000..ec6d95b6 --- /dev/null +++ b/src/Util.Datas/Ef/Oracle/AggregateRootMap.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Datas.Ef.Core; +using Util.Domains; + +namespace Util.Datas.Ef.Oracle { + /// + /// 聚合根映射配置 + /// + /// 实体类型 + public abstract class AggregateRootMap : MapBase, IMap where TEntity : class, IVersion { + /// + /// 映射乐观离线锁 + /// + protected override void MapVersion( EntityTypeBuilder builder ) { + builder.Property( t => t.Version ).IsConcurrencyToken(); + } + } +} \ No newline at end of file diff --git a/src/Util.Datas/Ef/Oracle/EntityMap.cs b/src/Util.Datas/Ef/Oracle/EntityMap.cs new file mode 100644 index 00000000..dd14e59a --- /dev/null +++ b/src/Util.Datas/Ef/Oracle/EntityMap.cs @@ -0,0 +1,10 @@ +using Util.Datas.Ef.Core; + +namespace Util.Datas.Ef.Oracle { + /// + /// 实体映射配置 + /// + /// 实体类型 + public abstract class EntityMap : MapBase, IMap where TEntity : class { + } +} \ No newline at end of file diff --git a/src/Util.Datas/Ef/Oracle/IMap.cs b/src/Util.Datas/Ef/Oracle/IMap.cs new file mode 100644 index 00000000..1e76c884 --- /dev/null +++ b/src/Util.Datas/Ef/Oracle/IMap.cs @@ -0,0 +1,7 @@ +namespace Util.Datas.Ef.Oracle { + /// + /// 映射 + /// + public interface IMap : Util.Datas.Ef.Core.IMap { + } +} \ No newline at end of file diff --git a/src/Util.Datas/Ef/Oracle/UnitOfWork.cs b/src/Util.Datas/Ef/Oracle/UnitOfWork.cs new file mode 100644 index 00000000..2a8f94c4 --- /dev/null +++ b/src/Util.Datas/Ef/Oracle/UnitOfWork.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Util.Datas.Ef.Core; +using Util.Datas.Ef.Internal; + +namespace Util.Datas.Ef.Oracle { + /// + /// Oracle工作单元 + /// + public abstract class UnitOfWork : UnitOfWorkBase { + /// + /// 初始化Oracle工作单元 + /// + /// 配置 + /// 服务提供器 + protected UnitOfWork( DbContextOptions options, IServiceProvider serviceProvider = null ) + : base( options, serviceProvider ) { + } + + /// + /// 获取映射接口类型 + /// + protected override Type GetMapType() { + return typeof( IMap ); + } + + /// + /// 获取映射实例列表 + /// + /// 程序集 + protected override IEnumerable GetMapInstances( Assembly assembly ) { + return Util.Helpers.Reflection.GetInstancesByInterface( assembly ); + } + + /// + /// 拦截添加操作 + /// + protected override void InterceptAddedOperation( EntityEntry entry ) { + base.InterceptAddedOperation( entry ); + Helper.InitVersion( entry ); + } + + /// + /// 拦截修改操作 + /// + protected override void InterceptModifiedOperation( EntityEntry entry ) { + base.InterceptModifiedOperation( entry ); + Helper.InitVersion( entry ); + } + } +} diff --git a/src/Util.Datas/Sql/Builders/Core/ParameterManager.cs b/src/Util.Datas/Sql/Builders/Core/ParameterManager.cs index 912090b8..5f54efa4 100644 --- a/src/Util.Datas/Sql/Builders/Core/ParameterManager.cs +++ b/src/Util.Datas/Sql/Builders/Core/ParameterManager.cs @@ -36,7 +36,9 @@ namespace Util.Datas.Sql.Builders.Core { /// 创建参数名 /// public string GenerateName() { - return $"{_dialect.GetPrefix()}_p_{_paramIndex++}"; + var result =_dialect.GenerateName( _paramIndex ); + _paramIndex += 1; + return result; } /// @@ -55,6 +57,7 @@ namespace Util.Datas.Sql.Builders.Core { public void Add( string name, object value, Operator? @operator = null ) { if( string.IsNullOrWhiteSpace( name ) ) return; + name = _dialect.GetParamName( name ); if ( _params.ContainsKey( name ) ) _params.Remove( name ); _params.Add( name, GetValue( value, @operator ) ); diff --git a/src/Util.Ui.Angular.AntDesign/Zorro/Buttons/ButtonTagHelper.cs b/src/Util.Ui.Angular.AntDesign/Zorro/Buttons/ButtonTagHelper.cs index da726dd5..f0fb9001 100644 --- a/src/Util.Ui.Angular.AntDesign/Zorro/Buttons/ButtonTagHelper.cs +++ b/src/Util.Ui.Angular.AntDesign/Zorro/Buttons/ButtonTagHelper.cs @@ -53,11 +53,11 @@ namespace Util.Ui.Zorro.Buttons { /// public string Loading { get; set; } /// - /// [nzBlock],将按钮宽度调整为其父宽度 + /// [nzBlock],将按钮宽度调整为其父宽度,默认值: false /// public bool Block { get; set; } /// - /// [nzGhost],设置为透明背景 + /// [nzGhost],设置为透明背景,默认值: false /// public bool Ghost { get; set; } /// diff --git a/src/Util.Ui.Angular.AntDesign/Zorro/Icons/Builders/IconBuilder.cs b/src/Util.Ui.Angular.AntDesign/Zorro/Icons/Builders/IconBuilder.cs index 9bdc5c82..837eb76e 100644 --- a/src/Util.Ui.Angular.AntDesign/Zorro/Icons/Builders/IconBuilder.cs +++ b/src/Util.Ui.Angular.AntDesign/Zorro/Icons/Builders/IconBuilder.cs @@ -9,7 +9,7 @@ namespace Util.Ui.Zorro.Icons.Builders { /// 初始化图标生成器 /// public IconBuilder() : base( "i" ) { - AddAttribute( "nz-icon" ); + base.AddAttribute( "nz-icon" ); } /// diff --git a/src/Util.Ui.Angular.AntDesign/Zorro/Links/LinkTagHelper.cs b/src/Util.Ui.Angular.AntDesign/Zorro/Links/LinkTagHelper.cs index 8f565dda..bd19fc5b 100644 --- a/src/Util.Ui.Angular.AntDesign/Zorro/Links/LinkTagHelper.cs +++ b/src/Util.Ui.Angular.AntDesign/Zorro/Links/LinkTagHelper.cs @@ -21,7 +21,7 @@ namespace Util.Ui.Zorro.Links { /// public string BindText { get; set; } /// - /// 颜色 + /// nzType,颜色 /// public Color Color { get; set; } /// @@ -29,13 +29,21 @@ namespace Util.Ui.Zorro.Links { /// public ButtonSize Size { get; set; } /// + /// nzShape,按钮形状 + /// + public ButtonShape Shape { get; set; } + /// /// 图标 /// public AntDesignIcon Icon { get; set; } /// - /// 禁用 + /// [nzBlock],将按钮宽度调整为其父宽度,默认值: false /// - public string Disabled { get; set; } + public bool Block { get; set; } + /// + /// [nzGhost],设置为透明背景,默认值: false + /// + public bool Ghost { get; set; } /// /// 提示 /// diff --git a/src/Util.Ui.Angular.AntDesign/Zorro/Links/Renders/LinkRender.cs b/src/Util.Ui.Angular.AntDesign/Zorro/Links/Renders/LinkRender.cs index 470d75b0..bd26f80b 100644 --- a/src/Util.Ui.Angular.AntDesign/Zorro/Links/Renders/LinkRender.cs +++ b/src/Util.Ui.Angular.AntDesign/Zorro/Links/Renders/LinkRender.cs @@ -4,6 +4,7 @@ using Util.Ui.Builders; using Util.Ui.Configs; using Util.Ui.Enums; using Util.Ui.Extensions; +using Util.Ui.Zorro.Icons.Builders; namespace Util.Ui.Zorro.Links.Renders { /// @@ -39,8 +40,8 @@ namespace Util.Ui.Zorro.Links.Renders { ConfigId( builder ); ConfigButton( builder ); ConfigStyle( builder ); + ConfigIcon( builder ); ConfigText( builder ); - ConfigDisabled( builder ); ConfigTooltip( builder ); ConfigLink( builder ); ConfigEvents( builder ); @@ -65,21 +66,25 @@ namespace Util.Ui.Zorro.Links.Renders { builder.AddAttribute( "[nzGhost]", _config.GetBoolValue( UiConst.Ghost ) ); } + /// + /// 配置图标 + /// + private void ConfigIcon( TagBuilder builder ) { + if( _config.Contains( UiConst.Icon ) == false ) + return; + var iconBuilder = new IconBuilder(); + iconBuilder.AddType( _config.GetValue( UiConst.Icon )?.Description() ); + builder.AppendContent( iconBuilder ); + } + /// /// 配置文本 /// private void ConfigText( TagBuilder builder ) { if( _config.Contains( UiConst.Text ) ) - builder.SetContent( _config.GetValue( UiConst.Text ) ); + builder.AppendContent( _config.GetValue( UiConst.Text ) ); if( _config.Contains( AngularConst.BindText ) ) - builder.SetContent( $"{{{{{_config.GetValue( AngularConst.BindText )}}}}}" ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); + builder.AppendContent( $"{{{{{_config.GetValue( AngularConst.BindText )}}}}}" ); } /// @@ -105,14 +110,5 @@ namespace Util.Ui.Zorro.Links.Renders { private void ConfigEvents( TagBuilder builder ) { builder.AddAttribute( "(click)", _config.GetValue( UiConst.OnClick ) ); } - - /// - /// 配置内容 - /// - protected override void ConfigContent( TagBuilder builder ) { - if( _config.Contains( UiConst.Text ) || _config.Contains( AngularConst.BindText ) ) - return; - builder.SetContent( _config.Content ); - } } } \ No newline at end of file diff --git a/src/Util/01-Util.csproj b/src/Util/01-Util.csproj index 3471cd0e..f1235fa0 100644 --- a/src/Util/01-Util.csproj +++ b/src/Util/01-Util.csproj @@ -37,8 +37,8 @@ Util是一个.net core平台下的应用框架,旨在提升小型团队的开 - - + + @@ -46,9 +46,9 @@ Util是一个.net core平台下的应用框架,旨在提升小型团队的开 - + - + diff --git a/src/Util/Datas/Enums/DatabaseType.cs b/src/Util/Datas/Enums/DatabaseType.cs index 0c1a8d7e..444a7bb9 100644 --- a/src/Util/Datas/Enums/DatabaseType.cs +++ b/src/Util/Datas/Enums/DatabaseType.cs @@ -14,6 +14,10 @@ /// /// PostgreSQL数据库 /// - PgSql + PgSql, + /// + /// Oracle数据库 + /// + Oracle } } diff --git a/src/Util/Datas/Sql/Builders/Core/DialectBase.cs b/src/Util/Datas/Sql/Builders/Core/DialectBase.cs index 3e045f6f..d63cfc69 100644 --- a/src/Util/Datas/Sql/Builders/Core/DialectBase.cs +++ b/src/Util/Datas/Sql/Builders/Core/DialectBase.cs @@ -46,5 +46,21 @@ public virtual string GetPrefix() { return "@"; } + + /// + /// 创建参数名 + /// + /// 参数索引 + public virtual string GenerateName( int paramIndex ) { + return $"{GetPrefix()}_p_{paramIndex}"; + } + + /// + /// 获取参数名 + /// + /// 参数名 + public virtual string GetParamName( string paramName ) { + return paramName; + } } } diff --git a/src/Util/Datas/Sql/Builders/Core/SqlItem.cs b/src/Util/Datas/Sql/Builders/Core/SqlItem.cs index 1cd2a5d9..3327c638 100644 --- a/src/Util/Datas/Sql/Builders/Core/SqlItem.cs +++ b/src/Util/Datas/Sql/Builders/Core/SqlItem.cs @@ -106,21 +106,21 @@ namespace Util.Datas.Sql.Builders.Core { /// /// 获取Sql /// - public string ToSql( IDialect dialect = null, ITableDatabase tableDatabase = null ) { + public virtual string ToSql( IDialect dialect = null, ITableDatabase tableDatabase = null ) { if( string.IsNullOrWhiteSpace( Name ) ) return null; if( Raw ) return Name; - return string.IsNullOrWhiteSpace( Alias ) ? GetColumn( dialect, tableDatabase ) : $"{GetColumn( dialect, tableDatabase )} As {GetSafeName( dialect, Alias )}"; + return string.IsNullOrWhiteSpace( Alias ) ? GetColumn( dialect, tableDatabase ) : GetColumnAlias( dialect, tableDatabase ); } /// /// 获取列 /// - private string GetColumn( IDialect dialect, ITableDatabase tableDatabase ) { + protected string GetColumn( IDialect dialect, ITableDatabase tableDatabase ) { var result = new StringBuilder(); var database = DatabaseName; - if ( string.IsNullOrWhiteSpace( DatabaseName ) && tableDatabase != null ) + if( string.IsNullOrWhiteSpace( DatabaseName ) && tableDatabase != null ) database = tableDatabase.GetDatabase( GetName() ); if( string.IsNullOrWhiteSpace( database ) == false ) result.Append( $"{GetSafeName( dialect, database )}." ); @@ -133,8 +133,8 @@ namespace Util.Datas.Sql.Builders.Core { /// /// 获取名称 /// - private string GetName() { - if ( string.IsNullOrWhiteSpace( Prefix ) ) + protected string GetName() { + if( string.IsNullOrWhiteSpace( Prefix ) ) return Name; return $"{Prefix}.{Name}"; } @@ -142,10 +142,17 @@ namespace Util.Datas.Sql.Builders.Core { /// /// 获取安全名称 /// - private string GetSafeName( IDialect dialect, string name ) { + protected string GetSafeName( IDialect dialect, string name ) { if( dialect == null ) return name; return dialect.SafeName( name ); } + + /// + /// 获取列名和别名 + /// + protected virtual string GetColumnAlias( IDialect dialect, ITableDatabase tableDatabase ) { + return $"{GetColumn( dialect, tableDatabase )} As {GetSafeName( dialect, Alias )}"; + } } } diff --git a/src/Util/Datas/Sql/Builders/IDialect.cs b/src/Util/Datas/Sql/Builders/IDialect.cs index 7e0b1954..8437c2ff 100644 --- a/src/Util/Datas/Sql/Builders/IDialect.cs +++ b/src/Util/Datas/Sql/Builders/IDialect.cs @@ -20,5 +20,15 @@ /// 获取参数前缀 /// string GetPrefix(); + /// + /// 创建参数名 + /// + /// 参数索引 + string GenerateName( int paramIndex ); + /// + /// 获取参数名 + /// + /// 参数名 + string GetParamName( string paramName ); } } diff --git a/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/Clauses/FromClauseTest.cs b/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/Clauses/FromClauseTest.cs new file mode 100644 index 00000000..7a356b23 --- /dev/null +++ b/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/Clauses/FromClauseTest.cs @@ -0,0 +1,71 @@ +using Util.Datas.Dapper.Oracle; +using Util.Datas.Sql.Builders.Clauses; +using Util.Datas.Sql.Builders.Core; +using Util.Datas.Tests.Sql.Builders.Samples; +using Xunit; + +namespace Util.Datas.Tests.Sql.Builders.Oracle.Clauses { + /// + /// From子句测试 + /// + public class FromClauseTest { + /// + /// From子句 + /// + private readonly FromClause _clause; + /// + /// 表数据库 + /// + private readonly TestTableDatabase _database; + + /// + /// 测试初始化 + /// + public FromClauseTest() { + _database = new TestTableDatabase(); + _clause = new OracleFromClause( null, new OracleDialect(), new EntityResolver(), new EntityAliasRegister(), null ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql() { + return _clause.ToSql(); + } + + /// + /// 默认输出空 + /// + [Fact] + public void TestDefault() { + Assert.Null( GetSql() ); + } + + /// + /// 设置表 + /// + [Fact] + public void TestFrom_1() { + _clause.From( "a" ); + Assert.Equal( "From \"a\"", GetSql() ); + } + + /// + /// 设置表 - 别名 + /// + [Fact] + public void TestFrom_2() { + _clause.From( "a", "b" ); + Assert.Equal( "From \"a\" \"b\"", GetSql() ); + } + + /// + /// 设置表 - 架构 + /// + [Fact] + public void TestFrom_3() { + _clause.From( "a.b", "c" ); + Assert.Equal( "From \"a\".\"b\" \"c\"", GetSql() ); + } + } +} diff --git a/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/Clauses/WhereClauseTest.cs b/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/Clauses/WhereClauseTest.cs new file mode 100644 index 00000000..9496f7bd --- /dev/null +++ b/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/Clauses/WhereClauseTest.cs @@ -0,0 +1,56 @@ +using Util.Datas.Dapper.Oracle; +using Util.Datas.Dapper.SqlServer; +using Util.Datas.Sql.Builders.Clauses; +using Util.Datas.Sql.Builders.Core; +using Util.Datas.Tests.Sql.Builders.Samples; +using Xunit; + +namespace Util.Datas.Tests.Sql.Builders.Oracle.Clauses { + /// + /// Where子句测试 + /// + public class WhereClauseTest { + /// + /// 参数管理器 + /// + private readonly ParameterManager _parameterManager; + /// + /// 表数据库 + /// + private readonly TestTableDatabase _database; + /// + /// Sql生成器 + /// + private readonly SqlServerBuilder _builder; + /// + /// Where子句 + /// + private readonly WhereClause _clause; + + /// + /// 测试初始化 + /// + public WhereClauseTest() { + _parameterManager = new ParameterManager( new OracleDialect() ); + _database = new TestTableDatabase(); + _builder = new SqlServerBuilder( new TestEntityMatedata(), null, _parameterManager ); + _clause = new WhereClause( _builder, new OracleDialect(), new EntityResolver(), new EntityAliasRegister(), _parameterManager ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql() { + return _clause.ToSql(); + } + + /// + /// 设置条件 + /// + [Fact] + public void TestWhere_1() { + _clause.Where( "Name", "a" ); + Assert.Equal( "Where \"Name\"=:p_0", GetSql() ); + } + } +} diff --git a/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/OracleBuilderTest.cs b/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/OracleBuilderTest.cs new file mode 100644 index 00000000..8e3d4b1c --- /dev/null +++ b/test/Util.Datas.Tests.Integration/Sql/Builders/Oracle/OracleBuilderTest.cs @@ -0,0 +1,53 @@ +using Util.Datas.Dapper.Oracle; +using Util.Datas.Queries; +using Util.Datas.Sql; +using Util.Datas.Tests.Samples; +using Xunit; +using Xunit.Abstractions; +using String = Util.Helpers.String; + +namespace Util.Datas.Tests.Sql.Builders.Oracle { + /// + /// Oracle Sql生成器测试 + /// + public class OracleBuilderTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// Oracle Sql生成器 + /// + private OracleBuilder _builder; + + /// + /// 测试初始化 + /// + public OracleBuilderTest( ITestOutputHelper output ) { + _output = output; + _builder = new OracleBuilder(); + } + + /// + /// 设置条件 - 属性表达式 + /// + [Fact] + public void TestWhere() { + //结果 + var result = new String(); + result.AppendLine( "Select \"a\".\"Email\" " ); + result.AppendLine( "From \"Sample\" \"a\" " ); + result.Append( "Where \"a\".\"Email\"<>:p_0" ); + + //执行 + _builder.Select( t => new object[] { t.Email } ) + .From( "a" ) + .Where( t => t.Email, "abc", Operator.NotEqual ); + + //验证 + Assert.Equal( result.ToString(), _builder.ToSql() ); + Assert.Single( _builder.GetParams() ); + Assert.Equal( "abc", _builder.GetParams()["p_0"] ); + } + } +} diff --git a/test/Util.Ui.Angular.AntDesign.Tests/Zorro/LinkTagHelperTest.cs b/test/Util.Ui.Angular.AntDesign.Tests/Zorro/LinkTagHelperTest.cs index 09ac5035..73e31baf 100644 --- a/test/Util.Ui.Angular.AntDesign.Tests/Zorro/LinkTagHelperTest.cs +++ b/test/Util.Ui.Angular.AntDesign.Tests/Zorro/LinkTagHelperTest.cs @@ -90,6 +90,17 @@ namespace Util.Ui.Angular.AntDesign.Tests.Zorro { Assert.Equal( result.ToString(), GetResult( attributes ) ); } + /// + /// 测试图标 + /// + [Fact] + public void TestIcon() { + var attributes = new TagHelperAttributeList { { UiConst.Text, "a" },{ UiConst.Icon, AntDesignIcon.Alert } }; + var result = new String(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + /// /// 测试颜色 /// @@ -113,13 +124,35 @@ namespace Util.Ui.Angular.AntDesign.Tests.Zorro { } /// - /// 测试禁用 + /// 测试调整为父宽度 /// [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, "a" } }; + public void TestBlock() { + var attributes = new TagHelperAttributeList { { UiConst.Block, true } }; var result = new String(); - result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试形状 + /// + [Fact] + public void TestShape() { + var attributes = new TagHelperAttributeList { { UiConst.Shape, ButtonShape.Round } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试透明背景 + /// + [Fact] + public void TestGhost() { + var attributes = new TagHelperAttributeList { { UiConst.Ghost, true } }; + var result = new String(); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult( attributes ) ); }