From 55c1ea0aa2766562fd5aa71079641ec775b05bd3 Mon Sep 17 00:00:00 2001 From: Samuel Abraham Date: Tue, 30 Jan 2024 23:19:32 -0800 Subject: [PATCH] v8.1.0 --- src/TypeCache.GraphQL/Data/Connection.cs | 3 +- src/TypeCache.GraphQL/Data/Edge.cs | 3 +- .../Extensions/EnumExtensions.cs | 63 ++-- .../Extensions/GraphQLExtensions.cs | 22 +- .../Extensions/SchemaExtensions.cs | 97 +++--- .../Extensions/ServiceCollectionExtensions.cs | 18 +- .../Extensions/TypeScriptExtensions.cs | 22 +- .../Resolvers/SqlApiCallFieldResolver.cs | 2 +- .../Resolvers/SqlApiDeleteFieldResolver.cs | 8 +- .../Resolvers/SqlApiInsertFieldResolver.cs | 8 +- .../Resolvers/SqlApiSelectFieldResolver.cs | 12 +- .../Resolvers/SqlApiUpdateFieldResolver.cs | 8 +- src/TypeCache.GraphQL/SqlApi/DataResponse.cs | 11 +- .../SqlApi/OutputResponse.cs | 6 +- src/TypeCache.GraphQL/SqlApi/Parameter.cs | 4 +- .../SqlApi/SelectResponse.cs | 6 +- .../TypeCache.GraphQL.csproj | 2 +- .../Types/ConfigureSchema.cs | 14 +- .../Types/GraphQLBooleanType.cs | 18 -- .../Types/GraphQLEnumType.cs | 24 +- .../Types/GraphQLNumberType.cs | 41 --- .../Types/GraphQLScalarType.cs | 121 ++++---- .../Types/GraphQLStringType.cs | 43 --- src/TypeCache.GraphQL/Types/GraphQLUriType.cs | 54 +++- .../Web/GraphQLMiddleware.cs | 1 + .../Attributes/RequireClaimAttribute.cs | 2 +- .../Filters/SqlApiEndpointFilter.cs | 20 +- src/TypeCache.Web/Handlers/SqlApiHandler.cs | 28 +- .../SqlApiErrorHandlerMiddleware.cs | 1 - src/TypeCache.Web/Middleware/WebMiddleware.cs | 21 +- src/TypeCache.Web/TypeCache.Web.csproj | 2 +- src/TypeCache/Data/DataSource.cs | 279 ++++++++++-------- src/TypeCache/Data/DataSourceType.cs | 5 +- .../Data/Extensions/SqlExtensions.cs | 2 +- src/TypeCache/Data/IDataSource.cs | 25 +- .../Data/Mediation/SqlDataSetRule.cs | 2 +- .../Data/Mediation/SqlDataTableRule.cs | 2 +- .../Data/Mediation/SqlExecuteRule.cs | 2 +- .../Data/Mediation/SqlJsonArrayRule.cs | 2 +- src/TypeCache/Data/Mediation/SqlModelsRule.cs | 2 +- src/TypeCache/Data/Mediation/SqlScalarRule.cs | 8 +- src/TypeCache/Extensions/ArrayExtensions.cs | 10 +- .../Extensions/DateTimeExtensions.cs | 2 +- src/TypeCache/Extensions/EnumExtensions.cs | 253 +++++++++------- .../Extensions/EnumerableExtensions.cs | 54 +++- .../Extensions/EnumeratorExtensions.cs | 2 +- .../Extensions/ExpressionExtensions.cs | 1 - src/TypeCache/Extensions/JsonExtensions.cs | 5 +- src/TypeCache/Extensions/LoggerExtensions.cs | 17 +- .../Extensions/ReadOnlySpanExtensions.cs | 1 - .../ReflectionExtensions.ConstructorInfo.cs | 14 +- .../ReflectionExtensions.FieldInfo.cs | 8 +- .../ReflectionExtensions.MemberInfo.cs | 6 +- .../ReflectionExtensions.MethodBase.cs | 2 +- .../ReflectionExtensions.MethodInfo.cs | 2 +- .../ReflectionExtensions.ScalarType.cs | 4 - .../Extensions/ReflectionExtensions.Type.cs | 39 ++- .../Extensions/ServiceCollectionExtensions.cs | 14 +- .../Extensions/StringBuilderExtensions.cs | 20 +- src/TypeCache/Mediation/CustomAfterRule.cs | 12 +- src/TypeCache/Mediation/CustomRule.cs | 15 +- .../Mediation/CustomValidationRule.cs | 6 +- src/TypeCache/Mediation/IAfterRule.cs | 4 +- src/TypeCache/Mediation/IMediator.cs | 6 +- src/TypeCache/Mediation/IRule.cs | 4 +- src/TypeCache/Mediation/IValidationRule.cs | 2 +- src/TypeCache/Mediation/Mediator.cs | 59 ++-- src/TypeCache/Mediation/RuleFactory.cs | 10 + src/TypeCache/Mediation/RulesBuilder.cs | 38 +++ src/TypeCache/Net/Mediation/HttpClientRule.cs | 2 +- .../Net/Mediation/HttpClientValidationRule.cs | 9 +- src/TypeCache/TypeCache.csproj | 2 +- src/TypeCache/Utilities/Accessor.cs | 21 -- src/TypeCache/Utilities/IAccessor.cs | 11 - src/TypeCache/Utilities/IName.cs | 8 - src/TypeCache/Utilities/Singleton.cs | 9 + src/TypeCache/Utilities/TypeStore.cs | 35 ++- src/TypeCache/Utilities/ValueConverter.cs | 6 - tests/TypeCache.GraphQL.TestApp/Program.cs | 13 +- .../Tables/Person.cs | 4 +- .../TypeCache.GraphQL.TestApp.csproj | 2 +- .../Extensions/EnumExtensions.cs | 23 ++ tests/TypeCache.Tests/TypeCache.Tests.csproj | 4 +- 83 files changed, 919 insertions(+), 854 deletions(-) delete mode 100644 src/TypeCache.GraphQL/Types/GraphQLBooleanType.cs delete mode 100644 src/TypeCache.GraphQL/Types/GraphQLNumberType.cs delete mode 100644 src/TypeCache.GraphQL/Types/GraphQLStringType.cs delete mode 100644 src/TypeCache/Utilities/Accessor.cs delete mode 100644 src/TypeCache/Utilities/IAccessor.cs delete mode 100644 src/TypeCache/Utilities/IName.cs create mode 100644 src/TypeCache/Utilities/Singleton.cs diff --git a/src/TypeCache.GraphQL/Data/Connection.cs b/src/TypeCache.GraphQL/Data/Connection.cs index a6d05c6b..ac8056da 100644 --- a/src/TypeCache.GraphQL/Data/Connection.cs +++ b/src/TypeCache.GraphQL/Data/Connection.cs @@ -3,6 +3,7 @@ using System.Linq; using GraphQL.Resolvers; using GraphQL.Types; +using TypeCache.Extensions; using TypeCache.GraphQL.Attributes; using TypeCache.GraphQL.Extensions; using TypeCache.GraphQL.Types; @@ -67,7 +68,7 @@ public static ObjectGraphType CreateGraphType(string name, IGraphType dataGraphT graphType.AddField(new() { Name = nameof(Connection.TotalCount), - Type = typeof(GraphQLNumberType), + Type = ScalarType.Int32.ToGraphType(), Resolver = new FuncFieldResolver, int?>(context => context.Source.TotalCount) }); diff --git a/src/TypeCache.GraphQL/Data/Edge.cs b/src/TypeCache.GraphQL/Data/Edge.cs index 1d9d561e..8967d4ba 100644 --- a/src/TypeCache.GraphQL/Data/Edge.cs +++ b/src/TypeCache.GraphQL/Data/Edge.cs @@ -2,6 +2,7 @@ using GraphQL.Resolvers; using GraphQL.Types; +using TypeCache.Extensions; using TypeCache.GraphQL.Attributes; using TypeCache.GraphQL.Extensions; using TypeCache.GraphQL.Types; @@ -26,7 +27,7 @@ public static ObjectGraphType CreateGraphType(string name, IGraphType dataGraphT graphType.AddField(new() { Name = nameof(Edge.Cursor), - Type = typeof(GraphQLNumberType), + Type = ScalarType.Int32.ToGraphType(), Resolver = new FuncFieldResolver, int>(context => context.Source.Cursor) }); graphType.AddField(new() diff --git a/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs b/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs index 1c5bda6f..14f32408 100644 --- a/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs @@ -9,35 +9,36 @@ namespace TypeCache.GraphQL.Extensions; public static class EnumExtensions { - public static Type? ToGraphType(this ScalarType @this) => @this switch - { - ScalarType.Boolean => typeof(GraphQLBooleanType), - ScalarType.Byte => typeof(GraphQLNumberType), - ScalarType.Decimal => typeof(GraphQLNumberType), - ScalarType.DateOnly => typeof(GraphQLStringType), - ScalarType.DateTime => typeof(GraphQLStringType), - ScalarType.DateTimeOffset => typeof(GraphQLStringType), - ScalarType.Guid => typeof(GraphQLStringType), - ScalarType.Half => typeof(GraphQLNumberType), - ScalarType.Int16 => typeof(GraphQLNumberType), - ScalarType.Int32 or ScalarType.Index => typeof(GraphQLNumberType), - ScalarType.Int64 => typeof(GraphQLNumberType), - ScalarType.IntPtr => typeof(GraphQLNumberType), - ScalarType.Int128 => typeof(GraphQLNumberType), - ScalarType.UInt128 => typeof(GraphQLNumberType), - ScalarType.BigInteger => typeof(GraphQLNumberType), - ScalarType.UInt16 => typeof(GraphQLNumberType), - ScalarType.UInt32 => typeof(GraphQLNumberType), - ScalarType.UInt64 => typeof(GraphQLNumberType), - ScalarType.UIntPtr => typeof(GraphQLNumberType), - ScalarType.SByte => typeof(GraphQLNumberType), - ScalarType.Single => typeof(GraphQLNumberType), - ScalarType.Double => typeof(GraphQLNumberType), - ScalarType.Char => typeof(GraphQLStringType), - ScalarType.String => typeof(GraphQLStringType), - ScalarType.TimeOnly => typeof(GraphQLStringType), - ScalarType.TimeSpan => typeof(GraphQLStringType), - ScalarType.Uri => typeof(GraphQLUriType), - _ => null - }; + public static Type? ToGraphType(this ScalarType @this) + => @this switch + { + ScalarType.Boolean => typeof(GraphQLScalarType), + ScalarType.Byte => typeof(GraphQLScalarType), + ScalarType.Int16 => typeof(GraphQLScalarType), + ScalarType.Int32 or ScalarType.Index => typeof(GraphQLScalarType), + ScalarType.Int64 => typeof(GraphQLScalarType), + ScalarType.IntPtr => typeof(GraphQLScalarType), + ScalarType.Int128 => typeof(GraphQLScalarType), + ScalarType.BigInteger => typeof(GraphQLScalarType), + ScalarType.UInt16 => typeof(GraphQLScalarType), + ScalarType.UInt32 => typeof(GraphQLScalarType), + ScalarType.UInt64 => typeof(GraphQLScalarType), + ScalarType.UIntPtr => typeof(GraphQLScalarType), + ScalarType.UInt128 => typeof(GraphQLScalarType), + ScalarType.SByte => typeof(GraphQLScalarType), + ScalarType.Half => typeof(GraphQLScalarType), + ScalarType.Single => typeof(GraphQLScalarType), + ScalarType.Double => typeof(GraphQLScalarType), + ScalarType.Decimal => typeof(GraphQLScalarType), + ScalarType.DateOnly => typeof(GraphQLScalarType), + ScalarType.DateTime => typeof(GraphQLScalarType), + ScalarType.DateTimeOffset => typeof(GraphQLScalarType), + ScalarType.TimeOnly => typeof(GraphQLScalarType), + ScalarType.TimeSpan => typeof(GraphQLScalarType), + ScalarType.Guid => typeof(GraphQLScalarType), + ScalarType.Char => typeof(GraphQLScalarType), + ScalarType.String => typeof(GraphQLScalarType), + ScalarType.Uri => typeof(GraphQLUriType), + _ => null + }; } diff --git a/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs b/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs index 329fd971..6d87e215 100644 --- a/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs @@ -193,20 +193,20 @@ public static FieldType ToFieldType(this PropertyInfo @this) arguments.Add("null", type, description: "Return this value instead of null."); if (@this.PropertyType.IsAssignableTo()) - arguments.Add("format", description: "Use .NET format specifiers to format the data."); + arguments.Add>("format", description: "Use .NET format specifiers to format the data."); - if (type.Is>() || type.Is>>()) - arguments.Add("timeZone", description: Invariant($"{typeof(TimeZoneInfo).Namespace}.{nameof(TimeZoneInfo)}.{nameof(TimeZoneInfo.ConvertTimeBySystemTimeZoneId)}(value, [..., ...] | [UTC, ...])")); - else if (type.Is>() || type.Is>>()) - arguments.Add("timeZone", description: Invariant($"{typeof(TimeZoneInfo).Namespace}.{nameof(TimeZoneInfo)}.{nameof(TimeZoneInfo.ConvertTimeBySystemTimeZoneId)}(value, ...)")); - else if (type.Is() || type.Is>()) + if (type.Is>() || type.Is>>()) + arguments.Add>("timeZone", description: Invariant($"{typeof(TimeZoneInfo).Namespace}.{nameof(TimeZoneInfo)}.{nameof(TimeZoneInfo.ConvertTimeBySystemTimeZoneId)}(value, [..., ...] | [UTC, ...])")); + else if (type.Is>() || type.Is>>()) + arguments.Add>("timeZone", description: Invariant($"{typeof(TimeZoneInfo).Namespace}.{nameof(TimeZoneInfo)}.{nameof(TimeZoneInfo.ConvertTimeBySystemTimeZoneId)}(value, ...)")); + else if (type.Is>() || type.Is>>()) { arguments.Add>("case", description: "Convert string value to upper or lower case."); - arguments.Add>("length", description: "Exclude the rest of the string value if it exceeds this length."); - arguments.Add("match", description: "Returns the matching result based on the specified regular expression pattern, null if no match."); - arguments.Add("trim", description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.Trim)}(value)")); - arguments.Add("trimEnd", description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.TrimEnd)}(value)")); - arguments.Add("trimStart", description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.TrimStart)}(value)")); + arguments.Add>("length", description: "Exclude the rest of the string value if it exceeds this length."); + arguments.Add>("match", description: "Returns the matching result based on the specified regular expression pattern, null if no match."); + arguments.Add>("trim", description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.Trim)}(value)")); + arguments.Add>("trimEnd", description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.TrimEnd)}(value)")); + arguments.Add>("trimStart", description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.TrimStart)}(value)")); } return new() diff --git a/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs b/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs index f1e245b1..4bd616ac 100644 --- a/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs @@ -65,7 +65,6 @@ public static FieldType[] AddDatabaseSchemaQueries(this ISchema @this, IDataSour public static FieldType AddDatabaseSchemaQuery(this ISchema @this, IDataSource dataSource, SchemaCollection collection) { dataSource.AssertNotNull(); - var table = dataSource.GetDatabaseSchema(collection); var graphDatabasesEnum = new EnumerationGraphType { @@ -103,14 +102,14 @@ public static FieldType AddDatabaseSchemaQuery(this ISchema @this, IDataSource d return value switch { DBNull or null => null, - byte[] bytes when fieldType == typeof(GraphQLStringType) => bytes.ToBase64(), - _ when fieldType == typeof(GraphQLStringType) => value.ToString(), + byte[] bytes when fieldType == typeof(GraphQLScalarType) => bytes.ToBase64(), + _ when fieldType == typeof(GraphQLScalarType) => value.ToString(), _ => value }; }), Type = column.DataType switch { - _ when column.DataType == typeof(object) => typeof(GraphQLStringType), + _ when column.DataType == typeof(object) => typeof(GraphQLScalarType), _ when column.AllowDBNull => column.DataType.ToGraphQLType(false), _ => column.DataType.ToGraphQLType(false).ToNonNullGraphType() } @@ -125,7 +124,7 @@ public static FieldType AddDatabaseSchemaQuery(this ISchema @this, IDataSource d Description = Invariant($"Database Schema: {table.TableName}"), Arguments = new QueryArguments( new QueryArgument(graphDatabasesEnum) { Name = "database" }, - new QueryArgument { Name = "where" }, + new QueryArgument> { Name = "where" }, new QueryArgument(new ListGraphType(new NonNullGraphType(graphOrderByEnum))) { Name = "orderBy" } ), ResolvedType = new ListGraphType(resolvedType), @@ -178,7 +177,7 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour Description = Invariant($"`{column.Name}`"), Type = columnDataType switch { - _ when columnDataType == typeof(object) => typeof(GraphQLStringType), + _ when columnDataType == typeof(object) => typeof(GraphQLScalarType), _ => columnDataType.ToGraphQLType(false) } }); @@ -196,14 +195,14 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour return value switch { DBNull => null, - byte[] bytes when fieldType == typeof(GraphQLStringType) => bytes.ToBase64(), - _ when fieldType == typeof(GraphQLStringType) => value.ToString(), + byte[] bytes when fieldType == typeof(GraphQLScalarType) => bytes.ToBase64(), + _ when fieldType == typeof(GraphQLScalarType) => value.ToString(), _ => value }; }), Type = column.DataTypeHandle switch { - _ when columnDataType == typeof(object) => typeof(GraphQLStringType), + _ when columnDataType == typeof(object) => typeof(GraphQLScalarType), _ when column.Nullable => columnDataType.ToGraphQLType(false), _ => columnDataType.ToGraphQLType(false).ToNonNullGraphType() } @@ -221,14 +220,14 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour var arguments = new QueryArguments(); arguments.Add>>>("parameters", null, "Used to reference user input values from the where clause."); if (dataSource.Type is DataSourceType.SqlServer) - arguments.Add(nameof(SelectQuery.Top)); + arguments.Add>(nameof(SelectQuery.Top)); - arguments.Add(nameof(SelectQuery.Distinct), false); - arguments.Add(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); + arguments.Add>(nameof(SelectQuery.Distinct), false); + arguments.Add>(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); arguments.Add(nameof(SelectQuery.OrderBy), new ListGraphType(new NonNullGraphType(graphOrderByEnum)), Array.Empty); - arguments.Add>(nameof(SelectQuery.Fetch), 0U); - arguments.Add>(nameof(SelectQuery.Offset), 0U); - arguments.Add(nameof(SqlCommand.Timeout), null); + arguments.Add>(nameof(SelectQuery.Fetch), 0U); + arguments.Add>(nameof(SelectQuery.Offset), 0U); + arguments.Add>(nameof(SqlCommand.Timeout), null); var field = new FieldType { @@ -282,7 +281,7 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour Name = "parameters", Description = "Used to reference user input values from the where clause." }, - new QueryArgument + new QueryArgument> { Name = "where", Description = "If `where` is omitted, all records will be deleted!" @@ -307,14 +306,14 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour var arguments = new QueryArguments(); arguments.Add>>("parameters", null, "Used to reference user input values from the where clause."); if (dataSource.Type is DataSourceType.SqlServer) - arguments.Add(nameof(SelectQuery.Top)); + arguments.Add>(nameof(SelectQuery.Top)); - arguments.Add(nameof(SelectQuery.Distinct), false); - arguments.Add(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); + arguments.Add>(nameof(SelectQuery.Distinct), false); + arguments.Add>(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); arguments.Add(nameof(SelectQuery.OrderBy), new ListGraphType(new NonNullGraphType(graphOrderByEnum)), Array.Empty); - arguments.Add>(nameof(SelectQuery.Fetch), 0U); - arguments.Add>(nameof(SelectQuery.Offset), 0U); - arguments.Add(nameof(SqlCommand.Timeout), null); + arguments.Add>(nameof(SelectQuery.Fetch), 0U); + arguments.Add>(nameof(SelectQuery.Offset), 0U); + arguments.Add>(nameof(SqlCommand.Timeout), null); var fieldType = new FieldType { @@ -332,7 +331,7 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour if (actions.HasFlag(SqlApiAction.InsertData)) { var arguments = new QueryArguments(); - arguments.Add>>>("columns", null, "The columns to insert data into."); + arguments.Add>>>>("columns", null, "The columns to insert data into."); arguments.Add("data", new NonNullGraphType(new ListGraphType(new NonNullGraphType(dataInputType))), null, "The data to be inserted."); var fieldType = new FieldType @@ -352,8 +351,8 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour { var arguments = new QueryArguments(); arguments.Add>>>("parameters", null, "Used to reference user input values from the where clause."); - arguments.Add>>>("set", null, "SET [Column1] = 111, [Column2] = N'111', [Column3] = GETDATE()"); - arguments.Add("where", null, "If `where` is omitted, all records will be updated."); + arguments.Add>>>>("set", null, "SET [Column1] = 111, [Column2] = N'111', [Column3] = GETDATE()"); + arguments.Add>("where", null, "If `where` is omitted, all records will be updated."); var fieldType = new FieldType { @@ -371,7 +370,7 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour if (actions.HasFlag(SqlApiAction.UpdateData)) { var arguments = new QueryArguments(); - arguments.Add>>>("columns", null, "The columns to be updated."); + arguments.Add>>>>("columns", null, "The columns to be updated."); arguments.Add("data", new NonNullGraphType(new ListGraphType(new NonNullGraphType(dataInputType))), null, "The data to be inserted."); var fieldType = new FieldType @@ -845,7 +844,7 @@ public static FieldType AddSqlApiCallProcedureEndpoint(this ISchema @this, ID .ToArray(); var fieldType = new FieldType { - Arguments = new QueryArguments(parameters.Select(parameter => new QueryArgument(typeof(GraphQLStringType)) { Name = parameter })), + Arguments = new QueryArguments(parameters.Select(parameter => new QueryArgument(typeof(GraphQLScalarType)) { Name = parameter })), Name = graphQlName?.ToCamelCase() ?? Invariant($"call{objectSchema.ObjectName.ToPascalCase()}"), Description = Invariant($"Calls stored procedure: {name}."), Resolver = new SqlApiCallFieldResolver() @@ -886,7 +885,7 @@ public static FieldType AddSqlApiDeleteDataEndpoint(this ISchema @this, IData var objectSchema = dataSource.ObjectSchemas[name]; var arguments = new QueryArguments(); arguments.Add>>("data", null, "The data to be deleted."); - arguments.Add(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); + arguments.Add>(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -921,8 +920,8 @@ public static FieldType AddSqlApiDeleteEndpoint(this ISchema @this, IDataSour var objectSchema = dataSource.ObjectSchemas[name]; var arguments = new QueryArguments(); arguments.Add>>>("parameters", null, "Used to reference user input values from the where clause."); - arguments.Add("where", null, "If `where` is omitted, all records will be deleted!"); - arguments.Add(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); + arguments.Add>("where", null, "If `where` is omitted, all records will be deleted!"); + arguments.Add>(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -956,9 +955,9 @@ public static FieldType AddSqlApiInsertDataEndpoint(this ISchema @this, IData var name = dataSource.CreateName(table); var objectSchema = dataSource.ObjectSchemas[name]; var arguments = new QueryArguments(); - arguments.Add>>>("columns", null, "The columns to insert data into."); + arguments.Add>>>>("columns", null, "The columns to insert data into."); arguments.Add>>>>("data", null, "The data to be inserted."); - arguments.Add(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); + arguments.Add>(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -1007,15 +1006,15 @@ public static FieldType AddSqlApiInsertEndpoint(this ISchema @this, IDataSour var arguments = new QueryArguments(); arguments.Add>>>("parameters", null, "Used to reference user input values from the where clause."); if (dataSource.Type is DataSourceType.SqlServer) - arguments.Add(nameof(SelectQuery.Top)); + arguments.Add>(nameof(SelectQuery.Top)); - arguments.Add(nameof(SelectQuery.Distinct), false); - arguments.Add>(nameof(SelectQuery.From), null, "The table or view to pull the data from to insert."); - arguments.Add(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); + arguments.Add>(nameof(SelectQuery.Distinct), false); + arguments.Add>>(nameof(SelectQuery.From), null, "The table or view to pull the data from to insert."); + arguments.Add>(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); arguments.Add(nameof(SelectQuery.OrderBy), new ListGraphType(new NonNullGraphType(graphOrderByEnum)), Array.Empty); - arguments.Add>(nameof(SelectQuery.Fetch), 0U); - arguments.Add>(nameof(SelectQuery.Offset), 0U); - arguments.Add(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); + arguments.Add>(nameof(SelectQuery.Fetch), 0U); + arguments.Add>(nameof(SelectQuery.Offset), 0U); + arguments.Add>(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -1064,14 +1063,14 @@ public static FieldType AddSqlApiSelectEndpoint(this ISchema @this, IDataSour var arguments = new QueryArguments(); arguments.Add>>>("parameters", null, "Used to reference user input values from the where clause."); if (dataSource.Type is DataSourceType.SqlServer) - arguments.Add(nameof(SelectQuery.Top)); + arguments.Add>(nameof(SelectQuery.Top)); - arguments.Add(nameof(SelectQuery.Distinct), false); - arguments.Add(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); + arguments.Add>(nameof(SelectQuery.Distinct), false); + arguments.Add>(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); arguments.Add(nameof(SelectQuery.OrderBy), new ListGraphType(new NonNullGraphType(graphOrderByEnum)), Array.Empty); - arguments.Add>(nameof(SelectQuery.Fetch), 0U); - arguments.Add>(nameof(SelectQuery.Offset), 0U); - arguments.Add(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); + arguments.Add>(nameof(SelectQuery.Fetch), 0U); + arguments.Add>(nameof(SelectQuery.Offset), 0U); + arguments.Add>(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -1106,7 +1105,7 @@ public static FieldType AddSqlApiUpdateDataEndpoint(this ISchema @this, IData var objectSchema = dataSource.ObjectSchemas[name]; var arguments = new QueryArguments(); arguments.Add>>>>("set", null, "The columns to be updated."); - arguments.Add(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); + arguments.Add>(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -1141,9 +1140,9 @@ public static FieldType AddSqlApiUpdateEndpoint(this ISchema @this, IDataSour var objectSchema = dataSource.ObjectSchemas[name]; var arguments = new QueryArguments(); arguments.Add>>>("parameters", null, "Used to reference user input values from the where clause."); - arguments.Add>>>("set", null, "SET [Column1] = 111, [Column2] = N'111', [Column3] = GETDATE()"); - arguments.Add("where", null, "If `where` is omitted, all records will be updated."); - arguments.Add(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); + arguments.Add>>>>("set", null, "SET [Column1] = 111, [Column2] = N'111', [Column3] = GETDATE()"); + arguments.Add>("where", null, "If `where` is omitted, all records will be updated."); + arguments.Add>(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); var fieldType = new FieldType { diff --git a/src/TypeCache.GraphQL/Extensions/ServiceCollectionExtensions.cs b/src/TypeCache.GraphQL/Extensions/ServiceCollectionExtensions.cs index 9d47888b..7005830b 100644 --- a/src/TypeCache.GraphQL/Extensions/ServiceCollectionExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/ServiceCollectionExtensions.cs @@ -19,20 +19,17 @@ public static class ServiceCollectionExtensions /// /// /// Registers the following: + /// Singleton JsonConverter<> + /// Singleton /// Singleton /// Singleton /// Singleton /// Singleton - /// Singleton /// Singleton /// Singleton + /// Singleton /// Transient - /// Singleton /// Transient - /// Singleton - /// Singleton - /// Singleton - /// Singleton JsonConverter<> /// /// /// @@ -51,15 +48,12 @@ public static IServiceCollection AddGraphQL(this IServiceCollection @this) return @this.AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton(typeof(GraphQLScalarType<>)) .AddSingleton(typeof(GraphQLEnumType<>)) .AddSingleton() + .AddSingleton() .AddTransient(typeof(GraphQLInputType<>)) - .AddSingleton(typeof(GraphQLNumberType<>)) - .AddTransient(typeof(GraphQLObjectType<>)) - .AddSingleton() - .AddSingleton(typeof(GraphQLStringType<>)) - .AddSingleton(); + .AddTransient(typeof(GraphQLObjectType<>)); } /// diff --git a/src/TypeCache.GraphQL/Extensions/TypeScriptExtensions.cs b/src/TypeCache.GraphQL/Extensions/TypeScriptExtensions.cs index 04814df2..57d44330 100644 --- a/src/TypeCache.GraphQL/Extensions/TypeScriptExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/TypeScriptExtensions.cs @@ -18,16 +18,16 @@ public static string GetTypeScriptType(this IGraphType @this, bool list = false) { NonNullGraphType graphType => graphType.ResolvedType!.GetTypeScriptType(list), ListGraphType graphType => graphType.ResolvedType!.GetTypeScriptType(true), - GraphQLBooleanType when list => "boolean[]", - GraphQLBooleanType => "boolean", - GraphQLNumberType or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType - or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType when list => "number[]", - GraphQLNumberType or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType - or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType => "number", - GraphQLNumberType or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType when list => "bigint[]", - GraphQLNumberType or GraphQLNumberType or GraphQLNumberType or GraphQLNumberType => "bigint", - GraphQLStringType or IdGraphType or GuidGraphType or GraphQLStringType or GraphQLStringType or GraphQLStringType or GraphQLUriType when list => "string[]", - GraphQLStringType or IdGraphType or GuidGraphType or GraphQLStringType or GraphQLStringType or GraphQLStringType or GraphQLUriType => "string", + GraphQLScalarType when list => "boolean[]", + GraphQLScalarType => "boolean", + GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType + or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType when list => "number[]", + GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType + or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType => "number", + GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType when list => "bigint[]", + GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType => "bigint", + GraphQLScalarType or IdGraphType or GuidGraphType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLUriType when list => "string[]", + GraphQLScalarType or IdGraphType or GuidGraphType or GraphQLScalarType or GraphQLScalarType or GraphQLScalarType or GraphQLUriType => "string", _ when list => Invariant($"{@this.Name}[]"), _ => @this.Name }; @@ -52,7 +52,7 @@ public static string ToTypeScript(this IComplexGraphType @this) var builder = new StringBuilder(); if (@this.Description.IsNotBlank()) builder.AppendLine(Invariant($"/* {@this.Description} */")); - builder.AppendLine($"export type {@this.Name} = {{"); + builder.AppendLine(Invariant($"export type {@this.Name} = {{")); @this.Fields.ToArray().ForEach(field => builder.AppendLine(Invariant($"\t{field.Name}: {field.ResolvedType!.GetTypeScriptType()};"))); return builder.Append('}').AppendLine().ToString(); } diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiCallFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiCallFieldResolver.cs index 5ac132f4..908b96e2 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiCallFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiCallFieldResolver.cs @@ -22,7 +22,7 @@ public sealed class SqlApiCallFieldResolver : FieldResolver context.GetArgument("parameters")?.ForEach(parameter => sqlCommand.Parameters[parameter.Name] = parameter.Value); - var result = (IList)await mediator.MapAsync(sqlCommand.ToSqlModelsRequest(), context.CancellationToken); + var result = (IList)await mediator.Map(sqlCommand.ToSqlModelsRequest(), context.CancellationToken); return new OutputResponse() { TotalCount = sqlCommand.RecordsAffected > 0 ? sqlCommand.RecordsAffected : result.Count, diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiDeleteFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiDeleteFieldResolver.cs index 357000c8..cad9b34f 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiDeleteFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiDeleteFieldResolver.cs @@ -42,9 +42,9 @@ public sealed class SqlApiDeleteFieldResolver : FieldResolver var result = Array.Empty; if (output.Any()) - result = (await mediator.MapAsync(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken)).Select(); + result = (await mediator.Map(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken)).Select(); else - await mediator.ExecuteAsync(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); + await mediator.Execute(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); return new OutputResponse() { @@ -82,9 +82,9 @@ public sealed class SqlApiDeleteFieldResolver : FieldResolver var result = (IList)Array.Empty; if (output.Any()) - result = await mediator.MapAsync(sqlCommand.ToSqlModelsRequest(data.Length), context.CancellationToken); + result = await mediator.Map(sqlCommand.ToSqlModelsRequest(data.Length), context.CancellationToken); else - await mediator.ExecuteAsync(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); + await mediator.Execute(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); return new OutputResponse() { diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiInsertFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiInsertFieldResolver.cs index 77002dc3..ac763433 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiInsertFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiInsertFieldResolver.cs @@ -59,9 +59,9 @@ public sealed class SqlApiInsertFieldResolver : FieldResolver var result = Array.Empty; if (output.Any()) - result = (await mediator.MapAsync(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken)).Select(); + result = (await mediator.Map(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken)).Select(); else - await mediator.ExecuteAsync(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); + await mediator.Execute(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); return new OutputResponse() { @@ -116,9 +116,9 @@ public sealed class SqlApiInsertFieldResolver : FieldResolver var result = (IList)Array.Empty; if (output.Any()) - result = await mediator.MapAsync(sqlCommand.ToSqlModelsRequest(data.Length), context.CancellationToken); + result = await mediator.Map(sqlCommand.ToSqlModelsRequest(data.Length), context.CancellationToken); else - await mediator.ExecuteAsync(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); + await mediator.Execute(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); return new OutputResponse() { diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs index 740fa37f..0a3ec393 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs @@ -48,13 +48,13 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver if (selections.Any(_ => _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.Items)}.")) || _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.Edges)}.{nameof(Edge.Node)}.")))) { - var result = await mediator.MapAsync(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken); + var result = await mediator.Map(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken); var totalCount = result.Rows.Count; if (select.Fetch == totalCount) { var countSql = objectSchema.CreateCountSQL(null, select.Where); var countCommand = objectSchema.DataSource.CreateSqlCommand(countSql); - totalCount = (int?)await mediator.MapAsync(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; + totalCount = (int?)await mediator.Map(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; } var rows = result.Select(); @@ -70,7 +70,7 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver { var countSql = objectSchema.CreateCountSQL(null, select.Where); var countCommand = objectSchema.DataSource.CreateSqlCommand(countSql); - var totalCount = (int?)await mediator.MapAsync(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; + var totalCount = (int?)await mediator.Map(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; return new SelectResponse() { Data = new() @@ -124,13 +124,13 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver if (selections.Any(_ => _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.Items)}.")) || _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.Edges)}.{nameof(Edge.Node)}.")))) { - var result = await mediator.MapAsync(sqlCommand.ToSqlModelsRequest((int)select.Fetch), context.CancellationToken); + var result = await mediator.Map(sqlCommand.ToSqlModelsRequest((int)select.Fetch), context.CancellationToken); var totalCount = result.Count; if (select.Fetch == totalCount) { var countSql = objectSchema.CreateCountSQL(null, select.Where); var countCommand = objectSchema.DataSource.CreateSqlCommand(countSql); - totalCount = (int?)await mediator.MapAsync(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; + totalCount = (int?)await mediator.Map(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; } var items = result.OfType().ToArray(); @@ -146,7 +146,7 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver { var countSql = objectSchema.CreateCountSQL(null, select.Where); var countCommand = objectSchema.DataSource.CreateSqlCommand(countSql); - var totalCount = (int?)await mediator.MapAsync(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; + var totalCount = (int?)await mediator.Map(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; return new SelectResponse() { Data = new() diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiUpdateFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiUpdateFieldResolver.cs index dff39f83..d9465eb2 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiUpdateFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiUpdateFieldResolver.cs @@ -58,9 +58,9 @@ public sealed class SqlApiUpdateFieldResolver : FieldResolver var result = Array.Empty; if (output.Any()) - result = (await mediator.MapAsync(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken)).Select(); + result = (await mediator.Map(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken)).Select(); else - await mediator.ExecuteAsync(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); + await mediator.Execute(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); return new OutputResponse() { @@ -106,9 +106,9 @@ public sealed class SqlApiUpdateFieldResolver : FieldResolver var result = (IList)Array.Empty; if (output.Any()) - result = await mediator.MapAsync(sqlCommand.ToSqlModelsRequest(data.Length), context.CancellationToken); + result = await mediator.Map(sqlCommand.ToSqlModelsRequest(data.Length), context.CancellationToken); else - await mediator.ExecuteAsync(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); + await mediator.Execute(sqlCommand.ToSqlExecuteRequest(), context.CancellationToken); return new OutputResponse() { diff --git a/src/TypeCache.GraphQL/SqlApi/DataResponse.cs b/src/TypeCache.GraphQL/SqlApi/DataResponse.cs index 3ad990d7..453fbad8 100644 --- a/src/TypeCache.GraphQL/SqlApi/DataResponse.cs +++ b/src/TypeCache.GraphQL/SqlApi/DataResponse.cs @@ -8,6 +8,7 @@ using GraphQL.Types.Relay.DataObjects; using TypeCache.Extensions; using TypeCache.GraphQL.Attributes; +using TypeCache.GraphQL.Extensions; using TypeCache.GraphQL.Types; using static System.FormattableString; @@ -65,7 +66,7 @@ public static ObjectGraphType CreateGraphType(string name, string description, I graphType.AddField(new() { Name = nameof(DataResponse.DataSource), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver(context => context.Source.DataSource) }); graphType.AddField(new() @@ -89,19 +90,19 @@ public static ObjectGraphType CreateGraphType(string name, string description, I graphType.AddField(new() { Name = nameof(DataResponse.Sql), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver(context => context.Source.Sql) }); graphType.AddField(new() { Name = nameof(DataResponse.Table), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver(context => context.Source.Table) }); graphType.AddField(new() { Name = nameof(DataResponse.TotalCount), - Type = typeof(GraphQLNumberType), + Type = ScalarType.Int32.ToGraphType(), Resolver = new FuncFieldResolver(context => context.Source.TotalCount) }); @@ -118,7 +119,7 @@ public static ObjectGraphType CreateEdgeGraphType(string name, string descriptio graphType.AddField(new() { Name = nameof(Edge.Cursor), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver, string>(context => context.Source.Cursor) }); graphType.AddField(new() diff --git a/src/TypeCache.GraphQL/SqlApi/OutputResponse.cs b/src/TypeCache.GraphQL/SqlApi/OutputResponse.cs index 68cff670..1754707b 100644 --- a/src/TypeCache.GraphQL/SqlApi/OutputResponse.cs +++ b/src/TypeCache.GraphQL/SqlApi/OutputResponse.cs @@ -33,7 +33,7 @@ public static ObjectGraphType CreateGraphType(string name, string description, I graphType.AddField(new() { Name = nameof(OutputResponse.DataSource), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver, string?>(context => context.Source.DataSource) }); graphType.AddField(new() @@ -45,13 +45,13 @@ public static ObjectGraphType CreateGraphType(string name, string description, I graphType.AddField(new() { Name = nameof(OutputResponse.Sql), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver, string?>(context => context.Source.Sql) }); graphType.AddField(new() { Name = nameof(OutputResponse.Table), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver, string?>(context => context.Source.Table) }); graphType.AddField(new() diff --git a/src/TypeCache.GraphQL/SqlApi/Parameter.cs b/src/TypeCache.GraphQL/SqlApi/Parameter.cs index b099027f..c705d675 100644 --- a/src/TypeCache.GraphQL/SqlApi/Parameter.cs +++ b/src/TypeCache.GraphQL/SqlApi/Parameter.cs @@ -8,9 +8,9 @@ namespace TypeCache.GraphQL.SqlApi; public class Parameter { - [GraphQLType>()] + [GraphQLType>>()] public string Name { get; set; } = string.Empty; - [GraphQLType>()] + [GraphQLType>>()] public string Value { get; set; } = string.Empty; } diff --git a/src/TypeCache.GraphQL/SqlApi/SelectResponse.cs b/src/TypeCache.GraphQL/SqlApi/SelectResponse.cs index 929974f8..a9f4de18 100644 --- a/src/TypeCache.GraphQL/SqlApi/SelectResponse.cs +++ b/src/TypeCache.GraphQL/SqlApi/SelectResponse.cs @@ -52,19 +52,19 @@ public static ObjectGraphType CreateGraphType(string name, string description, I graphType.AddField(new() { Name = nameof(SelectResponse.DataSource), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver, string>(context => context.Source.DataSource) }); graphType.AddField(new() { Name = nameof(SelectResponse.Sql), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver, string>(context => context.Source.Sql) }); graphType.AddField(new() { Name = nameof(SelectResponse.Table), - Type = typeof(GraphQLStringType), + Type = typeof(GraphQLScalarType), Resolver = new FuncFieldResolver, string>(context => context.Source.Table) }); diff --git a/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj b/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj index 3b4de7c8..e67039b7 100644 --- a/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj +++ b/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj @@ -4,7 +4,7 @@ enable TypeCache.GraphQL TypeCache.GraphQL - 8.0.1 + 8.1.0 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache GraphQL diff --git a/src/TypeCache.GraphQL/Types/ConfigureSchema.cs b/src/TypeCache.GraphQL/Types/ConfigureSchema.cs index d3585057..1e08e3c8 100644 --- a/src/TypeCache.GraphQL/Types/ConfigureSchema.cs +++ b/src/TypeCache.GraphQL/Types/ConfigureSchema.cs @@ -3,21 +3,11 @@ using System; using GraphQL.DI; using GraphQL.Types; -using TypeCache.Extensions; namespace TypeCache.GraphQL.Types; -internal class ConfigureSchema : IConfigureSchema +internal sealed class ConfigureSchema(Action configure) : IConfigureSchema { - private readonly Action _Configure; - - public ConfigureSchema(Action configure) - { - configure.AssertNotNull(); - - this._Configure = configure; - } - public void Configure(ISchema schema, IServiceProvider serviceProvider) - => this._Configure(schema, serviceProvider); + => configure?.Invoke(schema, serviceProvider); } diff --git a/src/TypeCache.GraphQL/Types/GraphQLBooleanType.cs b/src/TypeCache.GraphQL/Types/GraphQLBooleanType.cs deleted file mode 100644 index ca6782fb..00000000 --- a/src/TypeCache.GraphQL/Types/GraphQLBooleanType.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -using System; -using GraphQLParser.AST; -using TypeCache.Utilities; - -namespace TypeCache.GraphQL.Types; - -public sealed class GraphQLBooleanType : GraphQLScalarType -{ - public GraphQLBooleanType() : base( - nameof(Boolean), - value => bool.TryParse(value.Value.Span, out _), - value => bool.Parse(value.Value.Span), - value => ValueConverter.ConvertToBoolean(value)) - { - } -} diff --git a/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs b/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs index e7141553..5ca4c387 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs @@ -22,17 +22,17 @@ public GraphQLEnumType() this.Description = typeof(T).GraphQLDescription(); this.DeprecationReason = typeof(T).GraphQLDeprecationReason(); - var changeEnumCase = Enum.Attributes switch + Func? changeEnumCase = Enum.Attributes switch { _ when Enum.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, _ when Enum.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, _ when Enum.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, - _ => new Func(_ => _) + _ => null }; Enum.Values .Where(_ => !_.Attributes().Any()) - .Select(_ => new EnumValueDefinition(_.Attributes().FirstOrDefault()?.Name ?? changeEnumCase(_.Name()), _) + .Select(_ => new EnumValueDefinition(_.Attributes().FirstOrDefault()?.Name ?? changeEnumCase?.Invoke(_.Name()) ?? _.Name(), _) { Description = _.Attributes().FirstOrDefault()?.Description, DeprecationReason = _.Attributes().FirstOrDefault()?.DeprecationReason @@ -45,16 +45,20 @@ _ when Enum.Attributes.TryFirst(out var attribute) => at public override bool CanParseValue(object? value) => value switch { - null or T => true, - Enum token => token is T, + null => true, + T token => Enum.IsDefined(token), + string text => Enum.TryParse(text, true, out _), _ => false }; /// public override object? ParseValue(object? value) - => value is not null ? Enum.ToObject(typeof(T), value) : null; - - /// - public override object? Serialize(object? value) - => this.ParseValue(value); + => value switch + { + null => null, + T token when Enum.IsDefined(token) => token, + T token => null, + string text => Enum.Parse(text, true), + _ => Enum.ToObject(typeof(T), value) + }; } diff --git a/src/TypeCache.GraphQL/Types/GraphQLNumberType.cs b/src/TypeCache.GraphQL/Types/GraphQLNumberType.cs deleted file mode 100644 index d5cd2653..00000000 --- a/src/TypeCache.GraphQL/Types/GraphQLNumberType.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -using System; -using System.Globalization; -using GraphQLParser.AST; -using TypeCache.Extensions; -using TypeCache.Utilities; - -namespace TypeCache.GraphQL.Types; - -public sealed class GraphQLNumberType : GraphQLScalarType - where T : ISpanParsable -{ - public GraphQLNumberType() : base( - typeof(T).Name, - value => T.TryParse(value.Value.Span, CultureInfo.InvariantCulture, out _), - value => T.Parse(value.Value.Span, CultureInfo.InvariantCulture), - value => typeof(T).GetScalarType() switch - { - ScalarType.SByte => ValueConverter.ConvertToSByte(value), - ScalarType.Int16 => ValueConverter.ConvertToInt16(value), - ScalarType.Int32 => ValueConverter.ConvertToInt32(value), - ScalarType.Int64 => ValueConverter.ConvertToInt64(value), - ScalarType.Int128 => ValueConverter.ConvertToInt128(value), - ScalarType.BigInteger => ValueConverter.ConvertToBigInteger(value), - ScalarType.IntPtr => ValueConverter.ConvertToIntPtr(value), - ScalarType.Byte => ValueConverter.ConvertToByte(value), - ScalarType.UInt16 => ValueConverter.ConvertToUInt16(value), - ScalarType.UInt32 => ValueConverter.ConvertToUInt32(value), - ScalarType.UInt64 => ValueConverter.ConvertToUInt64(value), - ScalarType.UInt128 => ValueConverter.ConvertToUInt128(value), - ScalarType.UIntPtr => ValueConverter.ConvertToUIntPtr(value), - ScalarType.Half => ValueConverter.ConvertToHalf(value), - ScalarType.Single => ValueConverter.ConvertToSingle(value), - ScalarType.Double => ValueConverter.ConvertToDouble(value), - ScalarType.Decimal => ValueConverter.ConvertToDecimal(value), - _ => null - }) - { - } -} diff --git a/src/TypeCache.GraphQL/Types/GraphQLScalarType.cs b/src/TypeCache.GraphQL/Types/GraphQLScalarType.cs index 4c6dd848..4c1a2a21 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLScalarType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLScalarType.cs @@ -1,83 +1,88 @@ // Copyright (c) 2021 Samuel Abraham using System; +using System.Globalization; using System.Numerics; using GraphQL.Types; using GraphQLParser.AST; using TypeCache.Extensions; +using TypeCache.Utilities; using static System.FormattableString; namespace TypeCache.GraphQL.Types; -public abstract class GraphQLScalarType : ScalarGraphType - where T : GraphQLValue +public sealed class GraphQLScalarType : ScalarGraphType + where T : ISpanParsable { - private static readonly GraphQLFalseBooleanValue _GraphQLFalseBooleanValue = new GraphQLFalseBooleanValue(); - private static readonly GraphQLNullValue _GraphQLNullValue = new GraphQLNullValue(); - private static readonly GraphQLTrueBooleanValue _GraphQLTrueBooleanValue = new GraphQLTrueBooleanValue(); - - private readonly Func _CanParseLiteral; - private readonly Func _ParseLiteral; - private readonly Func _ParseValue; - - public GraphQLScalarType(string typeName, Func canParseLiteral, Func parseLiteral, Func parseValue) + public GraphQLScalarType() { - canParseLiteral.AssertNotNull(); - parseLiteral.AssertNotNull(); - parseValue.AssertNotNull(); + this.Name = Invariant($"{typeof(T).Namespace}_{typeof(T).Name}"); + this.Description = Invariant($"{typeof(T).Namespace}.{typeof(T).Name}"); + } - this.Name = Invariant($"GraphQL{typeName}Type"); - this._CanParseLiteral = value => value switch + public override bool CanParseLiteral(GraphQLValue value) + => value switch { GraphQLNullValue => true, - T graphQLValue => canParseLiteral(graphQLValue), + IHasValueNode node => T.TryParse(node.Value.Span, CultureInfo.InvariantCulture, out var _), + _ => false + }; + + public override bool CanParseValue(object? value) + => value switch + { + null or T => true, + string text => T.TryParse(text, CultureInfo.InvariantCulture, out _), _ => false }; - this._ParseLiteral = value => value switch + + public override object? ParseLiteral(GraphQLValue value) + => value switch { GraphQLNullValue => null, - T graphQLValue => parseLiteral(graphQLValue), + IHasValueNode node => T.Parse(node.Value.Span, CultureInfo.InvariantCulture), _ => this.ThrowLiteralConversionError(value) }; - this._ParseValue = parseValue; - } - - public override bool CanParseLiteral(GraphQLValue value) => this._CanParseLiteral(value); - public override object? ParseLiteral(GraphQLValue value) => this._ParseLiteral(value); - - public override object? ParseValue(object? value) => this._ParseValue(value); + public override object? ParseValue(object? value) + => value switch + { + null or T => value, + string text => T.Parse(text, CultureInfo.InvariantCulture), + _ => this.ThrowValueConversionError(value) + }; - public override GraphQLValue ToAST(object? value) => this._ParseValue(value) switch - { - null => _GraphQLNullValue, - true => _GraphQLTrueBooleanValue, - false => _GraphQLFalseBooleanValue, - Enum x => new GraphQLEnumValue(new GraphQLName(x.ToString("F"))), - sbyte x => new GraphQLIntValue(x), - short x => new GraphQLIntValue(x), - int x => new GraphQLIntValue(x), - long x => new GraphQLIntValue(x), - Int128 x => new GraphQLIntValue(x), - BigInteger x => new GraphQLIntValue(x), - byte x => new GraphQLIntValue(x), - ushort x => new GraphQLIntValue(x), - uint x => new GraphQLIntValue(x), - ulong x => new GraphQLIntValue(x), - UInt128 x => new GraphQLIntValue(x), - IntPtr x => new GraphQLIntValue(x), - UIntPtr x => new GraphQLIntValue(x), - Half x => new GraphQLFloatValue((decimal)x), - float x => new GraphQLFloatValue(x), - double x => new GraphQLFloatValue(x), - decimal x => new GraphQLFloatValue(x), - DateOnly x => new GraphQLStringValue(x.ToISO8601()), - DateTime x => new GraphQLStringValue(x.ToISO8601()), - DateTimeOffset x => new GraphQLStringValue(x.ToISO8601()), - TimeOnly x => new GraphQLStringValue(x.ToISO8601()), - TimeSpan x => new GraphQLStringValue(x.ToText()), - char x => new GraphQLStringValue(x.ToString()), - string x => new GraphQLStringValue(x), - _ => ThrowASTConversionError(value) - }; + public override GraphQLValue ToAST(object? value) + => this.ParseValue(value) switch + { + null => Singleton.Instance, + true => Singleton.Instance, + false => Singleton.Instance, + sbyte x => new GraphQLIntValue(x), + short x => new GraphQLIntValue(x), + int x => new GraphQLIntValue(x), + long x => new GraphQLIntValue(x), + Int128 x => new GraphQLIntValue(x), + BigInteger x => new GraphQLIntValue(x), + byte x => new GraphQLIntValue(x), + ushort x => new GraphQLIntValue(x), + uint x => new GraphQLIntValue(x), + ulong x => new GraphQLIntValue(x), + UInt128 x => new GraphQLIntValue(x), + IntPtr x => new GraphQLIntValue(x), + UIntPtr x => new GraphQLIntValue(x), + Half x => new GraphQLFloatValue((float)x), + float x => new GraphQLFloatValue(x), + double x => new GraphQLFloatValue(x), + decimal x => new GraphQLFloatValue(x), + DateOnly x => new GraphQLStringValue(x.ToISO8601()), + DateTime x => new GraphQLStringValue(x.ToISO8601()), + DateTimeOffset x => new GraphQLStringValue(x.ToISO8601()), + TimeOnly x => new GraphQLStringValue(x.ToISO8601()), + TimeSpan x => new GraphQLStringValue(x.ToText()), + char x => new GraphQLStringValue(x.ToString()), + Guid x => new GraphQLStringValue(x.ToText()), + string x => new GraphQLStringValue(x), + _ => this.ThrowASTConversionError(value) + }; } diff --git a/src/TypeCache.GraphQL/Types/GraphQLStringType.cs b/src/TypeCache.GraphQL/Types/GraphQLStringType.cs deleted file mode 100644 index 5f4ef076..00000000 --- a/src/TypeCache.GraphQL/Types/GraphQLStringType.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -using System; -using System.Globalization; -using GraphQLParser.AST; -using TypeCache.Extensions; -using TypeCache.Utilities; - -namespace TypeCache.GraphQL.Types; - -public sealed class GraphQLStringType : GraphQLScalarType -{ - public GraphQLStringType() : base( - nameof(String), - value => true, - value => value.Value.Span.ToString(), - value => value?.ToString()) - { - } -} - -public sealed class GraphQLStringType : GraphQLScalarType - where T : ISpanParsable -{ - public GraphQLStringType() : base( - typeof(T).Name, - value => T.TryParse(value.Value.Span, CultureInfo.InvariantCulture, out _), - value => T.Parse(value.Value.Span, CultureInfo.InvariantCulture), - value => typeof(T).GetScalarType() switch - { - ScalarType.Char => ValueConverter.ConvertToChar(value), - ScalarType.DateOnly => ValueConverter.ConvertToDateOnly(value), - ScalarType.DateTime => ValueConverter.ConvertToDateTime(value), - ScalarType.DateTimeOffset => ValueConverter.ConvertToDateTimeOffset(value), - ScalarType.Guid => ValueConverter.ConvertToGuid(value), - ScalarType.TimeOnly => ValueConverter.ConvertToTimeOnly(value), - ScalarType.TimeSpan => ValueConverter.ConvertToTimeSpan(value), - ScalarType.Uri => ValueConverter.ConvertToUri(value), - _ => null - }) - { - } -} diff --git a/src/TypeCache.GraphQL/Types/GraphQLUriType.cs b/src/TypeCache.GraphQL/Types/GraphQLUriType.cs index 0aa5b5ae..a8f2aa24 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLUriType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLUriType.cs @@ -1,17 +1,59 @@ // Copyright (c) 2021 Samuel Abraham using System; +using GraphQL.Types; using GraphQLParser.AST; +using TypeCache.Utilities; +using static System.FormattableString; namespace TypeCache.GraphQL.Types; -public sealed class GraphQLUriType : GraphQLScalarType +public sealed class GraphQLUriType : ScalarGraphType { - public GraphQLUriType() : base( - nameof(Uri), - value => Uri.TryCreate(value.Value.Span.ToString(), value.Value.Span[0] is '/' ? UriKind.Relative : UriKind.Absolute, out _), - value => new Uri(value.Value.Span.ToString(), value.Value.Span[0] is '/' ? UriKind.Relative : UriKind.Absolute), - value => value is not null ? new Uri(value.ToString()!, value.ToString()![0] is '/' ? UriKind.Relative : UriKind.Absolute) : null) + public GraphQLUriType() { + this.Name = nameof(Uri); + this.Description = Invariant($"GraphQL Type for: `{this.Name}`."); } + + public override bool CanParseLiteral(GraphQLValue value) + => value switch + { + GraphQLNullValue => true, + GraphQLStringValue graphQLValue => Uri.TryCreate(graphQLValue.Value.Span.ToString(), graphQLValue.Value.Span[0] is '/' ? UriKind.Relative : UriKind.Absolute, out _), + _ => false + }; + + public override bool CanParseValue(object? value) + => value switch + { + null or Uri => true, + string text => Uri.TryCreate(text, text[0] is '/' ? UriKind.Relative : UriKind.Absolute, out _), + _ => false + }; + + public override object? ParseLiteral(GraphQLValue value) + => value switch + { + GraphQLNullValue => null, + GraphQLStringValue graphQLValue => new Uri(graphQLValue.Value.Span.ToString(), graphQLValue.Value.Span[0] is '/' ? UriKind.Relative : UriKind.Absolute), + _ => this.ThrowLiteralConversionError(value) + }; + + public override object? ParseValue(object? value) + => value switch + { + null or Uri => value, + string text => new Uri(text, text[0] is '/' ? UriKind.Relative : UriKind.Absolute), + _ => this.ThrowValueConversionError(value) + }; + + public override GraphQLValue ToAST(object? value) + => this.ParseValue(value) switch + { + null => Singleton.Instance, + char x => new GraphQLStringValue(x.ToString()), + string x => new GraphQLStringValue(x), + _ => this.ThrowASTConversionError(value) + }; } diff --git a/src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs b/src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs index 34badbb6..36551b70 100644 --- a/src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs +++ b/src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs @@ -11,6 +11,7 @@ using GraphQL.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using TypeCache.GraphQL.Types; using static System.FormattableString; using static System.Net.Mime.MediaTypeNames; using static System.StringSplitOptions; diff --git a/src/TypeCache.Web/Attributes/RequireClaimAttribute.cs b/src/TypeCache.Web/Attributes/RequireClaimAttribute.cs index 69d63c1f..10ad8203 100644 --- a/src/TypeCache.Web/Attributes/RequireClaimAttribute.cs +++ b/src/TypeCache.Web/Attributes/RequireClaimAttribute.cs @@ -27,7 +27,7 @@ public RequireClaimAttribute(params string[] claims) : base(nameof(ClaimAuthoriz this.Claims = new Dictionary(claims.Length, StringComparer.OrdinalIgnoreCase); claims?.ForEach(claim => { - (claim.StartsWith(separator) || claim.EndsWith(separator) || claim.Count(c => c.Equals(separator)) > 1).AssertEquals(false); + (claim.StartsWith(separator) || claim.EndsWith(separator) || claim.Count(c => c.Equals(separator)) > 1).AssertFalse(); if (claim.Contains(separator)) { diff --git a/src/TypeCache.Web/Filters/SqlApiEndpointFilter.cs b/src/TypeCache.Web/Filters/SqlApiEndpointFilter.cs index 7c3350b8..5bbb4aed 100644 --- a/src/TypeCache.Web/Filters/SqlApiEndpointFilter.cs +++ b/src/TypeCache.Web/Filters/SqlApiEndpointFilter.cs @@ -4,14 +4,15 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using TypeCache.Data; using TypeCache.Mediation; -using TypeCache.Utilities; using static System.FormattableString; namespace TypeCache.Web.Filters; -public sealed class SqlApiEndpointFilter : IEndpointFilter +public sealed class SqlApiEndpointFilter(IServiceProvider serviceProvider, IMediator mediator) + : IEndpointFilter { private const string DATASOURCE = "dataSource"; private const string DATABASE = "database"; @@ -20,20 +21,13 @@ public sealed class SqlApiEndpointFilter : IEndpointFilter private const string VIEW = "view"; private const string PROCEDURE = "procedure"; - private readonly IAccessor _DataSourceAccessor; - private readonly IMediator _Mediator; - - public SqlApiEndpointFilter(IAccessor dataSourceAccessor, IMediator mediator) - { - this._DataSourceAccessor = dataSourceAccessor; - this._Mediator = mediator; - } - public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) { var routeValues = context.HttpContext.Request.RouteValues; var dataSourceName = (string)routeValues[DATASOURCE]!; - var dataSource = this._DataSourceAccessor[dataSourceName]; + + await using var serviceScope = serviceProvider.CreateAsyncScope(); + var dataSource = serviceScope.ServiceProvider.GetKeyedService(dataSourceName); if (dataSource is null) return Results.NotFound(Invariant($"{nameof(IDataSource)} [{dataSourceName}] was not found.")); @@ -81,7 +75,7 @@ public SqlApiEndpointFilter(IAccessor dataSourceAccessor, IMediator if (objectSchema is not null) context.HttpContext.Items.Add(nameof(ObjectSchema), objectSchema); - context.HttpContext.Items.Add(nameof(IMediator), this._Mediator); + context.HttpContext.Items.Add(nameof(IMediator), mediator); return await next(context); } diff --git a/src/TypeCache.Web/Handlers/SqlApiHandler.cs b/src/TypeCache.Web/Handlers/SqlApiHandler.cs index 4e9d6f0f..18fedb28 100644 --- a/src/TypeCache.Web/Handlers/SqlApiHandler.cs +++ b/src/TypeCache.Web/Handlers/SqlApiHandler.cs @@ -47,7 +47,7 @@ HttpContext httpContext var mediator = httpContext.GetMediator(); var request = new SqlDataSetRequest { Command = sqlCommand }; - var response = await mediator.MapAsync(request, httpContext.RequestAborted); + var response = await mediator.Map(request, httpContext.RequestAborted); foreach (var parameter in objectSchema.Parameters.Where(parameter => parameter.Direction is ParameterDirection.InputOutput || parameter.Direction is ParameterDirection.Output)) @@ -79,13 +79,13 @@ HttpContext httpContext if (output.IsNotBlank()) { var request = new SqlJsonArrayRequest { Command = sqlCommand }; - var response = await mediator.MapAsync(request, httpContext.RequestAborted); + var response = await mediator.Map(request, httpContext.RequestAborted); return Results.Ok(response); } else { var request = new SqlExecuteRequest { Command = sqlCommand }; - await mediator.ExecuteAsync(request, httpContext.RequestAborted); + await mediator.Execute(request, httpContext.RequestAborted); return Results.Ok(); } } @@ -107,13 +107,13 @@ HttpContext httpContext if (output.IsNotBlank()) { var request = new SqlJsonArrayRequest { Command = sqlCommand }; - var response = await mediator.MapAsync(request, httpContext.RequestAborted); + var response = await mediator.Map(request, httpContext.RequestAborted); return Results.Ok(response); } else { var request = new SqlExecuteRequest { Command = sqlCommand }; - await mediator.ExecuteAsync(request, httpContext.RequestAborted); + await mediator.Execute(request, httpContext.RequestAborted); return Results.Ok(); } } @@ -139,13 +139,13 @@ HttpContext httpContext if (output.IsNotBlank()) { var request = new SqlJsonArrayRequest { Command = sqlCommand }; - var response = await mediator.MapAsync(request, httpContext.RequestAborted); + var response = await mediator.Map(request, httpContext.RequestAborted); return Results.Ok(response); } else { var request = new SqlExecuteRequest { Command = sqlCommand }; - await mediator.ExecuteAsync(request, httpContext.RequestAborted); + await mediator.Execute(request, httpContext.RequestAborted); return Results.Ok(); } } @@ -167,13 +167,13 @@ HttpContext httpContext if (output.IsNotBlank()) { var request = new SqlJsonArrayRequest { Command = sqlCommand }; - var response = await mediator.MapAsync(request, httpContext.RequestAborted); + var response = await mediator.Map(request, httpContext.RequestAborted); return Results.Ok(response); } else { var request = new SqlExecuteRequest { Command = sqlCommand }; - await mediator.ExecuteAsync(request, httpContext.RequestAborted); + await mediator.Execute(request, httpContext.RequestAborted); return Results.Ok(); } } @@ -404,7 +404,7 @@ HttpContext httpContext var mediator = httpContext.GetMediator(); var request = new SqlJsonArrayRequest { Command = sqlCommand }; - var response = await mediator.MapAsync(request, httpContext.RequestAborted); + var response = await mediator.Map(request, httpContext.RequestAborted); return Results.Ok(response); } @@ -429,13 +429,13 @@ HttpContext httpContext if (output.IsNotBlank()) { var request = new SqlJsonArrayRequest { Command = sqlCommand }; - var response = await mediator.MapAsync(request, httpContext.RequestAborted); + var response = await mediator.Map(request, httpContext.RequestAborted); return Results.Ok(response); } else { var request = new SqlExecuteRequest { Command = sqlCommand }; - await mediator.ExecuteAsync(request, httpContext.RequestAborted); + await mediator.Execute(request, httpContext.RequestAborted); return Results.Ok(); } } @@ -457,13 +457,13 @@ HttpContext httpContext if (output.IsNotBlank()) { var request = new SqlJsonArrayRequest { Command = sqlCommand }; - var response = await mediator.MapAsync(request, httpContext.RequestAborted); + var response = await mediator.Map(request, httpContext.RequestAborted); return Results.Ok(response); } else { var request = new SqlExecuteRequest { Command = sqlCommand }; - await mediator.ExecuteAsync(request, httpContext.RequestAborted); + await mediator.Execute(request, httpContext.RequestAborted); return Results.Ok(); } } diff --git a/src/TypeCache.Web/Middleware/SqlApiErrorHandlerMiddleware.cs b/src/TypeCache.Web/Middleware/SqlApiErrorHandlerMiddleware.cs index 7c496d79..a4dd9611 100644 --- a/src/TypeCache.Web/Middleware/SqlApiErrorHandlerMiddleware.cs +++ b/src/TypeCache.Web/Middleware/SqlApiErrorHandlerMiddleware.cs @@ -4,7 +4,6 @@ using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using TypeCache.Extensions; using TypeCache.Mediation; using static System.Net.Mime.MediaTypeNames; diff --git a/src/TypeCache.Web/Middleware/WebMiddleware.cs b/src/TypeCache.Web/Middleware/WebMiddleware.cs index ff9282ab..e16a8ac1 100644 --- a/src/TypeCache.Web/Middleware/WebMiddleware.cs +++ b/src/TypeCache.Web/Middleware/WebMiddleware.cs @@ -5,20 +5,19 @@ using Microsoft.AspNetCore.Http; using TypeCache.Mediation; -namespace TypeCache.Web.Middleware +namespace TypeCache.Web.Middleware; + +public abstract class WebMiddleware : IMiddleware { - public abstract class WebMiddleware : IMiddleware + protected WebMiddleware(IMediator mediator, JsonSerializerOptions? jsonSerializerOptions = null) { - protected WebMiddleware(IMediator mediator, JsonSerializerOptions? jsonSerializerOptions = null) - { - this.JsonSerializerOptions = jsonSerializerOptions; - this.Mediator = mediator; - } + this.JsonSerializerOptions = jsonSerializerOptions; + this.Mediator = mediator; + } - protected JsonSerializerOptions? JsonSerializerOptions { get; } + protected JsonSerializerOptions? JsonSerializerOptions { get; } - protected IMediator Mediator { get; } + protected IMediator Mediator { get; } - public abstract Task InvokeAsync(HttpContext context, RequestDelegate next); - } + public abstract Task InvokeAsync(HttpContext context, RequestDelegate next); } diff --git a/src/TypeCache.Web/TypeCache.Web.csproj b/src/TypeCache.Web/TypeCache.Web.csproj index 4558fa94..bbde3011 100644 --- a/src/TypeCache.Web/TypeCache.Web.csproj +++ b/src/TypeCache.Web/TypeCache.Web.csproj @@ -5,7 +5,7 @@ enable TypeCache.Web TypeCache.Web - 8.0.0 + 8.1.0 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache Web Library diff --git a/src/TypeCache/Data/DataSource.cs b/src/TypeCache/Data/DataSource.cs index ea2c07b7..c517d92f 100644 --- a/src/TypeCache/Data/DataSource.cs +++ b/src/TypeCache/Data/DataSource.cs @@ -13,23 +13,59 @@ namespace TypeCache.Data; internal sealed class DataSource : IDataSource { - public DataSource(string name, DbProviderFactory dbProviderFactory, string connectionString, DataSourceType type) + public DataSource(string name, DbProviderFactory dbProviderFactory, string connectionString) { this.Factory = dbProviderFactory; this.Name = name; this.ConnectionString = connectionString; - this.Type = type; using var connection = this.CreateDbConnection(); connection.Open(); + this.DefaultDatabase = connection.Database; - this.DefaultSchema = type switch + this.Server = connection.DataSource; + this.Version = connection.ServerVersion; + + var @namespace = dbProviderFactory.GetType().Namespace; + this.Type = @namespace switch { - SqlServer => "dbo", - PostgreSql => "public", - _ => string.Empty + _ when @namespace.Is("Microsoft.Data.SqlClient") || @namespace.Is("System.Data.SqlClient") => DataSourceType.SqlServer, + _ when @namespace.Is("Oracle.DataAccess.Client") => DataSourceType.Oracle, + _ when @namespace.Is("Npgsql") => DataSourceType.PostgreSql, + _ when @namespace.Is("MySql.Data.MySqlClient") => DataSourceType.MySql, + _ => DataSourceType.Unknown }; + if (this.Type == DataSourceType.PostgreSql) + this.DefaultSchema = "public"; + else if (this.Type == DataSourceType.MySql) + this.DefaultSchema = this.DefaultDatabase; + else if (this.Type == DataSourceType.Unknown) + this.DefaultSchema = string.Empty; + else + { + var sql = this.Type switch + { + DataSourceType.SqlServer => "SELECT SCHEMA_NAME();", + DataSourceType.Oracle => "SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual", + _ => string.Empty + }; + + using var command = connection.CreateCommand(); + command.Connection = connection; + command.CommandType = CommandType.Text; + command.CommandText = sql; + + this.DefaultSchema = command.ExecuteScalar()?.ToString() ?? string.Empty; + } + + var metadata = connection.GetSchema(SchemaCollection.MetaDataCollections.Name()); + this.SupportedMetadataCollections = metadata.Rows + .Cast() + .Select(row => row[SchemaColumn.collectionName]!.ToString()!.ToEnum()!.Value) + .WhereNotNull() + .ToFrozenSet(); + this.Databases = this.GetDatabases().ToFrozenSet(StringComparer.OrdinalIgnoreCase); this.ObjectSchemas = this.GetObjectSchemas(); } @@ -49,10 +85,16 @@ public ObjectSchema? this[string objectName] public string Name { get; } - public DataSourceType Type { get; } - public IReadOnlyDictionary ObjectSchemas { get; } + public string Server { get; } + + public IReadOnlySet SupportedMetadataCollections { get; } + + public string Version { get; } + + public DataSourceType Type { get; } + [MethodImpl(AggressiveInlining), DebuggerHidden] public DbConnection CreateDbConnection() => this.Factory.CreateConnection(this.ConnectionString); @@ -61,47 +103,52 @@ public DatabaseObject CreateName(string databaseObject) { databaseObject.AssertNotNull(); - var items = databaseObject.Split('.', RemoveEmptyEntries); - if (items.Length > 3) - throw new ArgumentException(Invariant($"{nameof(DataSource)}.{nameof(CreateName)}: Invalid name: {databaseObject}"), nameof(databaseObject)); + var items = databaseObject.Split('.', RemoveEmptyEntries | TrimEntries); + if (items.Length > 3 || (this.Type == MySql && items.Length > 2)) + throw new ArgumentOutOfRangeException(Invariant($"{nameof(DataSource)}.{nameof(CreateName)}: Invalid name: {databaseObject}"), nameof(databaseObject)); items = items.Select(item => this.Type switch { - PostgreSql => item.Trim('"'), - _ => item.TrimStart('[').TrimEnd(']') + SqlServer when item[0] == '[' => item.TrimStart('[').TrimEnd(']'), + MySql => item.Trim('`').Replace("``", "`"), + _ => item.Trim('"').Replace("\"\"", "\"") }).ToArray(); - return this.Type switch + return items.Length switch { - SqlServer or PostgreSql => items.Length switch - { - 1 => this.CreateName(this.DefaultSchema, items[0]), - 2 when databaseObject.Contains("..") => this.CreateName(items[0], this.DefaultSchema, items[1]), - 2 => this.CreateName(items[0], items[1]), - _ => this.CreateName(items[0], items[1], items[2]) - }, - _ => new(databaseObject) + 1 => this.CreateName(this.DefaultSchema, items[0]), + 2 when databaseObject.Contains("..") => this.CreateName(items[0], this.DefaultSchema, items[1]), + 2 => this.CreateName(items[0], items[1]), + _ => this.CreateName(items[0], items[1], items[2]) }; } - [MethodImpl(AggressiveInlining), DebuggerHidden] public DatabaseObject CreateName(string schema, string objectName) - => new(Invariant($"{this.EscapeIdentifier(this.DefaultDatabase)}.{this.EscapeIdentifier(schema)}.{this.EscapeIdentifier(objectName)}")); + => this.Type switch + { + MySql => new(Invariant($"{this.EscapeIdentifier(schema)}.{this.EscapeIdentifier(objectName)}")), + _ => new(Invariant($"{this.EscapeIdentifier(this.DefaultDatabase)}.{this.EscapeIdentifier(schema)}.{this.EscapeIdentifier(objectName)}")) + }; - [MethodImpl(AggressiveInlining), DebuggerHidden] public DatabaseObject CreateName(string database, string schema, string objectName) - => new(Invariant($"{this.EscapeIdentifier(database)}.{this.EscapeIdentifier(schema)}.{this.EscapeIdentifier(objectName)}")); + { + this.Databases.Contains(database).AssertTrue(); + + return new(Invariant($"{this.EscapeIdentifier(database)}.{this.EscapeIdentifier(schema)}.{this.EscapeIdentifier(objectName)}")); + } [MethodImpl(AggressiveInlining), DebuggerHidden] public SqlCommand CreateSqlCommand(string sql) - => new SqlCommand(this, sql); + => new(this, sql); [DebuggerHidden] public string EscapeIdentifier([NotNull] string identifier) => this.Type switch { - PostgreSql => Invariant($"\"{identifier}\""), - _ => Invariant($"[{identifier.Replace("]", "]]")}]") + SqlServer => Invariant($"[{identifier.Replace("]", "]]")}]"), + Oracle or PostgreSql => Invariant($"\"{identifier.Replace("\"", "\"\"")}\""), + MySql => Invariant($"`{identifier.Replace("`", "``")}`"), + _ => identifier }; [MethodImpl(AggressiveInlining), DebuggerHidden] @@ -112,7 +159,7 @@ public string EscapeLikeValue([NotNull] string text) public string EscapeValue([NotNull] string text) => text.Replace("'", "''"); - public async ValueTask GetDatabaseSchemaAsync(string? database = null, CancellationToken token = default) + public async Task GetDatabaseSchemaAsync(string? database = null, CancellationToken token = default) { var schemaSet = new DataSet(SchemaCollection.MetaDataCollections.Name()); @@ -129,7 +176,7 @@ public async ValueTask GetDatabaseSchemaAsync(string? database = null, return schemaSet; } - public async ValueTask GetDatabaseSchemaAsync(SchemaCollection collection, string? database = null, CancellationToken token = default) + public async Task GetDatabaseSchemaAsync(SchemaCollection collection, string? database = null, CancellationToken token = default) { await using var connection = this.CreateDbConnection(); await connection.OpenAsync(token); @@ -185,16 +232,9 @@ public override string ToString() => this.Name; private string[] GetDatabases() - { - var table = this.GetDatabaseSchema(SchemaCollection.Databases, null); - var rows = this.Type switch - { - SqlServer => table?.Select(Invariant($"{SchemaColumn.database_name} NOT IN ('master', 'tempdb', 'model', 'msdb')")), - _ => table?.Select() - }; - - return rows?.Select(row => row[SchemaColumn.database_name].ToString()!).ToArray() ?? Array.Empty; - } + => this.GetDatabaseSchema(SchemaCollection.Databases, null)? + .Select()? + .Select(row => row[SchemaColumn.database_name].ToString()!).ToArray() ?? Array.Empty; private IReadOnlyDictionary GetObjectSchemas() { @@ -216,86 +256,95 @@ private IReadOnlyDictionary GetObjectSchemas() { connection.ChangeDatabase(databaseName); - var tables = connection.GetSchema(SchemaCollection.Tables.Name()); - var tablesRows = tables.Select(Invariant($"{SchemaColumn.table_type} = 'BASE TABLE'"), Invariant($"{SchemaColumn.table_name} ASC")); - tablesRows.ForEach(tablesRow => + if (this.SupportedMetadataCollections.Contains(SchemaCollection.Tables)) { - var tableName = tablesRow[SchemaColumn.table_name].ToString()!; - var tableSchema = tablesRow[SchemaColumn.table_schema].ToString()!; - var name = this.CreateName(databaseName, tableSchema, tableName); - - command.CommandText = Invariant($"SELECT TOP 1 * FROM {name};"); - var table = new DataTable(); - - try + var tables = connection.GetSchema(SchemaCollection.Tables.Name()); + var tablesRows = tables.Select(Invariant($"{SchemaColumn.table_type} = 'BASE TABLE'"), Invariant($"{SchemaColumn.table_name} ASC")); + tablesRows.ForEach(tablesRow => { - adapter.FillSchema(table, SchemaType.Source); - - var columns = table.Columns - .OfType() - .Select(column => new ColumnSchema( - column.ColumnName, column.AllowDBNull, table.PrimaryKey.Contains(column), column.ReadOnly, column.Unique, column.DataType.TypeHandle)); - - var objectSchema = new ObjectSchema(this, DatabaseObjectType.Table, name, databaseName, tableSchema, tableName, columns); - objectSchemas.Add(name, objectSchema); - } - catch (Exception) { } - }); - - var views = connection.GetSchema(SchemaCollection.Views.Name()); - var viewsRows = views?.Select(null, Invariant($"{SchemaColumn.table_name} ASC")); - viewsRows?.ForEach(viewsRow => + var tableName = tablesRow[SchemaColumn.table_name].ToString()!; + var tableSchema = tablesRow[SchemaColumn.table_schema].ToString()!; + var name = this.CreateName(databaseName, tableSchema, tableName); + + command.CommandText = Invariant($"SELECT * FROM {name} WHERE 0 = 1;"); + var table = new DataTable(); + + try + { + adapter.FillSchema(table, SchemaType.Source); + + var columns = table.Columns + .OfType() + .Select(column => new ColumnSchema( + column.ColumnName, column.AllowDBNull, table.PrimaryKey.Contains(column), column.ReadOnly, column.Unique, column.DataType.TypeHandle)); + + var objectSchema = new ObjectSchema(this, DatabaseObjectType.Table, name, databaseName, tableSchema, tableName, columns); + objectSchemas.Add(name, objectSchema); + } + catch (Exception) { } + }); + } + + if (this.SupportedMetadataCollections.Contains(SchemaCollection.Views)) { - var tableName = viewsRow[SchemaColumn.table_name].ToString()!; - var tableSchema = viewsRow[SchemaColumn.table_schema].ToString()!; - var name = this.CreateName(databaseName, tableSchema, tableName); - - command.CommandText = Invariant($"SELECT * FROM {name} WHERE 0 = 1;"); - var table = new DataTable(); - - try + var views = connection.GetSchema(SchemaCollection.Views.Name()); + var viewsRows = views?.Select(null, Invariant($"{SchemaColumn.table_name} ASC")); + viewsRows?.ForEach(viewsRow => { - adapter.FillSchema(table, SchemaType.Source); - - var columns = table.Columns - .OfType() - .Select(column => new ColumnSchema( - column.ColumnName, column.AllowDBNull, table.PrimaryKey.Contains(column), column.ReadOnly, column.Unique, column.DataType.TypeHandle)); - - var objectSchema = new ObjectSchema(this, DatabaseObjectType.View, name, databaseName, tableSchema, tableName, columns); - objectSchemas.Add(name, objectSchema); - } - catch (Exception) { } - }); - - var procedures = connection.GetSchema(SchemaCollection.Procedures.Name()); - var procedureParameters = connection.GetSchema(SchemaCollection.ProcedureParameters.Name()); - var proceduresRows = procedures?.Select(null, Invariant($"{SchemaColumn.routine_name} ASC")); - proceduresRows?.ForEach(proceduresRow => + var tableName = viewsRow[SchemaColumn.table_name].ToString()!; + var tableSchema = viewsRow[SchemaColumn.table_schema].ToString()!; + var name = this.CreateName(databaseName, tableSchema, tableName); + + command.CommandText = Invariant($"SELECT * FROM {name} WHERE 0 = 1;"); + var table = new DataTable(); + + try + { + adapter.FillSchema(table, SchemaType.Source); + + var columns = table.Columns + .OfType() + .Select(column => new ColumnSchema( + column.ColumnName, column.AllowDBNull, table.PrimaryKey.Contains(column), column.ReadOnly, column.Unique, column.DataType.TypeHandle)); + + var objectSchema = new ObjectSchema(this, DatabaseObjectType.View, name, databaseName, tableSchema, tableName, columns); + objectSchemas.Add(name, objectSchema); + } + catch (Exception) { } + }); + } + + if (this.SupportedMetadataCollections.Contains(SchemaCollection.Procedures)) { - var routineName = proceduresRow[SchemaColumn.routine_name].ToString()!; - var routineSchema = proceduresRow[SchemaColumn.routine_schema].ToString()!; - var routineType = proceduresRow[SchemaColumn.routine_type].ToString()!; - - var procedureParametersRows = procedureParameters?.Select( - Invariant($"{SchemaColumn.specific_schema} = '{routineSchema}' AND {SchemaColumn.specific_name} = '{routineName}'") - , Invariant($"{SchemaColumn.ordinal_position} ASC")); - var parameters = procedureParametersRows?.Select(row => new ParameterSchema(row[SchemaColumn.parameter_name].ToString()!, row[SchemaColumn.parameter_mode].ToString() switch + var procedures = connection.GetSchema(SchemaCollection.Procedures.Name()); + var procedureParameters = connection.GetSchema(SchemaCollection.ProcedureParameters.Name()); + var proceduresRows = procedures?.Select(null, Invariant($"{SchemaColumn.routine_name} ASC")); + proceduresRows?.ForEach(proceduresRow => { - string value when value.Is("OUT") => ParameterDirection.Output, - string value when value.Is("INOUT") => ParameterDirection.InputOutput, - _ => ParameterDirection.Input - })); - - var name = this.CreateName(databaseName, routineSchema, routineName); - var objectType = routineType switch - { - _ when routineType.Is("FUNCTION") => DatabaseObjectType.Function, - _ => DatabaseObjectType.StoredProcedure - }; - var objectSchema = new ObjectSchema(this, objectType, name, databaseName, routineSchema, routineName, parameters ?? Array.Empty); - objectSchemas.Add(name, objectSchema); - }); + var routineName = proceduresRow[SchemaColumn.routine_name].ToString()!; + var routineSchema = proceduresRow[SchemaColumn.routine_schema].ToString()!; + var routineType = proceduresRow[SchemaColumn.routine_type].ToString()!; + + var procedureParametersRows = procedureParameters?.Select( + Invariant($"{SchemaColumn.specific_schema} = '{routineSchema}' AND {SchemaColumn.specific_name} = '{routineName}'") + , Invariant($"{SchemaColumn.ordinal_position} ASC")); + var parameters = procedureParametersRows?.Select(row => new ParameterSchema(row[SchemaColumn.parameter_name].ToString()!, row[SchemaColumn.parameter_mode].ToString() switch + { + string value when value.Is("OUT") => ParameterDirection.Output, + string value when value.Is("INOUT") => ParameterDirection.InputOutput, + _ => ParameterDirection.Input + })); + + var name = this.CreateName(databaseName, routineSchema, routineName); + var objectType = routineType switch + { + _ when routineType.Is("FUNCTION") => DatabaseObjectType.Function, + _ => DatabaseObjectType.StoredProcedure + }; + var objectSchema = new ObjectSchema(this, objectType, name, databaseName, routineSchema, routineName, parameters ?? Array.Empty); + objectSchemas.Add(name, objectSchema); + }); + } }); return objectSchemas.ToFrozenDictionary(); diff --git a/src/TypeCache/Data/DataSourceType.cs b/src/TypeCache/Data/DataSourceType.cs index c3b75e75..625690d1 100644 --- a/src/TypeCache/Data/DataSourceType.cs +++ b/src/TypeCache/Data/DataSourceType.cs @@ -4,6 +4,9 @@ namespace TypeCache.Data; public enum DataSourceType { + Unknown = 0, SqlServer, - PostgreSql + Oracle, + PostgreSql, + MySql } diff --git a/src/TypeCache/Data/Extensions/SqlExtensions.cs b/src/TypeCache/Data/Extensions/SqlExtensions.cs index bf7ede43..882a3d58 100644 --- a/src/TypeCache/Data/Extensions/SqlExtensions.cs +++ b/src/TypeCache/Data/Extensions/SqlExtensions.cs @@ -18,7 +18,7 @@ public static string EscapeValue([NotNull] this string @this) => @this.Replace("'", "''"); /// - public static string ToSQL(this object? @this) => @this switch + public static string ToSQL(this T? @this) => @this switch { null or DBNull => "NULL", true => "1", diff --git a/src/TypeCache/Data/IDataSource.cs b/src/TypeCache/Data/IDataSource.cs index c0aef2a9..592a7e07 100644 --- a/src/TypeCache/Data/IDataSource.cs +++ b/src/TypeCache/Data/IDataSource.cs @@ -2,30 +2,38 @@ using System.Data; using System.Data.Common; -using TypeCache.Utilities; namespace TypeCache.Data; -public interface IDataSource : IName, IEquatable +public interface IDataSource : IEquatable { - ObjectSchema? this[string objectName] { get; } - string ConnectionString { get; } IReadOnlySet Databases { get; } string DefaultDatabase { get; } - string DefaultSchema { get; } + public string DefaultSchema { get; } DbProviderFactory Factory { get; } - DataSourceType Type { get; } + string Name { get; } IReadOnlyDictionary ObjectSchemas { get; } + public string Server { get; } + + IReadOnlySet SupportedMetadataCollections { get; } + + DataSourceType Type { get; } + + public string Version { get; } + + ObjectSchema? this[string objectName] { get; } + DbConnection CreateDbConnection(); + /// DatabaseObject CreateName(string databaseObject); /// @@ -36,6 +44,7 @@ public interface IDataSource : IName, IEquatable /// /// => (($"{.EscapeIdentifier()}.{.EscapeIdentifier()}.{.EscapeIdentifier()}")); /// + /// DatabaseObject CreateName(string database, string schema, string objectName); SqlCommand CreateSqlCommand(string sql); @@ -52,9 +61,9 @@ public interface IDataSource : IName, IEquatable /// string EscapeValue([NotNull] string text); - ValueTask GetDatabaseSchemaAsync(string? database = null, CancellationToken token = default); + Task GetDatabaseSchemaAsync(string? database = null, CancellationToken token = default); - ValueTask GetDatabaseSchemaAsync(SchemaCollection collection, string? database = null, CancellationToken token = default); + Task GetDatabaseSchemaAsync(SchemaCollection collection, string? database = null, CancellationToken token = default); DataSet GetDatabaseSchema(string? database = null); diff --git a/src/TypeCache/Data/Mediation/SqlDataSetRule.cs b/src/TypeCache/Data/Mediation/SqlDataSetRule.cs index f3e7c2c6..6d4c55e5 100644 --- a/src/TypeCache/Data/Mediation/SqlDataSetRule.cs +++ b/src/TypeCache/Data/Mediation/SqlDataSetRule.cs @@ -8,7 +8,7 @@ namespace TypeCache.Data.Mediation; internal sealed class SqlDataSetRule : IRule { - public async Task MapAsync(SqlDataSetRequest request, CancellationToken token) + public async Task Map(SqlDataSetRequest request, CancellationToken token) { await using var connection = request.Command.DataSource.CreateDbConnection(); await connection.OpenAsync(token); diff --git a/src/TypeCache/Data/Mediation/SqlDataTableRule.cs b/src/TypeCache/Data/Mediation/SqlDataTableRule.cs index 27734208..e399bf39 100644 --- a/src/TypeCache/Data/Mediation/SqlDataTableRule.cs +++ b/src/TypeCache/Data/Mediation/SqlDataTableRule.cs @@ -8,7 +8,7 @@ namespace TypeCache.Data.Mediation; internal sealed class SqlDataTableRule : IRule { - public async Task MapAsync(SqlDataTableRequest request, CancellationToken token) + public async Task Map(SqlDataTableRequest request, CancellationToken token) { await using var connection = request.Command.DataSource.CreateDbConnection(); await connection.OpenAsync(token); diff --git a/src/TypeCache/Data/Mediation/SqlExecuteRule.cs b/src/TypeCache/Data/Mediation/SqlExecuteRule.cs index ce6c0c25..77483cbc 100644 --- a/src/TypeCache/Data/Mediation/SqlExecuteRule.cs +++ b/src/TypeCache/Data/Mediation/SqlExecuteRule.cs @@ -7,7 +7,7 @@ namespace TypeCache.Data.Mediation; internal sealed class SqlExecuteRule : IRule { - public async Task ExecuteAsync(SqlExecuteRequest request, CancellationToken token = default) + public async Task Execute(SqlExecuteRequest request, CancellationToken token = default) { await using var connection = request.Command.DataSource.CreateDbConnection(); await connection.OpenAsync(token); diff --git a/src/TypeCache/Data/Mediation/SqlJsonArrayRule.cs b/src/TypeCache/Data/Mediation/SqlJsonArrayRule.cs index 5a28a517..5ff83d83 100644 --- a/src/TypeCache/Data/Mediation/SqlJsonArrayRule.cs +++ b/src/TypeCache/Data/Mediation/SqlJsonArrayRule.cs @@ -8,7 +8,7 @@ namespace TypeCache.Data.Mediation; internal sealed class SqlJsonArrayRule : IRule { - public async Task MapAsync(SqlJsonArrayRequest request, CancellationToken token) + public async Task Map(SqlJsonArrayRequest request, CancellationToken token) { await using var connection = request.Command.DataSource.CreateDbConnection(); await connection.OpenAsync(token); diff --git a/src/TypeCache/Data/Mediation/SqlModelsRule.cs b/src/TypeCache/Data/Mediation/SqlModelsRule.cs index 3ee2e3aa..99618a03 100644 --- a/src/TypeCache/Data/Mediation/SqlModelsRule.cs +++ b/src/TypeCache/Data/Mediation/SqlModelsRule.cs @@ -7,7 +7,7 @@ namespace TypeCache.Data.Mediation; internal sealed class SqlModelsRule : IRule> { - public async Task> MapAsync(SqlModelsRequest request, CancellationToken token) + public async Task> Map(SqlModelsRequest request, CancellationToken token) { await using var connection = request.Command.DataSource.CreateDbConnection(); await connection.OpenAsync(token); diff --git a/src/TypeCache/Data/Mediation/SqlScalarRule.cs b/src/TypeCache/Data/Mediation/SqlScalarRule.cs index 54a9118b..29089d60 100644 --- a/src/TypeCache/Data/Mediation/SqlScalarRule.cs +++ b/src/TypeCache/Data/Mediation/SqlScalarRule.cs @@ -7,7 +7,7 @@ namespace TypeCache.Data.Mediation; internal sealed class SqlScalarRule : IRule { - public async Task MapAsync(SqlScalarRequest request, CancellationToken token) + public async Task Map(SqlScalarRequest request, CancellationToken token) { await using var connection = request.Command.DataSource.CreateDbConnection(); await connection.OpenAsync(token); @@ -17,10 +17,6 @@ internal sealed class SqlScalarRule : IRule dbCommand.CopyOutputParameters(request.Command); - return result switch - { - DBNull => null, - _ => result - }; + return result is not DBNull ? result : null; } } diff --git a/src/TypeCache/Extensions/ArrayExtensions.cs b/src/TypeCache/Extensions/ArrayExtensions.cs index 218abbcf..ebb8aab6 100644 --- a/src/TypeCache/Extensions/ArrayExtensions.cs +++ b/src/TypeCache/Extensions/ArrayExtensions.cs @@ -18,7 +18,7 @@ public static class ArrayExtensions /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static void Clear(this T[] @this, int start = 0, int length = 0) - => Array.Clear(@this, start, length == 0 ? @this.Length : length); + => Array.Clear(@this, start, length is 0 ? @this.Length : length); /// /// @@ -26,7 +26,7 @@ public static T[] Copy(this T[] @this) { @this.AssertNotNull(); - if (@this.Length == 0) + if (@this.Length is 0) return Array.Empty; var copy = new T[@this.Length]; @@ -163,7 +163,7 @@ public static int Search(this T[] @this, T value, IComparer? comparer = nu /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static int Search(this T[] @this, T value, int start, int length = 0, IComparer? comparer = null) - => Array.BinarySearch(@this, start, length > 0 ? length : @this.Length, value, comparer); + => Array.BinarySearch(@this, start, length is 0 ? @this.Length : length, value, comparer); /// /// @@ -189,7 +189,7 @@ public static void Sort(this T[] @this, IComparer? comparer = null) /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static void Sort(this T[] @this, int start, int length = 0, IComparer? comparer = null) - => Array.Sort(@this, start, length > 0 ? length : @this.Length, comparer); + => Array.Sort(@this, start, length is 0 ? @this.Length : length, comparer); /// /// @@ -200,7 +200,7 @@ public static T[] Subarray(this T[] @this, int sourceIndex, int length = 0) if (sourceIndex + length > @this.Length) throw new IndexOutOfRangeException($"{nameof(Subarray)}: last index {sourceIndex + length} is more than array length {@this.Length}."); - var array = new T[length > 0 ? length : @this.Length - sourceIndex]; + var array = new T[length is 0 ? @this.Length - sourceIndex : length]; Array.Copy(@this, sourceIndex, array, 0, array.Length); return array; } diff --git a/src/TypeCache/Extensions/DateTimeExtensions.cs b/src/TypeCache/Extensions/DateTimeExtensions.cs index 69ba44e1..928e2dee 100644 --- a/src/TypeCache/Extensions/DateTimeExtensions.cs +++ b/src/TypeCache/Extensions/DateTimeExtensions.cs @@ -114,7 +114,7 @@ public static DateTimeOffset ToTimeZone(this DateTimeOffset @this, TimeZoneInfo /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static DateTimeOffset ToTimeZone(this DateTimeOffset @this, string targetSystemTimeZoneId) - => TimeZoneInfo.ConvertTimeBySystemTimeZoneId(@this, targetSystemTimeZoneId); + => ConvertTimeBySystemTimeZoneId(@this, targetSystemTimeZoneId); /// /// diff --git a/src/TypeCache/Extensions/EnumExtensions.cs b/src/TypeCache/Extensions/EnumExtensions.cs index a24a18b2..0d6ba794 100644 --- a/src/TypeCache/Extensions/EnumExtensions.cs +++ b/src/TypeCache/Extensions/EnumExtensions.cs @@ -67,118 +67,145 @@ CollectionType.ConcurrentBag or CollectionType.ConcurrentDictionary _ => false }; - public static bool IsConvertibleTo(this ScalarType @this, ScalarType target) => (@this, target) switch - { - _ when target == @this => true, - (ScalarType.String, _) or (_, ScalarType.Boolean or ScalarType.String) => true, - (ScalarType.Boolean, ScalarType.Char or ScalarType.SByte or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 - or ScalarType.Byte or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64) => true, - (ScalarType.Byte, ScalarType.Boolean or ScalarType.Char - or ScalarType.SByte or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger - or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.Char, ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger - or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.DateOnly, ScalarType.DateTime or ScalarType.DateTimeOffset or Extensions.ScalarType.TimeSpan - or ScalarType.Int32 or ScalarType.Int64 or ScalarType.UInt32 or ScalarType.UInt64) => true, - (ScalarType.DateTime, ScalarType.DateOnly or ScalarType.DateTimeOffset or ScalarType.TimeOnly or ScalarType.Int64 or ScalarType.UInt64) => true, - (ScalarType.DateTimeOffset, ScalarType.DateOnly or ScalarType.DateTime or ScalarType.TimeOnly or ScalarType.Int64 or ScalarType.UInt64) => true, - (ScalarType.SByte, ScalarType.Char - or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger - or ScalarType.Byte or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.Int16, ScalarType.Char - or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger + public static bool IsConvertibleTo(this ScalarType @this, ScalarType target) + => (@this, target) switch + { + _ when target == @this => true, + (ScalarType.String, _) or (_, ScalarType.Boolean or ScalarType.String) => true, + (ScalarType.Boolean, ScalarType.Char or ScalarType.SByte or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 + or ScalarType.Byte or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128) => true, + (ScalarType.Byte, ScalarType.Boolean or ScalarType.Char + or ScalarType.SByte or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger + or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 + or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.Char, ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger + or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 + or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.DateOnly, ScalarType.DateTime or ScalarType.DateTimeOffset or ScalarType.TimeSpan + or ScalarType.Int32 or ScalarType.Int64 or ScalarType.UInt32 or ScalarType.UInt64) => true, + (ScalarType.DateTime, ScalarType.DateOnly or ScalarType.DateTimeOffset or ScalarType.TimeOnly or ScalarType.Int64 or ScalarType.UInt64) => true, + (ScalarType.DateTimeOffset, ScalarType.DateOnly or ScalarType.DateTime or ScalarType.TimeOnly or ScalarType.Int64 or ScalarType.UInt64) => true, + (ScalarType.Int16, ScalarType.Char + or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger + or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 + or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.Int32, ScalarType.DateOnly or ScalarType.Index + or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger + or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 + or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.Int64, ScalarType.DateTime or ScalarType.DateTimeOffset or ScalarType.TimeOnly or ScalarType.TimeSpan + or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger or ScalarType.UInt64 or ScalarType.UInt128 + or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.Int128, ScalarType.UInt128 or ScalarType.BigInteger or ScalarType.Decimal) => true, + (ScalarType.IntPtr, ScalarType.Int32 or ScalarType.Int64) => true, + (ScalarType.SByte, ScalarType.Char + or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger + or ScalarType.Byte or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 + or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.TimeOnly, ScalarType.TimeSpan or ScalarType.Int64 or ScalarType.UInt64) => true, + (ScalarType.TimeSpan, ScalarType.TimeOnly or ScalarType.Int64 or ScalarType.UInt64) => true, + (ScalarType.UInt16, ScalarType.Char + or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger + or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 + or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.UInt32, ScalarType.DateOnly or ScalarType.Index + or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger or ScalarType.UInt64 or ScalarType.UInt128 + or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.UInt64, ScalarType.DateTime or ScalarType.DateTimeOffset or ScalarType.TimeOnly or ScalarType.TimeSpan + or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger or ScalarType.UInt128 + or ScalarType.Double or ScalarType.Decimal) => true, + (ScalarType.UInt128, ScalarType.Int128 or ScalarType.BigInteger or ScalarType.Decimal) => true, + (ScalarType.UIntPtr, ScalarType.UInt32 or ScalarType.UInt64) => true, + _ => false + }; + + public static bool IsDictionary(this CollectionType @this) + => @this switch + { + CollectionType.Dictionary or CollectionType.ConcurrentDictionary or CollectionType.FrozenDictionary or CollectionType.SortedDictionary + or CollectionType.ImmutableDictionary or CollectionType.ImmutableSortedDictionary + or CollectionType.Hashtable or CollectionType.HybridDictionary or CollectionType.OrderedDictionary or CollectionType.ReadOnlyDictionary + or CollectionType.KeyedCollection or CollectionType.ListDictionary or CollectionType.StringDictionary + or CollectionType.NameObjectCollection or CollectionType.NameValueCollection or CollectionType.SortedList => true, + _ => false + }; + + public static bool IsEnumUnderlyingType(this ScalarType @this) + => @this switch + { + ScalarType.SByte or ScalarType.Byte + or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 + or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 => true, + _ => false + }; + + public static bool IsFrozen(this CollectionType @this) + => @this switch + { + CollectionType.FrozenDictionary or CollectionType.FrozenSet => true, + _ => false + }; + + public static bool IsImmutable(this CollectionType @this) + => @this switch + { + CollectionType.ImmutableArray + or CollectionType.ImmutableDictionary or CollectionType.ImmutableSortedDictionary + or CollectionType.ImmutableSet or CollectionType.ImmutableSortedSet + or CollectionType.ImmutableList + or CollectionType.ImmutableQueue + or CollectionType.ImmutableStack => true, + _ => false + }; + + /// + /// Returns true for the current .Net primitives.
+ /// In addition, returns true for the following types: + /// + /// + /// + /// + /// + ///
+ public static bool IsPrimitive(this ScalarType @this) + => @this switch + { + ScalarType.Boolean + or ScalarType.SByte or ScalarType.Byte + or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 + or ScalarType.IntPtr or ScalarType.UIntPtr or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.Int32, ScalarType.DateOnly or ScalarType.Index - or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger - or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.Int64, ScalarType.DateTime or ScalarType.DateTimeOffset or ScalarType.TimeOnly or ScalarType.TimeSpan - or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.Int128, ScalarType.UInt128 or ScalarType.BigInteger or ScalarType.Decimal) => true, - (ScalarType.IntPtr, ScalarType.Int32 or ScalarType.Int64) => true, - (ScalarType.TimeOnly, ScalarType.TimeSpan or ScalarType.Int64 or ScalarType.UInt64) => true, - (ScalarType.TimeSpan, ScalarType.TimeOnly or ScalarType.Int64 or ScalarType.UInt64) => true, - (ScalarType.UInt16, ScalarType.Char - or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger - or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.UInt32, ScalarType.DateOnly or ScalarType.Index - or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Single or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.UInt64, ScalarType.DateTime or ScalarType.DateTimeOffset or ScalarType.TimeOnly or ScalarType.TimeSpan - or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger or ScalarType.UInt128 or ScalarType.Double or ScalarType.Decimal) => true, - (ScalarType.UInt128, ScalarType.Int128 or ScalarType.BigInteger or ScalarType.Decimal) => true, - (ScalarType.UIntPtr, ScalarType.UInt32 or ScalarType.UInt64) => true, - _ => false - }; - - public static bool IsDictionary(this CollectionType @this) => @this switch - { - CollectionType.Dictionary or CollectionType.ConcurrentDictionary or CollectionType.SortedDictionary - or CollectionType.ImmutableDictionary or CollectionType.ImmutableSortedDictionary - or CollectionType.Hashtable or CollectionType.HybridDictionary or CollectionType.OrderedDictionary or CollectionType.ReadOnlyDictionary - or CollectionType.KeyedCollection or CollectionType.ListDictionary or CollectionType.StringDictionary - or CollectionType.NameObjectCollection or CollectionType.NameValueCollection or CollectionType.SortedList => true, - _ => false - }; - - public static bool IsEnumUnderlyingType(this ScalarType @this) => @this switch - { - ScalarType.SByte or ScalarType.Byte - or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 - or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 => true, - _ => false - }; - - public static bool IsFrozen(this CollectionType @this) => @this switch - { - CollectionType.FrozenDictionary or CollectionType.FrozenSet => true, - _ => false - }; - - public static bool IsImmutable(this CollectionType @this) => @this switch - { - CollectionType.ImmutableArray - or CollectionType.ImmutableDictionary or CollectionType.ImmutableSortedDictionary - or CollectionType.ImmutableSet or CollectionType.ImmutableSortedSet - or CollectionType.ImmutableList - or CollectionType.ImmutableQueue - or CollectionType.ImmutableStack => true, - _ => false - }; - - public static bool IsPrimitive(this ScalarType @this) => @this switch - { - ScalarType.Boolean - or ScalarType.SByte or ScalarType.Byte - or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 - or ScalarType.IntPtr or ScalarType.UIntPtr - or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.Single or ScalarType.Double - or ScalarType.Char => true, - _ => false - }; - - public static bool IsQueue(this CollectionType @this) => @this switch - { - CollectionType.ConcurrentQueue or CollectionType.ImmutableQueue or CollectionType.PriorityQueue or CollectionType.Queue => true, - _ => false - }; - - public static bool IsReadOnly(this CollectionType @this) => @this switch - { - CollectionType.ReadOnlyCollection or CollectionType.ReadOnlyDictionary or CollectionType.ReadOnlyObservableCollection => true, - _ => false - }; - - public static bool IsStack(this CollectionType @this) => @this switch - { - CollectionType.ConcurrentStack or CollectionType.ImmutableStack or CollectionType.Stack => true, - _ => false - }; + or ScalarType.Single or ScalarType.Double or ScalarType.Decimal + or ScalarType.Char => true, + _ => false + }; + + public static bool IsQueue(this CollectionType @this) + => @this switch + { + CollectionType.ConcurrentQueue or CollectionType.ImmutableQueue or CollectionType.PriorityQueue or CollectionType.Queue => true, + _ => false + }; + + public static bool IsReadOnly(this CollectionType @this) + => @this switch + { + CollectionType.ReadOnlyCollection or CollectionType.ReadOnlyDictionary or CollectionType.ReadOnlyObservableCollection => true, + _ => false + }; + + public static bool IsSet(this CollectionType @this) + => @this switch + { + CollectionType.Set or CollectionType.ReadOnlySet or CollectionType.SortedSet + or CollectionType.FrozenSet or CollectionType.ImmutableSet or CollectionType.ImmutableSortedSet => true, + _ => false + }; + + public static bool IsStack(this CollectionType @this) + => @this switch + { + CollectionType.ConcurrentStack or CollectionType.ImmutableStack or CollectionType.Stack => true, + _ => false + }; } diff --git a/src/TypeCache/Extensions/EnumerableExtensions.cs b/src/TypeCache/Extensions/EnumerableExtensions.cs index 6aee7f06..1c2b161e 100644 --- a/src/TypeCache/Extensions/EnumerableExtensions.cs +++ b/src/TypeCache/Extensions/EnumerableExtensions.cs @@ -1,5 +1,6 @@ // Copyright (c) 2021 Samuel Abraham +using System; using System.Collections; using TypeCache.Collections; @@ -139,11 +140,19 @@ public static IEnumerable> ToTasks(this IEnumerable> @th => @this.Select(_ => _.AsTask()); public static bool TryFirst([NotNullWhen(true)] this IEnumerable @this, [NotNullWhen(true)] out T? value) + where T : class { value = @this.OfType().FirstOrDefault(); return value is not null; } + public static bool TryFirst([NotNullWhen(true)] this IEnumerable @this, T defaultValue, [NotNullWhen(true)] out T value) + where T : struct + { + value = @this.OfType().FirstOrDefault(defaultValue); + return !value.Equals(defaultValue); + } + public static bool TryFirst([NotNullWhen(true)] this IEnumerable @this, [NotNullWhen(true)] out T? value) { using var enumerator = @this.GetEnumerator(); @@ -153,18 +162,49 @@ public static bool TryFirst([NotNullWhen(true)] this IEnumerable @this, [N } public static bool TryFirst([NotNullWhen(true)] this IEnumerable @this, Func predicate, [NotNullWhen(true)] out T? value) + where T : class { - var success = false; - var filter = new Func(value => success = predicate(value)); - value = @this.FirstOrDefault(filter); - return success; + value = @this.FirstOrDefault(predicate); + return value is not null; } + public static bool TryFirst([NotNullWhen(true)] this IEnumerable @this, T defaultValue, Func predicate, [NotNullWhen(true)] out T value) + where T : struct + { + value = @this.FirstOrDefault(predicate, defaultValue); + return !value.Equals(defaultValue); + } + + /// public static bool TrySingle([NotNullWhen(true)] this IEnumerable @this, [NotNullWhen(true)] out T? value) + where T : class { - using var enumerator = @this.GetEnumerator(); - value = enumerator.Next(); - return enumerator.MoveNext(); + value = @this.SingleOrDefault(); + return value is not null; + } + + /// + public static bool TrySingle([NotNullWhen(true)] this IEnumerable @this, T defaultValue, [NotNullWhen(true)] out T? value) + where T : struct + { + value = @this.SingleOrDefault(defaultValue); + return !value.Equals(defaultValue); + } + + /// + public static bool TrySingle([NotNullWhen(true)] this IEnumerable @this, Func predicate, [NotNullWhen(true)] out T? value) + where T : class + { + value = @this.SingleOrDefault(predicate); + return value is not null; + } + + /// + public static bool TrySingle([NotNullWhen(true)] this IEnumerable @this, T defaultValue, Func predicate, [NotNullWhen(true)] out T value) + where T : struct + { + value = @this.SingleOrDefault(predicate, defaultValue); + return !value.Equals(defaultValue); } /// diff --git a/src/TypeCache/Extensions/EnumeratorExtensions.cs b/src/TypeCache/Extensions/EnumeratorExtensions.cs index 11e1e806..836d4f32 100644 --- a/src/TypeCache/Extensions/EnumeratorExtensions.cs +++ b/src/TypeCache/Extensions/EnumeratorExtensions.cs @@ -18,7 +18,7 @@ public static bool IfNext(this IEnumerator @this, [NotNullWhen(true)] out object { if (@this.MoveNext()) { - item = @this.Current!; + item = @this.Current; return true; } diff --git a/src/TypeCache/Extensions/ExpressionExtensions.cs b/src/TypeCache/Extensions/ExpressionExtensions.cs index fbae5615..4cf64c4a 100644 --- a/src/TypeCache/Extensions/ExpressionExtensions.cs +++ b/src/TypeCache/Extensions/ExpressionExtensions.cs @@ -238,7 +238,6 @@ public static Expression Convert(this Expression @this, Type targetType) ScalarType.DateOnly => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToDateOnly), @this), ScalarType.DateTime => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToDateTime), @this), ScalarType.DateTimeOffset => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToDateTimeOffset), @this), - ScalarType.DBNull => DBNull.Value.ToConstantExpression(), ScalarType.Decimal => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToDecimal), @this), ScalarType.Double => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToDouble), @this), ScalarType.Enum => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToEnum), @this), diff --git a/src/TypeCache/Extensions/JsonExtensions.cs b/src/TypeCache/Extensions/JsonExtensions.cs index 4da99fa6..9dab12e9 100644 --- a/src/TypeCache/Extensions/JsonExtensions.cs +++ b/src/TypeCache/Extensions/JsonExtensions.cs @@ -2,6 +2,7 @@ using System.Text.Json; using System.Text.Json.Nodes; +using static System.FormattableString; namespace TypeCache.Extensions; @@ -14,7 +15,7 @@ public static JsonElement[] GetArrayElements(this JsonElement @this) return @this.EnumerateArrayValues().ToArray(); } - public static object?[]? GetArrayValues(this JsonElement @this) + public static object?[] GetArrayValues(this JsonElement @this) { @this.ValueKind.AssertEquals(JsonValueKind.Array); @@ -50,7 +51,7 @@ public static IDictionary GetObjectElements(this JsonElemen public static object? GetValue(this JsonElement @this) => @this.ValueKind switch { - JsonValueKind.Undefined => throw new UnreachableException(), + JsonValueKind.Undefined => throw new UnreachableException(Invariant($"{nameof(@this.ValueKind)} == {JsonValueKind.Undefined:F}")), JsonValueKind.True => true, JsonValueKind.False => false, JsonValueKind.Number when @this.TryGetInt32(out var value) => value, diff --git a/src/TypeCache/Extensions/LoggerExtensions.cs b/src/TypeCache/Extensions/LoggerExtensions.cs index 94c52260..dc878a78 100644 --- a/src/TypeCache/Extensions/LoggerExtensions.cs +++ b/src/TypeCache/Extensions/LoggerExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) 2021 Samuel Abraham using Microsoft.Extensions.Logging; -using TypeCache.Utilities; namespace TypeCache.Extensions; @@ -15,14 +14,12 @@ public static void LogAggregateException(this ILogger @this, EventId event @this.AssertNotNull(); error.AssertNotNull(); - @this.LogError(eventId, error, message, args); - if (error.InnerExceptions.Count > 1) - error.InnerExceptions.AsArray().ForEach((exception, i) => @this.LogError(eventId, exception, "Aggregate InnerException #{position}: {message}", i + 1, exception.Message)); + error.InnerExceptions.AsArray().ForEach(exception => @this.LogError(eventId, exception, message, args)); else if (error.InnerException is not null) - @this.LogError(eventId, error.InnerException, "Aggregate InnerException: {message}", error.InnerException.Message); + @this.LogError(eventId, error.InnerException, message, args); else - @this.LogError(eventId, error, "Aggregate Exception: {message}", error.Message); + @this.LogError(eventId, error, message, args); } /// @@ -33,13 +30,11 @@ public static void LogAggregateException(this ILogger @this, AggregateExce @this.AssertNotNull(); error.AssertNotNull(); - @this.LogError(error, message, args); - if (error.InnerExceptions.Count > 1) - error.InnerExceptions.AsArray().ForEach((exception, i) => @this.LogError(exception, "Aggregate InnerException #{position}: {message}", i + 1, exception.Message)); + error.InnerExceptions.AsArray().ForEach(exception => @this.LogError(exception, message, args)); else if (error.InnerException is not null) - @this.LogError(error.InnerException, "Aggregate InnerException: {message}", error.InnerException.Message); + @this.LogError(error.InnerException, message, args); else - @this.LogError(error, "Aggregate Exception: {message}", error.Message); + @this.LogError(error, message, args); } } diff --git a/src/TypeCache/Extensions/ReadOnlySpanExtensions.cs b/src/TypeCache/Extensions/ReadOnlySpanExtensions.cs index 58ae2123..f73aa7bc 100644 --- a/src/TypeCache/Extensions/ReadOnlySpanExtensions.cs +++ b/src/TypeCache/Extensions/ReadOnlySpanExtensions.cs @@ -3,7 +3,6 @@ using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; -using System.Text; using TypeCache.Collections; using static System.Globalization.CultureInfo; diff --git a/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs index d110eb3e..c2740de9 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs @@ -7,7 +7,7 @@ namespace TypeCache.Extensions; partial class ReflectionExtensions { - public static Expression> ToInvokeLambdaExpression(this ConstructorInfo @this) + public static Expression> ToFuncExpression(this ConstructorInfo @this) { ParameterExpression arguments = nameof(arguments).ToParameterExpression(); var parameters = @this.GetParameters() @@ -15,7 +15,7 @@ partial class ReflectionExtensions .Select((parameterInfo, i) => arguments.Array()[i].Convert(parameterInfo.ParameterType)) .ToArray(); - return @this.ToNewExpression(parameters).As().Lambda>(arguments); + return @this.ToExpression(parameters).As().Lambda>(arguments); } public static LambdaExpression ToLambdaExpression(this ConstructorInfo @this) @@ -25,7 +25,7 @@ public static LambdaExpression ToLambdaExpression(this ConstructorInfo @this) .Select(parameterInfo => parameterInfo!.ToExpression()) .ToArray(); - return @this.ToNewExpression(parameters).Lambda(parameters); + return @this.ToExpression(parameters).Lambda(parameters); } /// @@ -33,7 +33,7 @@ public static LambdaExpression ToLambdaExpression(this ConstructorInfo @this) /// => .New(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static NewExpression ToNewExpression(this ConstructorInfo @this, IEnumerable parameters) + public static NewExpression ToExpression(this ConstructorInfo @this, IEnumerable parameters) => Expression.New(@this, parameters); /// @@ -41,7 +41,7 @@ public static NewExpression ToNewExpression(this ConstructorInfo @this, IEnumera /// => .New(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static NewExpression ToNewExpression(this ConstructorInfo @this, params Expression[]? parameters) + public static NewExpression ToExpression(this ConstructorInfo @this, params Expression[]? parameters) => Expression.New(@this, parameters); /// @@ -49,7 +49,7 @@ public static NewExpression ToNewExpression(this ConstructorInfo @this, params E /// => .New(@, , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static NewExpression ToNewExpression(this ConstructorInfo @this, IEnumerable parameters, IEnumerable memberInfos) + public static NewExpression ToExpression(this ConstructorInfo @this, IEnumerable parameters, IEnumerable memberInfos) => Expression.New(@this, parameters, memberInfos); /// @@ -57,6 +57,6 @@ public static NewExpression ToNewExpression(this ConstructorInfo @this, IEnumera /// => .New(@, , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static NewExpression ToNewExpression(this ConstructorInfo @this, IEnumerable parameters, params MemberInfo[]? memberInfos) + public static NewExpression ToExpression(this ConstructorInfo @this, IEnumerable parameters, params MemberInfo[]? memberInfos) => Expression.New(@this, parameters, memberInfos); } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.FieldInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.FieldInfo.cs index 7a485cf8..784f5dd8 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.FieldInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.FieldInfo.cs @@ -18,9 +18,9 @@ partial class ReflectionExtensions public static object? GetFieldValue(this FieldInfo @this, object? instance) => @this.IsLiteral ? @this.GetRawConstantValue() - : TypeStore.FieldGetInvokes.GetOrAdd(@this.FieldHandle, handle => @this.GetFieldValueFuncLambdaExpression().Compile())(instance); + : TypeStore.FieldGetFuncs.GetOrAdd(@this.FieldHandle, handle => @this.GetFieldValueFuncExpression().Compile())(instance); - public static Expression> GetFieldValueFuncLambdaExpression(this FieldInfo @this) + public static Expression> GetFieldValueFuncExpression(this FieldInfo @this) { ParameterExpression instance = nameof(instance).ToParameterExpression(); var field = !@this.IsStatic ? instance.Cast(@this.DeclaringType!).Field(@this) : @this.ToStaticFieldExpression(); @@ -41,10 +41,10 @@ public static LambdaExpression GetFieldValueLambdaExpression(this FieldInfo @thi public static void SetFieldValue(this FieldInfo @this, object? instance, object? value) { if (!@this.IsInitOnly && !@this.IsLiteral) - TypeStore.FieldSetInvokes.GetOrAdd(@this.FieldHandle, handle => @this.SetFieldValueActionLambdaExpression().Compile())(instance, value); + TypeStore.FieldSetActions.GetOrAdd(@this.FieldHandle, handle => @this.SetFieldValueActionExpression().Compile())(instance, value); } - public static Expression> SetFieldValueActionLambdaExpression(this FieldInfo @this) + public static Expression> SetFieldValueActionExpression(this FieldInfo @this) { @this.IsInitOnly.AssertFalse(); @this.IsLiteral.AssertFalse(); diff --git a/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs index 43827287..797fd2b1 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs @@ -24,9 +24,5 @@ public static bool HasCustomAttribute(this MemberInfo @this, Type attributeType, [DebuggerHidden] public static string Name(this MemberInfo @this) - => @this.Name.IndexOf(GENERIC_TICKMARK) switch - { - var index when index > -1 => @this.Name.Left(index), - _ => @this.Name - }; + => @this.Name.Left(@this.Name.IndexOf(GENERIC_TICKMARK)); } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.MethodBase.cs b/src/TypeCache/Extensions/ReflectionExtensions.MethodBase.cs index 28e97473..710e6c7e 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.MethodBase.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.MethodBase.cs @@ -31,7 +31,7 @@ public static bool IsCallableWith(this MethodBase @this, params object?[]? argum public static object? InvokeMethod(this MethodBase @this, params object?[]? arguments) { @this.DeclaringType.AssertNotNull(); - var method = TypeStore.MethodInvokes[(@this.DeclaringType.TypeHandle, @this.MethodHandle)]; + var method = TypeStore.MethodFuncs[(@this.DeclaringType.TypeHandle, @this.MethodHandle)]; return method.Invoke(arguments); } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs index de1467b4..ca6d042b 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs @@ -84,7 +84,7 @@ public static MethodInfo MakeGenericMethod(this MethodInfo @ public static MethodInfo MakeGenericMethod(this MethodInfo @this) => @this.MakeGenericMethod(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)); - public static Expression> ToInvokeLambdaExpression(this MethodInfo @this) + public static Expression> ToFuncExpression(this MethodInfo @this) { ParameterExpression arguments = nameof(arguments).ToParameterExpression(); var parameterInfos = @this.GetParameters().OrderBy(parameterInfo => parameterInfo.Position); diff --git a/src/TypeCache/Extensions/ReflectionExtensions.ScalarType.cs b/src/TypeCache/Extensions/ReflectionExtensions.ScalarType.cs index 37ae06ad..52444720 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.ScalarType.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.ScalarType.cs @@ -34,10 +34,6 @@ public enum ScalarType /// DateTimeOffset, /// - /// - /// - DBNull, - /// /// /// Decimal, diff --git a/src/TypeCache/Extensions/ReflectionExtensions.Type.cs b/src/TypeCache/Extensions/ReflectionExtensions.Type.cs index be4c3389..a1dd004d 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.Type.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.Type.cs @@ -15,7 +15,7 @@ partial class ReflectionExtensions public static object? Create(this Type @this, params object?[]? parameters) => @this.FindConstructor(parameters) switch { - null when @this.IsValueType && parameters?.Any() is not true => TypeStore.DefaultValueTypeConstructorInvokes[@this.TypeHandle].Invoke(), + null when @this.IsValueType && parameters?.Any() is not true => TypeStore.DefaultValueTypeConstructorFuncs[@this.TypeHandle].Invoke(), null => throw new MissingMethodException(@this.Name, "Constructor"), var constructorInfo => constructorInfo.InvokeMethod(parameters) }; @@ -72,18 +72,15 @@ public static ObjectType GetObjectType(this Type @this) => TypeStore.ObjectTypes[@this.IsGenericType ? @this.GetGenericTypeDefinition().TypeHandle : @this.TypeHandle]; public static ScalarType GetScalarType(this Type @this) - { - if (@this.IsGenericTypeDefinition) - return ScalarType.None; - - if (@this.IsEnum) - return ScalarType.Enum; - - if (@this.IsGenericType && @this.IsNullable()) - @this = @this.GenericTypeArguments[0]; - - return TypeStore.DataTypes.TryGetValue(@this.TypeHandle, out var dataType) ? dataType : ScalarType.None; - } + => @this switch + { + { IsGenericTypeDefinition: true } => ScalarType.None, + { IsEnum: true } => ScalarType.Enum, + { IsGenericType: true } when @this.IsNullable() && TypeStore.DataTypes.TryGetValue(@this.GenericTypeArguments[0].TypeHandle, out var scalarType) + => scalarType, + _ when TypeStore.DataTypes.TryGetValue(@this.TypeHandle, out var scalarType) => scalarType, + _ => ScalarType.None + }; /// /// @@ -257,12 +254,14 @@ public static bool Is(this Type @this) => @this == typeof(T); [DebuggerHidden] - public static bool Is(this Type @this, Type type) => (@this, type) switch - { - ({ IsGenericType: true }, { IsGenericTypeDefinition: true }) or ({ IsGenericTypeDefinition: true }, { IsGenericType: true }) => @this.GetGenericTypeDefinition() == type.GetGenericTypeDefinition(), - _ => @this == type - }; - //=> (@this.IsGenericTypeDefinition || type.IsGenericTypeDefinition) ? @this.GetGenericTypeDefinition() == type.GetGenericTypeDefinition() : @this == type; + public static bool Is(this Type @this, Type type) + => (@this, type) switch + { + ({ IsGenericType: true, IsGenericTypeDefinition: false }, { IsGenericTypeDefinition: true }) + or ({ IsGenericTypeDefinition: true }, { IsGenericType: true, IsGenericTypeDefinition: false }) + => @this.GetGenericTypeDefinition() == type.GetGenericTypeDefinition(), + _ => @this == type + }; /// /// => .Any(@.Is); @@ -446,7 +445,7 @@ public static LabelTarget ToLabelTarget(this Type @this, string? name) public static NewExpression ToNewExpression(this Type @this, params Expression[] parameters) => parameters switch { null or { Length: 0 } => Expression.New(@this), - _ => @this.GetConstructor(parameters.Select(parameter => parameter.Type).ToArray())!.ToNewExpression(parameters) + _ => @this.GetConstructor(parameters.Select(parameter => parameter.Type).ToArray())!.ToExpression(parameters) }; /// diff --git a/src/TypeCache/Extensions/ServiceCollectionExtensions.cs b/src/TypeCache/Extensions/ServiceCollectionExtensions.cs index ca877911..f3521149 100644 --- a/src/TypeCache/Extensions/ServiceCollectionExtensions.cs +++ b/src/TypeCache/Extensions/ServiceCollectionExtensions.cs @@ -16,18 +16,10 @@ namespace TypeCache.Extensions; public static class ServiceCollectionExtensions { /// - /// Registers singleton to be available via: IAccessor<>
+ /// Registers keyed singleton .
///
- public static IServiceCollection AddDataSource(this IServiceCollection @this, string name, DbProviderFactory dbProviderFactory, string connectionString, DataSourceType type) - => @this.AddSingleton(new DataSource(name, dbProviderFactory, connectionString, type)); - - /// - /// Provides data source related information: IAccessor<>
- /// Requires call to: - /// - ///
- public static IServiceCollection AddDataSourceAccessor(this IServiceCollection @this) - => @this.AddSingleton, Accessor>(); + public static IServiceCollection AddDataSource(this IServiceCollection @this, string name, DbProviderFactory dbProviderFactory, string connectionString) + => @this.AddKeyedSingleton(name, new DataSource(name, dbProviderFactory, connectionString)); /// /// Registers Singletons: diff --git a/src/TypeCache/Extensions/StringBuilderExtensions.cs b/src/TypeCache/Extensions/StringBuilderExtensions.cs index 7fab0179..40e5f574 100644 --- a/src/TypeCache/Extensions/StringBuilderExtensions.cs +++ b/src/TypeCache/Extensions/StringBuilderExtensions.cs @@ -21,11 +21,20 @@ public static StringBuilder AppendIf(this StringBuilder @this, bool condition, s public static StringBuilder AppendIf(this StringBuilder @this, bool condition, string trueValue, string falseValue) => @this.Append(condition ? trueValue : falseValue); + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static StringBuilder AppendIf(this StringBuilder @this, bool condition, ReadOnlySpan value) + => condition ? @this.Append(value) : @this; + + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static StringBuilder AppendIf(this StringBuilder @this, bool condition, ReadOnlySpan trueValue, ReadOnlySpan falseValue) + => condition ? @this.Append(trueValue) : @this.Append(falseValue); + [DebuggerHidden] public static StringBuilder AppendIf(this StringBuilder @this, bool condition, T value) => value switch { _ when !condition => @this, + ReadOnlyMemory item => @this.Append(item), char item => @this.Append(item), bool item => @this.Append(item), sbyte item => @this.Append(item), @@ -39,8 +48,10 @@ public static StringBuilder AppendIf(this StringBuilder @this, bool condition float item => @this.Append(item), double item => @this.Append(item), decimal item => @this.Append(item), - Enum item => @this.Append(item.ToString()), + Enum item => @this.Append(item.ToString("F")), + char[] item => @this.Append(item), string item => @this.Append(item), + StringBuilder item => @this.Append(item), _ => @this.Append(value) }; @@ -51,13 +62,6 @@ public static StringBuilder AppendIf(this StringBuilder @this, bool condition public static StringBuilder AppendIf(this StringBuilder @this, bool condition, T trueValue, T falseValue) => @this.AppendIf(condition, trueValue).AppendIf(!condition, falseValue); - /// - /// => ? @.Append() : @; - /// - [MethodImpl(AggressiveInlining), DebuggerHidden] - public static StringBuilder AppendIf(this StringBuilder @this, bool condition, object value) - => condition ? @this.Append(value) : @this; - /// /// => ? @.AppendLine() : @; /// diff --git a/src/TypeCache/Mediation/CustomAfterRule.cs b/src/TypeCache/Mediation/CustomAfterRule.cs index dda45d80..01035702 100644 --- a/src/TypeCache/Mediation/CustomAfterRule.cs +++ b/src/TypeCache/Mediation/CustomAfterRule.cs @@ -5,20 +5,20 @@ namespace TypeCache.Mediation; -internal sealed class CustomAfterRule(Func executeAsync) +internal sealed class CustomAfterRule(Func execute) : IAfterRule where REQUEST : IRequest { [MethodImpl(AggressiveInlining), DebuggerHidden] - public Task HandleAsync(REQUEST request, CancellationToken token = default) - => executeAsync(request, token); + public Task Handle(REQUEST request, CancellationToken token = default) + => execute(request, token); } -internal sealed class CustomAfterRule(Func executeAsync) +internal sealed class CustomAfterRule(Func execute) : IAfterRule where REQUEST : IRequest { [MethodImpl(AggressiveInlining), DebuggerHidden] - public Task HandleAsync(REQUEST request, RESPONSE response, CancellationToken token = default) - => executeAsync(request, response, token); + public Task Handle(REQUEST request, RESPONSE response, CancellationToken token = default) + => execute(request, response, token); } diff --git a/src/TypeCache/Mediation/CustomRule.cs b/src/TypeCache/Mediation/CustomRule.cs index d572ba70..26dc85f5 100644 --- a/src/TypeCache/Mediation/CustomRule.cs +++ b/src/TypeCache/Mediation/CustomRule.cs @@ -1,24 +1,21 @@ // Copyright (c) 2021 Samuel Abraham -using System.Runtime.CompilerServices; -using TypeCache.Extensions; - namespace TypeCache.Mediation; -internal sealed class CustomRule(Func executeAsync) +internal sealed class CustomRule(Func execute) : IRule where REQUEST : IRequest { [MethodImpl(AggressiveInlining), DebuggerHidden] - public Task ExecuteAsync(REQUEST request, CancellationToken token = default) - => executeAsync(request, token); + public Task Execute(REQUEST request, CancellationToken token = default) + => execute(request, token); } -internal sealed class CustomRule(Func> mapAsync) +internal sealed class CustomRule(Func> map) : IRule where REQUEST : IRequest { [MethodImpl(AggressiveInlining), DebuggerHidden] - public Task MapAsync(REQUEST request, CancellationToken token = default) - => mapAsync(request, token); + public Task Map(REQUEST request, CancellationToken token = default) + => map(request, token); } diff --git a/src/TypeCache/Mediation/CustomValidationRule.cs b/src/TypeCache/Mediation/CustomValidationRule.cs index 8f4ccd33..87aa63f0 100644 --- a/src/TypeCache/Mediation/CustomValidationRule.cs +++ b/src/TypeCache/Mediation/CustomValidationRule.cs @@ -2,11 +2,11 @@ namespace TypeCache.Mediation; -internal sealed class CustomValidationRule(Func validateAsync) +internal sealed class CustomValidationRule(Func validate) : IValidationRule where REQUEST : IRequest { [MethodImpl(AggressiveInlining), DebuggerHidden] - public Task ValidateAsync(REQUEST request, CancellationToken token = default) - => validateAsync(request, token); + public Task Validate(REQUEST request, CancellationToken token = default) + => validate(request, token); } diff --git a/src/TypeCache/Mediation/IAfterRule.cs b/src/TypeCache/Mediation/IAfterRule.cs index 7e3fb24b..aa55f2ac 100644 --- a/src/TypeCache/Mediation/IAfterRule.cs +++ b/src/TypeCache/Mediation/IAfterRule.cs @@ -5,11 +5,11 @@ namespace TypeCache.Mediation; public interface IAfterRule where REQUEST : IRequest { - Task HandleAsync(REQUEST request, CancellationToken token = default); + Task Handle(REQUEST request, CancellationToken token = default); } public interface IAfterRule where REQUEST : IRequest { - Task HandleAsync(REQUEST request, RESPONSE response, CancellationToken token = default); + Task Handle(REQUEST request, RESPONSE response, CancellationToken token = default); } diff --git a/src/TypeCache/Mediation/IMediator.cs b/src/TypeCache/Mediation/IMediator.cs index 332aa711..685f2c91 100644 --- a/src/TypeCache/Mediation/IMediator.cs +++ b/src/TypeCache/Mediation/IMediator.cs @@ -10,7 +10,7 @@ public interface IMediator /// May register rules of type that run in parallel after the rules run. /// /// - Task ExecuteAsync(REQUEST request, CancellationToken token = default) + Task Execute(REQUEST request, CancellationToken token = default) where REQUEST : IRequest; /// @@ -19,13 +19,13 @@ Task ExecuteAsync(REQUEST request, CancellationToken token = default) /// May register rules of type that run in parallel after the rules run. /// /// - Task MapAsync(IRequest request, CancellationToken token = default); + Task Map(IRequest request, CancellationToken token = default); /// /// Retrieve validation messages for a rule request. /// Must register validation rules of type . /// /// - Task ValidateAsync(REQUEST request, CancellationToken token = default) + void Validate(REQUEST request, CancellationToken token = default) where REQUEST : notnull; } diff --git a/src/TypeCache/Mediation/IRule.cs b/src/TypeCache/Mediation/IRule.cs index a376e8d7..793158a0 100644 --- a/src/TypeCache/Mediation/IRule.cs +++ b/src/TypeCache/Mediation/IRule.cs @@ -5,11 +5,11 @@ namespace TypeCache.Mediation; public interface IRule where REQUEST : IRequest { - Task ExecuteAsync(REQUEST request, CancellationToken token = default); + Task Execute(REQUEST request, CancellationToken token = default); } public interface IRule where REQUEST : IRequest { - Task MapAsync(REQUEST request, CancellationToken token = default); + Task Map(REQUEST request, CancellationToken token = default); } diff --git a/src/TypeCache/Mediation/IValidationRule.cs b/src/TypeCache/Mediation/IValidationRule.cs index 4d338005..d89fecb5 100644 --- a/src/TypeCache/Mediation/IValidationRule.cs +++ b/src/TypeCache/Mediation/IValidationRule.cs @@ -4,5 +4,5 @@ namespace TypeCache.Mediation; public interface IValidationRule { - Task ValidateAsync(REQUEST request, CancellationToken token = default); + Task Validate(REQUEST request, CancellationToken token = default); } diff --git a/src/TypeCache/Mediation/Mediator.cs b/src/TypeCache/Mediation/Mediator.cs index 7a2b7f36..dbfd07a2 100644 --- a/src/TypeCache/Mediation/Mediator.cs +++ b/src/TypeCache/Mediation/Mediator.cs @@ -1,6 +1,5 @@ // Copyright (c) 2021 Samuel Abraham -using System.Data; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using TypeCache.Extensions; @@ -12,98 +11,100 @@ internal sealed class Mediator(IServiceProvider serviceProvider, ILogger(REQUEST request, CancellationToken token = default) + public async Task Execute(REQUEST request, CancellationToken token = default) where REQUEST : IRequest { + serviceProvider.AssertNotNull(); + await using var scope = this._ServiceProvider.CreateAsyncScope(); - await this.ValidateAsync(request, token); + this.Validate(request, token); var rule = scope.ServiceProvider.GetRequiredService>(); var afterRules = scope.ServiceProvider.GetService>>(); try { - await rule.ExecuteAsync(request, token); + await rule.Execute(request, token); if (afterRules?.Any() is true) - Task.WaitAll(afterRules.Select(afterRule => afterRule.HandleAsync(request, token)).ToArray(), token); + Task.WaitAll(afterRules.Select(afterRule => afterRule.Handle(request, token)).ToArray(), token); } catch (AggregateException error) { - logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.ExecuteAsync)); + logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.Execute)); await Task.FromException(error.InnerExceptions.Count == 1 ? error.InnerException! : error); } catch (Exception error) { - logger?.LogError(error, "{mediator}.{function} failure: {message}", nameof(Mediator), nameof(Mediator.ExecuteAsync), error.Message); + logger?.LogError(error, "{mediator}.{function} failure: {message}", nameof(Mediator), nameof(Mediator.Execute), error.Message); await Task.FromException(error); } } - public async Task MapAsync(REQUEST request, CancellationToken token = default) + public async Task Map(REQUEST request, CancellationToken token = default) where REQUEST : IRequest { + serviceProvider.AssertNotNull(); + await using var scope = this._ServiceProvider.CreateAsyncScope(); - await this.ValidateAsync(request, token); + this.Validate(request, token); var rule = scope.ServiceProvider.GetRequiredService>(); var afterRules = scope.ServiceProvider.GetService>>(); try { - var response = await rule.MapAsync(request, token); - + var response = await rule.Map(request, token); if (afterRules?.Any() is true) - Task.WaitAll(afterRules.Select(afterRule => afterRule.HandleAsync(request, response, token)).ToArray(), token); + Task.WaitAll(afterRules.Select(afterRule => afterRule.Handle(request, response, token)).ToArray(), token); return response; } catch (AggregateException error) { - logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.MapAsync)); + logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.Map)); await Task.FromException(error.InnerExceptions.Count == 1 ? error.InnerException! : error); } catch (Exception error) { - logger?.LogError(error, "{mediator}.{function} failure: {message}", nameof(Mediator), nameof(Mediator.MapAsync), error.Message); + logger?.LogError(error, "{mediator}.{function} failure: {message}", nameof(Mediator), nameof(Mediator.Map), error.Message); await Task.FromException(error); } return default!; } - public Task MapAsync(IRequest request, CancellationToken token = default) - { - request.AssertNotNull(); - - return (Task)this.GetType().InvokeMethod(nameof(Mediator.MapAsync), [request.GetType(), typeof(RESPONSE)], this, request, token)!; - } + public Task Map(IRequest request, CancellationToken token = default) + => (Task)this.GetType().InvokeMethod(nameof(Mediator.Map), [request.GetType(), typeof(RESPONSE)], this, request, token)!; - public async Task ValidateAsync(REQUEST request, CancellationToken token = default) + public void Validate(REQUEST request, CancellationToken token = default) where REQUEST : notnull { - request.AssertNotNull(); + serviceProvider.AssertNotNull(); - await using var scope = this._ServiceProvider.CreateAsyncScope(); + using var scope = this._ServiceProvider.CreateScope(); var validationRules = scope.ServiceProvider.GetService>>(); if (validationRules?.Any() is not true) - await Task.CompletedTask; + return; try { - Task.WaitAll(validationRules!.Select(validationRule => validationRule.ValidateAsync(request, token)).ToArray(), token); + Task.WaitAll(validationRules!.Select(validationRule => validationRule.Validate(request, token)).ToArray(), token); } catch (AggregateException error) { - logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.ValidateAsync)); - await Task.FromException(error.InnerExceptions.Count == 1 ? error.InnerException! : error); + logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.Validate)); + if (error.InnerExceptions.Count == 1) + throw error.InnerException!; + + throw; } catch (Exception error) { - logger?.LogError(error, "{mediator}.{function} failure: {message}", nameof(Mediator), nameof(Mediator.ValidateAsync), error.Message); - await Task.FromException(error); + logger?.LogError(error, "{mediator}.{function} failure: {message}", nameof(Mediator), nameof(Mediator.Validate), error.Message); + throw; } } } diff --git a/src/TypeCache/Mediation/RuleFactory.cs b/src/TypeCache/Mediation/RuleFactory.cs index 7c8c4fee..6be49c63 100644 --- a/src/TypeCache/Mediation/RuleFactory.cs +++ b/src/TypeCache/Mediation/RuleFactory.cs @@ -6,6 +6,7 @@ namespace TypeCache.Mediation; public static class RuleFactory { + /// public static IAfterRule CreateAfterRule(Func handleAsync) where REQUEST : IRequest { @@ -14,6 +15,7 @@ public static IAfterRule CreateAfterRule(Func(handleAsync); } + /// public static IAfterRule CreateAfterRule(Action handle) where REQUEST : IRequest { @@ -22,6 +24,7 @@ public static IAfterRule CreateAfterRule(Action handl return new CustomAfterRule((REQUEST request, CancellationToken token) => Task.Run(() => handle(request), token)); } + /// public static IAfterRule CreateAfterRule(Func handleAsync) where REQUEST : IRequest { @@ -30,6 +33,7 @@ public static IAfterRule CreateAfterRule(F return new CustomAfterRule(handleAsync); } + /// public static IAfterRule CreateAfterRule(Action handle) where REQUEST : IRequest { @@ -38,6 +42,7 @@ public static IAfterRule CreateAfterRule(A return new CustomAfterRule((REQUEST request, RESPONSE response, CancellationToken token) => Task.Run(() => handle(request, response), token)); } + /// public static IRule CreateRule(Func executeAsync) where REQUEST : IRequest { @@ -46,6 +51,7 @@ public static IRule CreateRule(Func(executeAsync); } + /// public static IRule CreateRule(Action execute) where REQUEST : IRequest { @@ -54,6 +60,7 @@ public static IRule CreateRule(Action execute) return new CustomRule((REQUEST request, CancellationToken token) => Task.Run(() => execute(request), token)); } + /// public static IRule CreateRule(Func> mapAsync) where REQUEST : IRequest { @@ -62,6 +69,7 @@ public static IRule CreateRule(Func(mapAsync); } + /// public static IRule CreateRule(Func map) where REQUEST : IRequest { @@ -70,6 +78,7 @@ public static IRule CreateRule(Func((REQUEST request, CancellationToken token) => Task.Run(() => map(request), token)); } + /// public static IValidationRule CreateValidationRule(Func validateAsync) where REQUEST : IRequest { @@ -78,6 +87,7 @@ public static IValidationRule CreateValidationRule(Func(validateAsync); } + /// public static IValidationRule CreateValidationRule(Action validate) where REQUEST : IRequest { diff --git a/src/TypeCache/Mediation/RulesBuilder.cs b/src/TypeCache/Mediation/RulesBuilder.cs index dd6690ea..3679b793 100644 --- a/src/TypeCache/Mediation/RulesBuilder.cs +++ b/src/TypeCache/Mediation/RulesBuilder.cs @@ -12,10 +12,12 @@ public readonly struct RulesBuilder(IServiceCollection services) /// /// Registers Singleton: . /// + /// public RulesBuilder AddAfterRule(IAfterRule afterRule) where REQUEST : IRequest { afterRule.AssertNotNull(); + this._Services.AddSingleton>(afterRule); return this; } @@ -23,10 +25,12 @@ public RulesBuilder AddAfterRule(IAfterRule afterRule) /// /// Registers Singleton: . /// + /// public RulesBuilder AddAfterRule(IAfterRule afterRule) where REQUEST : IRequest { afterRule.AssertNotNull(); + this._Services.AddSingleton>(afterRule); return this; } @@ -34,10 +38,12 @@ public RulesBuilder AddAfterRule(IAfterRule /// Registers an implementation of . ///
+ /// public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Func> createAfterRule) where REQUEST : IRequest { createAfterRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IAfterRule), createAfterRule, serviceLifetime)); return this; } @@ -45,10 +51,12 @@ public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Func< /// /// Registers an implementation of . /// + /// public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Func afterRule) where REQUEST : IRequest { afterRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IAfterRule), provider => RuleFactory.CreateAfterRule(afterRule), serviceLifetime)); return this; } @@ -56,10 +64,12 @@ public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Func< /// /// Registers an implementation of . /// + /// public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Action afterRule) where REQUEST : IRequest { afterRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IAfterRule), provider => RuleFactory.CreateAfterRule(afterRule), serviceLifetime)); return this; } @@ -67,10 +77,12 @@ public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Actio /// /// Registers an implementation of . /// + /// public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Func> createAfterRule) where REQUEST : IRequest { createAfterRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IAfterRule), createAfterRule, serviceLifetime)); return this; } @@ -78,10 +90,12 @@ public RulesBuilder AddAfterRule(ServiceLifetime serviceLifet /// /// Registers an implementation of . /// + /// public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Func afterRule) where REQUEST : IRequest { afterRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IAfterRule), provider => RuleFactory.CreateAfterRule(afterRule), serviceLifetime)); return this; } @@ -89,10 +103,12 @@ public RulesBuilder AddAfterRule(ServiceLifetime serviceLifet /// /// Registers an implementation of . /// + /// public RulesBuilder AddAfterRule(ServiceLifetime serviceLifetime, Action afterRule) where REQUEST : IRequest { afterRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IAfterRule), provider => RuleFactory.CreateAfterRule(afterRule), serviceLifetime)); return this; } @@ -100,10 +116,12 @@ public RulesBuilder AddAfterRule(ServiceLifetime serviceLifet /// /// Registers Singleton: . /// + /// public RulesBuilder AddRule(IRule rule) where REQUEST : IRequest { rule.AssertNotNull(); + this._Services.AddSingleton>(rule); return this; } @@ -111,10 +129,12 @@ public RulesBuilder AddRule(IRule rule) /// /// Registers Singleton: . /// + /// public RulesBuilder AddRule(IRule rule) where REQUEST : IRequest { rule.AssertNotNull(); + this._Services.AddSingleton>(rule); return this; } @@ -122,10 +142,12 @@ public RulesBuilder AddRule(IRule rule) /// /// Registers an implementation of . /// + /// public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Func> createRule) where REQUEST : IRequest { createRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IRule), createRule, serviceLifetime)); return this; } @@ -133,10 +155,12 @@ public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Func /// Registers an implementation of . /// + /// public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Func rule) where REQUEST : IRequest { rule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IRule), provider => RuleFactory.CreateRule(rule), serviceLifetime)); return this; } @@ -144,10 +168,12 @@ public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Func /// Registers an implementation of . /// + /// public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Action rule) where REQUEST : IRequest { rule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IRule), provider => RuleFactory.CreateRule(rule), serviceLifetime)); return this; } @@ -155,10 +181,12 @@ public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Action /// Registers an implementation of . /// + /// public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Func> createRule) where REQUEST : IRequest { createRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IRule), createRule, serviceLifetime)); return this; } @@ -166,10 +194,12 @@ public RulesBuilder AddRule(ServiceLifetime serviceLifetime, /// /// Registers an implementation of . /// + /// public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Func> rule) where REQUEST : IRequest { rule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IRule), provider => RuleFactory.CreateRule(rule), serviceLifetime)); return this; } @@ -177,10 +207,12 @@ public RulesBuilder AddRule(ServiceLifetime serviceLifetime, /// /// Registers an implementation of . /// + /// public RulesBuilder AddRule(ServiceLifetime serviceLifetime, Func rule) where REQUEST : IRequest { rule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IRule), provider => RuleFactory.CreateRule(rule), serviceLifetime)); return this; } @@ -188,10 +220,12 @@ public RulesBuilder AddRule(ServiceLifetime serviceLifetime, /// /// Registers Singleton: . /// + /// public RulesBuilder AddValidationRule(IValidationRule validationRule) where REQUEST : IRequest { validationRule.AssertNotNull(); + this._Services.AddSingleton>(validationRule); return this; } @@ -199,10 +233,12 @@ public RulesBuilder AddValidationRule(IValidationRule validati /// /// Registers an implementation of . /// + /// public RulesBuilder AddValidationRule(ServiceLifetime serviceLifetime, Func validationRule) where REQUEST : IRequest { validationRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IValidationRule), provider => RuleFactory.CreateValidationRule(validationRule), serviceLifetime)); return this; } @@ -210,10 +246,12 @@ public RulesBuilder AddValidationRule(ServiceLifetime serviceLifetime, /// /// Registers an implementation of . /// + /// public RulesBuilder AddValidationRule(ServiceLifetime serviceLifetime, Action validationRule) where REQUEST : IRequest { validationRule.AssertNotNull(); + this._Services.Add(ServiceDescriptor.Describe(typeof(IValidationRule), provider => RuleFactory.CreateValidationRule(validationRule), serviceLifetime)); return this; } diff --git a/src/TypeCache/Net/Mediation/HttpClientRule.cs b/src/TypeCache/Net/Mediation/HttpClientRule.cs index a7366643..afad6108 100644 --- a/src/TypeCache/Net/Mediation/HttpClientRule.cs +++ b/src/TypeCache/Net/Mediation/HttpClientRule.cs @@ -8,7 +8,7 @@ namespace TypeCache.Net.Mediation; internal sealed class HttpClientRule(IHttpClientFactory factory) : IRule { - public async Task MapAsync(HttpClientRequest request, CancellationToken token = default) + public async Task Map(HttpClientRequest request, CancellationToken token = default) { var httpClient = request.HttpClientName.IsNotBlank() ? factory.CreateClient(request.HttpClientName) : factory.CreateClient(); using var httpResponse = await httpClient.SendAsync(request.Message, HttpCompletionOption.ResponseContentRead, token); diff --git a/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs b/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs index 72680931..088ef832 100644 --- a/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs +++ b/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs @@ -8,9 +8,12 @@ namespace TypeCache.Net.Mediation; internal sealed class HttpClientValidationRule : IValidationRule { - public Task ValidateAsync(HttpClientRequest request, CancellationToken token) => Task.Run(() => + public Task Validate(HttpClientRequest request, CancellationToken token) => Task.Run(() => { - request?.Message?.RequestUri.AssertNotNull(); - request!.Message.RequestUri!.IsAbsoluteUri.AssertTrue(); + request.AssertNotNull(); + request.Message.AssertNotNull(); + request.Message.RequestUri.AssertNotNull(); + + request.Message.RequestUri.IsAbsoluteUri.AssertTrue(); }, token); } diff --git a/src/TypeCache/TypeCache.csproj b/src/TypeCache/TypeCache.csproj index acb6dacd..580d3e6b 100644 --- a/src/TypeCache/TypeCache.csproj +++ b/src/TypeCache/TypeCache.csproj @@ -6,7 +6,7 @@ TypeCache true TypeCache - 8.0.1 + 8.1.0 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache Reflection diff --git a/src/TypeCache/Utilities/Accessor.cs b/src/TypeCache/Utilities/Accessor.cs deleted file mode 100644 index f7e75405..00000000 --- a/src/TypeCache/Utilities/Accessor.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -using System.Collections.Frozen; -using TypeCache.Extensions; - -namespace TypeCache.Utilities; - -public sealed class Accessor(IEnumerable items) - : IAccessor - where T : class, IName -{ - private readonly IReadOnlyDictionary _Items = items - .ToDictionary(item => item.Name, item => item) - .ToFrozenDictionary(StringComparer.OrdinalIgnoreCase); - - public T? this[string name] => this._Items.TryGetValue(name, out var item) ? item : default; - - [MethodImpl(AggressiveInlining), DebuggerHidden] - public bool Has(string name) - => this._Items.ContainsKey(name); -} diff --git a/src/TypeCache/Utilities/IAccessor.cs b/src/TypeCache/Utilities/IAccessor.cs deleted file mode 100644 index 6bd9d778..00000000 --- a/src/TypeCache/Utilities/IAccessor.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -namespace TypeCache.Utilities; - -public interface IAccessor - where T : class, IName -{ - T? this[string name] { get; } - - bool Has([NotNullWhen(true)] string name); -} diff --git a/src/TypeCache/Utilities/IName.cs b/src/TypeCache/Utilities/IName.cs deleted file mode 100644 index 24b16a83..00000000 --- a/src/TypeCache/Utilities/IName.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -namespace TypeCache.Utilities; - -public interface IName -{ - public string Name { get; } -} diff --git a/src/TypeCache/Utilities/Singleton.cs b/src/TypeCache/Utilities/Singleton.cs new file mode 100644 index 00000000..0fd82f0c --- /dev/null +++ b/src/TypeCache/Utilities/Singleton.cs @@ -0,0 +1,9 @@ +using TypeCache.Extensions; + +namespace TypeCache.Utilities; + +public static class Singleton + where T : class, new() +{ + public static T Instance { get; } = (T)typeof(T).Create()!; +} diff --git a/src/TypeCache/Utilities/TypeStore.cs b/src/TypeCache/Utilities/TypeStore.cs index b23ca2ab..5a86d49a 100644 --- a/src/TypeCache/Utilities/TypeStore.cs +++ b/src/TypeCache/Utilities/TypeStore.cs @@ -16,9 +16,9 @@ namespace TypeCache.Utilities; -internal static class TypeStore +public static class TypeStore { - public static IReadOnlySet<(RuntimeTypeHandle Handle, CollectionType CollectionType)> CollectionTypeMap => new[] + internal static IReadOnlySet<(RuntimeTypeHandle Handle, CollectionType CollectionType)> CollectionTypeMap => new[] { (typeof(Array).TypeHandle, CollectionType.Array), (typeof(ArrayList).TypeHandle, CollectionType.ArrayList), @@ -72,7 +72,7 @@ internal static class TypeStore (typeof(ICollection<>).TypeHandle, CollectionType.Collection) }.ToFrozenSet(); - public static IReadOnlySet<(RuntimeTypeHandle Handle, ObjectType ObjectType)> ObjectTypeMap => new[] + internal static IReadOnlySet<(RuntimeTypeHandle Handle, ObjectType ObjectType)> ObjectTypeMap => new[] { (typeof(Attribute).TypeHandle, ObjectType.Attribute), (typeof(DataColumn).TypeHandle, ObjectType.DataColumn), @@ -138,15 +138,15 @@ static TypeStore() }); DefaultValueFactory = new LazyDictionary>(handle => handle.ToType().ToDefaultExpression().As().Lambda>().Compile()); - DefaultValueTypeConstructorInvokes = new LazyDictionary>(handle => + DefaultValueTypeConstructorFuncs = new LazyDictionary>(handle => handle.ToType().ToNewExpression().As().Lambda>().Compile()); - FieldGetInvokes = new(); - FieldSetInvokes = new(); - MethodInvokes = new LazyDictionary<(RuntimeTypeHandle TypeHandle, RuntimeMethodHandle MethodHandle), Func>(_ => + FieldGetFuncs = new(); + FieldSetActions = new(); + MethodFuncs = new LazyDictionary<(RuntimeTypeHandle TypeHandle, RuntimeMethodHandle MethodHandle), Func>(_ => _.MethodHandle.ToMethodBase(_.TypeHandle) switch { - MethodInfo methodInfo => methodInfo.ToInvokeLambdaExpression().Compile(), - ConstructorInfo constructorInfo => constructorInfo.ToInvokeLambdaExpression().Compile(), + MethodInfo methodInfo => methodInfo.ToFuncExpression().Compile(), + ConstructorInfo constructorInfo => constructorInfo.ToFuncExpression().Compile(), _ => throw new UnreachableException("Method or Constructor not found.") }); ObjectTypes = new LazyDictionary(handle => @@ -167,7 +167,6 @@ Type type when type.GetScalarType() is not ScalarType.None => ObjectType.DataTyp { typeof(DateOnly).TypeHandle, ScalarType.DateOnly }, { typeof(DateTime).TypeHandle, ScalarType.DateTime }, { typeof(DateTimeOffset).TypeHandle, ScalarType.DateTimeOffset }, - { typeof(DBNull).TypeHandle, ScalarType.DBNull }, { typeof(decimal).TypeHandle, ScalarType.Decimal }, { typeof(double).TypeHandle, ScalarType.Double }, { typeof(Enum).TypeHandle, ScalarType.Enum }, @@ -193,19 +192,19 @@ Type type when type.GetScalarType() is not ScalarType.None => ObjectType.DataTyp }.ToFrozenDictionary(); } - public static IReadOnlyDictionary> DefaultValueFactory { get; } + public static IReadOnlyDictionary CollectionTypes { get; } - public static IReadOnlyDictionary> DefaultValueTypeConstructorInvokes { get; } + public static IReadOnlyDictionary DataTypes { get; } - public static ConcurrentDictionary> FieldGetInvokes { get; } + public static IReadOnlyDictionary> DefaultValueFactory { get; } - public static ConcurrentDictionary> FieldSetInvokes { get; } + public static IReadOnlyDictionary> DefaultValueTypeConstructorFuncs { get; } - public static IReadOnlyDictionary<(RuntimeTypeHandle, RuntimeMethodHandle), Func> MethodInvokes { get; } + public static ConcurrentDictionary> FieldGetFuncs { get; } - internal static IReadOnlyDictionary CollectionTypes { get; } + public static ConcurrentDictionary> FieldSetActions { get; } - internal static IReadOnlyDictionary DataTypes { get; } + public static IReadOnlyDictionary<(RuntimeTypeHandle, RuntimeMethodHandle), Func> MethodFuncs { get; } - internal static IReadOnlyDictionary ObjectTypes { get; } + public static IReadOnlyDictionary ObjectTypes { get; } } diff --git a/src/TypeCache/Utilities/ValueConverter.cs b/src/TypeCache/Utilities/ValueConverter.cs index 9a140596..bd768569 100644 --- a/src/TypeCache/Utilities/ValueConverter.cs +++ b/src/TypeCache/Utilities/ValueConverter.cs @@ -20,12 +20,6 @@ public static Expression CreateConversionExpression(this Expression @this, Type var sourceScalarType = @this.Type.GetScalarType(); var targetScalarType = targetType.GetScalarType(); - if (targetScalarType == ScalarType.DBNull) - return DBNull.Value.ToConstantExpression(); - - if (sourceScalarType == ScalarType.DBNull) - return Expression.Constant(null); - var expression = (sourceScalarType, targetScalarType) switch { (ScalarType.Boolean, ScalarType.Char) => LambdaFactory.CreateFunc((bool _) => _ ? '1' : '0'), diff --git a/tests/TypeCache.GraphQL.TestApp/Program.cs b/tests/TypeCache.GraphQL.TestApp/Program.cs index eccd5ee3..23492966 100644 --- a/tests/TypeCache.GraphQL.TestApp/Program.cs +++ b/tests/TypeCache.GraphQL.TestApp/Program.cs @@ -2,6 +2,7 @@ using GraphQL.Server.Ui.Playground; using Microsoft.Data.SqlClient; +using Microsoft.Extensions.DependencyInjection; using TypeCache.Attributes; using TypeCache.Data; using TypeCache.Extensions; @@ -17,8 +18,7 @@ builder.Services .AddMediation(builder => builder.AddSqlCommandRules()) .AddHashMaker((decimal)(Tau - E), (decimal)(Tau + 2 * E)) - .AddDataSource(DATASOURCE, SqlClientFactory.Instance, builder.Configuration.GetConnectionString(DATASOURCE)!, DataSourceType.SqlServer) - .AddDataSourceAccessor() + .AddDataSource(DATASOURCE, SqlClientFactory.Instance, builder.Configuration.GetConnectionString(DATASOURCE)!) .AddGraphQL() .AddGraphQLTypeExtensions(person => { @@ -31,8 +31,7 @@ .UseRouting() .UseGraphQLSchema("/graphql/custom", (schema, provider) => { - var dataSourceAccessor = provider.GetRequiredService>(); - var dataSource = dataSourceAccessor[DATASOURCE]!; + var dataSource = provider.GetRequiredKeyedService(DATASOURCE); schema.AddVersion("1.0"); schema.AddSqlApiEndpoints(dataSource, "Person.Person"); @@ -41,16 +40,14 @@ }) .UseGraphQLSchema("/graphql/data", (schema, provider) => { - var dataSourceAccessor = provider.GetRequiredService>(); - var dataSource = dataSourceAccessor[DATASOURCE]!; + var dataSource = provider.GetRequiredKeyedService(DATASOURCE); schema.AddVersion("1.0"); schema.AddDatabaseEndpoints(dataSource, SqlApiAction.CRUD, "AdventureWorks2019", "Person"); }) .UseGraphQLSchema("/graphql/schema", (schema, provider) => { - var dataSourceAccessor = provider.GetRequiredService>(); - var dataSource = dataSourceAccessor[DATASOURCE]!; + var dataSource = provider.GetRequiredKeyedService(DATASOURCE); schema.AddVersion("1.0"); schema.AddDatabaseSchemaQueries(dataSource); diff --git a/tests/TypeCache.GraphQL.TestApp/Tables/Person.cs b/tests/TypeCache.GraphQL.TestApp/Tables/Person.cs index 7b080ba3..d501c70b 100644 --- a/tests/TypeCache.GraphQL.TestApp/Tables/Person.cs +++ b/tests/TypeCache.GraphQL.TestApp/Tables/Person.cs @@ -22,10 +22,10 @@ public class Person public int EmailPromotion { get; set; } public string? AdditionalContactInfo { get; set; } public string? Demographics { get; set; } - [GraphQLType>()] + [GraphQLType>()] [GraphQLName("rowguid")] public Guid Rowguid { get; set; } - [GraphQLType>()] + [GraphQLType>>()] public DateTime ModifiedDate { get; set; } public IEnumerable GetPersons() diff --git a/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj b/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj index d1b961da..85a68e5a 100644 --- a/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj +++ b/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/TypeCache.Tests/Extensions/EnumExtensions.cs b/tests/TypeCache.Tests/Extensions/EnumExtensions.cs index e86168ab..94208069 100644 --- a/tests/TypeCache.Tests/Extensions/EnumExtensions.cs +++ b/tests/TypeCache.Tests/Extensions/EnumExtensions.cs @@ -38,6 +38,29 @@ public void Hex() Assert.Equal(TestEnum.TestValue2.ToString("X"), TestEnum.TestValue2.Hex()); } + [Fact] + public void IsPrimitive() + { + Assert.Equal(typeof(bool).IsPrimitive, ScalarType.Boolean.IsPrimitive()); + Assert.Equal(typeof(char).IsPrimitive, ScalarType.Char.IsPrimitive()); + Assert.Equal(typeof(sbyte).IsPrimitive, ScalarType.SByte.IsPrimitive()); + Assert.Equal(typeof(short).IsPrimitive, ScalarType.Int16.IsPrimitive()); + Assert.Equal(typeof(int).IsPrimitive, ScalarType.Int32.IsPrimitive()); + Assert.Equal(typeof(long).IsPrimitive, ScalarType.Int64.IsPrimitive()); + Assert.Equal(typeof(nint).IsPrimitive, ScalarType.IntPtr.IsPrimitive()); + Assert.Equal(!typeof(Int128).IsPrimitive, ScalarType.Int128.IsPrimitive()); + Assert.Equal(typeof(byte).IsPrimitive, ScalarType.Byte.IsPrimitive()); + Assert.Equal(typeof(ushort).IsPrimitive, ScalarType.UInt16.IsPrimitive()); + Assert.Equal(typeof(uint).IsPrimitive, ScalarType.UInt32.IsPrimitive()); + Assert.Equal(typeof(ulong).IsPrimitive, ScalarType.UInt64.IsPrimitive()); + Assert.Equal(typeof(nuint).IsPrimitive, ScalarType.UIntPtr.IsPrimitive()); + Assert.Equal(!typeof(UInt128).IsPrimitive, ScalarType.UInt128.IsPrimitive()); + Assert.Equal(typeof(Half).IsPrimitive, ScalarType.Half.IsPrimitive()); + Assert.Equal(typeof(float).IsPrimitive, ScalarType.Single.IsPrimitive()); + Assert.Equal(typeof(double).IsPrimitive, ScalarType.Double.IsPrimitive()); + Assert.Equal(!typeof(decimal).IsPrimitive, ScalarType.Decimal.IsPrimitive()); + } + [Fact] public void Name() { diff --git a/tests/TypeCache.Tests/TypeCache.Tests.csproj b/tests/TypeCache.Tests/TypeCache.Tests.csproj index d22d800d..310fd43b 100644 --- a/tests/TypeCache.Tests/TypeCache.Tests.csproj +++ b/tests/TypeCache.Tests/TypeCache.Tests.csproj @@ -11,8 +11,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all