From 3bbd16639cd24de375a8fd86f2ceef535b42d3ba Mon Sep 17 00:00:00 2001 From: Samuel Abraham Date: Thu, 7 Mar 2024 19:39:19 -0800 Subject: [PATCH] v8.1.3 --- .editorconfig | 1 + src/TypeCache.GraphQL/Data/Connection.cs | 4 +- .../Extensions/EnumExtensions.cs | 4 +- .../Extensions/GraphQLAttributeExtensions.cs | 5 +- .../Extensions/GraphQLExtensions.cs | 39 +-- .../Extensions/QueryArgumentsExtensions.cs | 44 +++- .../ResolveFieldContextExtensions.cs | 19 +- .../Extensions/SchemaExtensions.cs | 116 +++++---- .../Resolvers/BatchLoaderFieldResolver.cs | 50 ++-- .../Resolvers/ItemLoaderFieldResolver.cs | 42 ++-- .../Resolvers/MethodFieldResolver.cs | 29 +-- .../Resolvers/MethodSourceStreamResolver.cs | 10 +- .../Resolvers/SqlApiDeleteFieldResolver.cs | 8 +- .../Resolvers/SqlApiInsertFieldResolver.cs | 12 +- .../Resolvers/SqlApiSelectFieldResolver.cs | 4 +- .../Resolvers/SqlApiUpdateFieldResolver.cs | 8 +- .../TypeCache.GraphQL.csproj | 6 +- .../Types/GraphQLEnumType.cs | 11 +- .../Types/GraphQLObjectType.cs | 7 +- .../Attributes/RequireClaimAttribute.cs | 4 +- .../Attributes/RequireHeaderAttribute.cs | 2 +- .../AuthorizationOptionsExtensions.cs | 6 +- .../Extensions/ClaimsPrincipalExtensions.cs | 2 +- .../EndpointRouteBuilderExtensions.cs | 6 +- src/TypeCache.Web/Handlers/SqlApiHandler.cs | 20 +- src/TypeCache.Web/TypeCache.Web.csproj | 2 +- src/TypeCache/Collections/EnumComparer.cs | 2 +- src/TypeCache/Data/DataSource.cs | 25 +- .../Data/Extensions/SqlExtensions.cs | 18 ++ .../Extensions/StringBuilderExtensions.cs | 5 +- src/TypeCache/Data/IDataSource.cs | 12 - src/TypeCache/Data/ObjectSchema.cs | 234 ++++++++++-------- src/TypeCache/Extensions/CharExtensions.cs | 4 +- src/TypeCache/Extensions/CsvExtensions.cs | 4 +- src/TypeCache/Extensions/EnumExtensions.cs | 13 +- ...essionExtensions.ArrayExpressionBuilder.cs | 6 +- .../Extensions/ExpressionExtensions.cs | 106 ++++---- src/TypeCache/Extensions/LoggerExtensions.cs | 9 +- src/TypeCache/Extensions/MapExtensions.cs | 4 +- .../Extensions/ReadOnlySpanExtensions.cs | 2 +- .../ReflectionExtensions.ConstructorInfo.cs | 8 +- .../ReflectionExtensions.Delegate.cs | 2 +- .../ReflectionExtensions.FieldInfo.cs | 21 +- .../ReflectionExtensions.MemberInfo.cs | 2 +- .../ReflectionExtensions.MethodBase.cs | 4 +- .../ReflectionExtensions.MethodInfo.cs | 35 ++- .../ReflectionExtensions.PropertyInfo.cs | 30 +-- .../Extensions/ReflectionExtensions.Type.cs | 138 ++++++----- .../Extensions/StringBuilderExtensions.cs | 13 + src/TypeCache/Extensions/StringExtensions.cs | 10 +- src/TypeCache/Mediation/Mediator.cs | 14 +- src/TypeCache/TypeCache.csproj | 2 +- src/TypeCache/Utilities/Enum.cs | 3 + src/TypeCache/Utilities/EventHandler.cs | 4 +- .../TypeCache.GraphQL.TestApp.csproj | 4 +- .../Data/Extensions/SqlExtensions.cs | 34 +-- .../Extensions/CharExtensions.cs | 4 +- .../Extensions/StringExtensions.cs | 8 +- tests/TypeCache.Tests/TypeCache.Tests.csproj | 8 +- 59 files changed, 670 insertions(+), 579 deletions(-) diff --git a/.editorconfig b/.editorconfig index 7ed285f7..a7019b98 100644 --- a/.editorconfig +++ b/.editorconfig @@ -141,6 +141,7 @@ csharp_style_prefer_method_group_conversion = true:silent csharp_style_prefer_top_level_statements = true:silent csharp_style_expression_bodied_lambdas = true:suggestion csharp_style_expression_bodied_local_functions = true:suggestion +csharp_style_prefer_primary_constructors = true:suggestion [*.vb] # Modifier preferences diff --git a/src/TypeCache.GraphQL/Data/Connection.cs b/src/TypeCache.GraphQL/Data/Connection.cs index ac8056da..0aaace62 100644 --- a/src/TypeCache.GraphQL/Data/Connection.cs +++ b/src/TypeCache.GraphQL/Data/Connection.cs @@ -20,9 +20,9 @@ public Connection() public Connection(uint offset, T[] items) { - offset += 1; + int o = (int)offset + 1; this.Items = items; - this.Edges = items.Select((row, i) => new Edge((int)offset + i, row)).ToArray(); + this.Edges = items.Select((row, i) => new Edge(o + i, row)).ToArray(); } [GraphQLDescription("The total number of records available. Returns `null` if the total number is unknown.")] diff --git a/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs b/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs index 14f32408..e54e1a3a 100644 --- a/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs @@ -13,19 +13,19 @@ public static class EnumExtensions => @this switch { ScalarType.Boolean => typeof(GraphQLScalarType), - ScalarType.Byte => typeof(GraphQLScalarType), + ScalarType.SByte => 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.Byte => 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), diff --git a/src/TypeCache.GraphQL/Extensions/GraphQLAttributeExtensions.cs b/src/TypeCache.GraphQL/Extensions/GraphQLAttributeExtensions.cs index 025c334b..9ba12dca 100644 --- a/src/TypeCache.GraphQL/Extensions/GraphQLAttributeExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/GraphQLAttributeExtensions.cs @@ -24,7 +24,8 @@ public static class GraphQLAttributeExtensions => @this.GetCustomAttribute()?.Description switch { null => null, - var description when @this is Type type && type.IsGenericType => string.Format(InvariantCulture, description, type.GenericTypeArguments.Select(_ => _.GraphQLName()).ToArray()), + var description when @this is Type type && type.IsGenericType => + string.Format(InvariantCulture, description, type.GenericTypeArguments.Select(_ => _.GraphQLName()).ToArray()), var description => description }; @@ -57,9 +58,11 @@ public static string GraphQLName(this MemberInfo @this) public static string GraphQLName(this ParameterInfo @this) => @this.GetCustomAttribute()?.Name ?? @this.Name(); + [MethodImpl(AggressiveInlining), DebuggerHidden] public static Type? GraphQLType(this MemberInfo @this) => @this.GetCustomAttribute()?.GetType().GenericTypeArguments[0]; + [MethodImpl(AggressiveInlining), DebuggerHidden] public static Type? GraphQLType(this ParameterInfo @this) => @this.GetCustomAttribute()?.GetType().GenericTypeArguments[0]; } diff --git a/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs b/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs index 6d87e215..f460d0f0 100644 --- a/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs @@ -92,9 +92,7 @@ public static Type ToGraphQLInterfaceType(this Type @this) /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static Type ToGraphQLObjectType(this Type @this) - => @this.IsValueType && @this.IsNullable() - ? typeof(GraphQLObjectType<>).MakeGenericType(@this.GenericTypeArguments[0]) - : typeof(GraphQLObjectType<>).MakeGenericType(@this); + => typeof(GraphQLObjectType<>).MakeGenericType(@this); public static Type ToGraphQLType(this ParameterInfo @this) { @@ -116,26 +114,29 @@ public static Type ToGraphQLType(this PropertyInfo @this, bool isInputType) public static Type ToGraphQLType(this Type @this, bool isInputType) { + if (@this.Is(typeof(Nullable<>))) + return @this.GenericTypeArguments[0].ToGraphQLType(isInputType); + + if (@this.IsEnum) + return @this.ToGraphQLEnumType(); + var objectType = @this.GetObjectType(); (objectType is ObjectType.Delegate).AssertFalse(); (objectType is ObjectType.Object).AssertFalse(); + var scalarGraphType = @this.GetScalarType().ToGraphType(); + if (scalarGraphType is not null) + return scalarGraphType; + var collectionType = @this.GetCollectionType(); if (collectionType.IsDictionary()) return typeof(KeyValuePair<,>).MakeGenericType(@this.GenericTypeArguments).ToGraphQLType(isInputType).ToNonNullGraphType().ToListGraphType(); - if (@this.IsEnum) - return @this.ToGraphQLEnumType(); - if (objectType is ObjectType.Task || objectType is ObjectType.ValueTask) return @this.IsGenericType ? @this.GenericTypeArguments.First()!.ToGraphQLType(false) : throw new ArgumentOutOfRangeException(nameof(@this), Invariant($"{nameof(Task)} and {nameof(ValueTask)} are not allowed as GraphQL types.")); - var scalarGraphType = @this.GetScalarType().ToGraphType(); - if (scalarGraphType is not null) - return scalarGraphType; - if (@this.HasElementType) { var elementType = @this.GetElementType()!.ToGraphQLType(isInputType); @@ -193,20 +194,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", nullable: true, 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, ...])")); + arguments.Add("timeZone", nullable: true, 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, ...)")); + arguments.Add("timeZone", nullable: true, 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("case", nullable: true, description: "Convert string value to upper or lower case."); + arguments.Add("length", nullable: true, description: "Exclude the rest of the string value if it exceeds this length."); + arguments.Add("match", nullable: true, description: "Returns the matching result based on the specified regular expression pattern, null if no match."); + arguments.Add("trim", nullable: true, description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.Trim)}(value)")); + arguments.Add("trimEnd", nullable: true, description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.TrimEnd)}(value)")); + arguments.Add("trimStart", nullable: true, description: Invariant($"{typeof(string).Namespace}.{nameof(String)}.{nameof(string.TrimStart)}(value)")); } return new() diff --git a/src/TypeCache.GraphQL/Extensions/QueryArgumentsExtensions.cs b/src/TypeCache.GraphQL/Extensions/QueryArgumentsExtensions.cs index c7e3c467..c7cbcb50 100644 --- a/src/TypeCache.GraphQL/Extensions/QueryArgumentsExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/QueryArgumentsExtensions.cs @@ -1,20 +1,58 @@ // Copyright (c) 2021 Samuel Abraham using System; +using System.Collections.Generic; using GraphQL.Types; +using TypeCache.Extensions; +using TypeCache.GraphQL.Types; namespace TypeCache.GraphQL.Extensions; public static class QueryArgumentsExtensions { - public static void Add(this QueryArguments @this, string name, object? defaultValue = null, string? description = null) - where T : IGraphType - => @this.Add(new QueryArgument + public static void Add(this QueryArguments @this, string name, bool nullable = false, object? defaultValue = null, string? description = null) + { + if (typeof(T).Implements(typeof(IGraphType))) + { + @this.Add(new QueryArgument(typeof(T)) + { + Name = name, + DefaultValue = defaultValue, + Description = description + }); + return; + } + + var isList = typeof(T).IsArray || typeof(T).Implements(typeof(IEnumerable<>).MakeGenericType(typeof(T))); + var type = typeof(T) switch + { + { IsArray: true } => typeof(T).GetElementType()!, + _ when isList => typeof(T).GenericTypeArguments[0], + _ => typeof(T) + }; + + var graphType = type switch + { + { IsEnum: true } => typeof(GraphQLEnumType<>), + _ when type.Implements(typeof(ISpanParsable<>)) => typeof(GraphQLScalarType<>), + _ => typeof(GraphQLInputType<>) + }; + + type = graphType.MakeGenericType(type); + + if (isList) + type = type.ToListGraphType(); + + if (!nullable) + type = type.ToNonNullGraphType(); + + @this.Add(new QueryArgument(type) { Name = name, DefaultValue = defaultValue, Description = description }); + } public static void Add(this QueryArguments @this, string name, Type type, object? defaultValue = null, string? description = null) => @this.Add(new(type) diff --git a/src/TypeCache.GraphQL/Extensions/ResolveFieldContextExtensions.cs b/src/TypeCache.GraphQL/Extensions/ResolveFieldContextExtensions.cs index 41f88324..23afb777 100644 --- a/src/TypeCache.GraphQL/Extensions/ResolveFieldContextExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/ResolveFieldContextExtensions.cs @@ -3,16 +3,13 @@ using System; using System.Collections.Generic; using System.Data; -using System.Diagnostics; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using GraphQL; using GraphQLParser.AST; using TypeCache.Data; using TypeCache.Extensions; using static System.FormattableString; -using static System.Runtime.CompilerServices.MethodImplOptions; namespace TypeCache.GraphQL.Extensions; @@ -41,16 +38,12 @@ public static DataTable GetArgumentAsDataTable(this IResolveFieldContext @this, return table; } - /// - /// => @.GetArguments((), ); - /// - /// Source object type. - [MethodImpl(AggressiveInlining), DebuggerHidden] - public static IEnumerable GetArguments(this IResolveFieldContext @this, MethodInfo methodInfo) - => @this.GetArguments(typeof(TSource), methodInfo); - - public static IEnumerable GetArguments(this IResolveFieldContext @this, Type? sourceType, MethodInfo methodInfo) + public static IEnumerable GetArguments(this IResolveFieldContext @this, MethodInfo methodInfo) { + var sourceType = @this.Source?.GetType(); + if (sourceType == typeof(object)) + sourceType = null; + var parameterInfos = methodInfo.GetParameters() .Where(parameterInfo => !parameterInfo.IsOut && !parameterInfo.IsRetval) .OrderBy(parameterInfo => parameterInfo.Position); @@ -62,7 +55,7 @@ public static DataTable GetArgumentAsDataTable(this IResolveFieldContext @this, { _ when parameterInfo.GraphQLIgnore() => null, _ when parameterInfo.ParameterType.Is() => @this, - _ when parameterInfo.ParameterType.Is(sourceType!) && !parameterInfo.ParameterType.Is() => @this.Source, + _ when sourceType is not null && parameterInfo.ParameterType.Is(sourceType) => @this.Source, IDictionary dictionary when !parameterInfo.ParameterType.Is>() => dictionary.MapTo(parameterInfo.ParameterType.Create()!), _ => argument diff --git a/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs b/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs index 4bd616ac..4a58fc09 100644 --- a/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using GraphQL; @@ -13,6 +14,7 @@ using TypeCache.Attributes; using TypeCache.Collections; using TypeCache.Data; +using TypeCache.Data.Extensions; using TypeCache.Extensions; using TypeCache.GraphQL.Attributes; using TypeCache.GraphQL.Extensions; @@ -218,16 +220,16 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour { var selectResponseType = SelectResponse.CreateGraphType(table, Invariant($"{objectSchema.Type.Name()}: `{objectSchema.Name}`"), resolvedType); var arguments = new QueryArguments(); - arguments.Add>>>("parameters", null, "Used to reference user input values from the where clause."); + arguments.Add("parameters", nullable: true, description: "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), defaultValue: false); + arguments.Add(nameof(SelectQuery.Where), nullable: true, description: "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), defaultValue: 0U); + arguments.Add(nameof(SelectQuery.Offset), defaultValue: 0U); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U); var field = new FieldType { @@ -259,6 +261,7 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour new QueryArgument { Name = nameof(SqlCommand.Timeout), + DefaultValue = 120U, Description = "The SQL command timeout in seconds." }), Name = Invariant($"delete{table}"), @@ -289,6 +292,7 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour new QueryArgument { Name = nameof(SqlCommand.Timeout), + DefaultValue = 120U, Description = "The SQL command timeout in seconds." }), Name = Invariant($"delete{table}Data"), @@ -304,16 +308,16 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour if (actions.HasFlag(SqlApiAction.Insert)) { var arguments = new QueryArguments(); - arguments.Add>>("parameters", null, "Used to reference user input values from the where clause."); + arguments.Add("parameters", nullable: true, description: "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.Distinct), false); - arguments.Add>(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); + arguments.Add(nameof(SelectQuery.Distinct), defaultValue: false); + arguments.Add(nameof(SelectQuery.Where), nullable: true, description: "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), defaultValue: 0U); + arguments.Add(nameof(SelectQuery.Offset), defaultValue: 0U); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U); var fieldType = new FieldType { @@ -331,7 +335,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", description: "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 @@ -350,9 +354,9 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour if (actions.HasFlag(SqlApiAction.Update)) { 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("parameters", nullable: true, description: "Used to reference user input values from the where clause."); + arguments.Add("set", description: "SET [Column1] = 111, [Column2] = N'111', [Column3] = GETDATE()"); + arguments.Add("where", nullable: true, description: "If `where` is omitted, all records will be updated."); var fieldType = new FieldType { @@ -370,8 +374,8 @@ 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("data", new NonNullGraphType(new ListGraphType(new NonNullGraphType(dataInputType))), null, "The data to be inserted."); + arguments.Add("columns", description: "The columns to be updated."); + arguments.Add("data", new NonNullGraphType(new ListGraphType(new NonNullGraphType(dataInputType))), description: "The data to be inserted."); var fieldType = new FieldType { @@ -574,13 +578,15 @@ public static FieldType AddMutation(this ISchema @this, MethodInfo methodInfo) /// public static FieldType[] AddMutations(this ISchema @this, string method) where T : notnull - => typeof(T).GetPublicMethods() + { + var publicMethods = typeof(T).GetPublicMethods() .Where(_ => _.Name().Is(method)) - .Select(@this.AddMutation) - .Concat(typeof(T).GetPublicStaticMethods() - .Where(_ => _.Name().Is(method)) - .Select(@this.AddMutation)) - .ToArray(); + .Select(@this.AddMutation); + var publicStaticMethods = typeof(T).GetPublicStaticMethods() + .Where(_ => _.Name().Is(method)) + .Select(@this.AddMutation); + return [..publicMethods, ..publicStaticMethods]; + } /// /// Method parameters with the following type are ignored in the schema and will have their value injected: @@ -737,7 +743,7 @@ public static FieldType AddQuery(this ISchema @this, /// public static FieldType AddQuery(this ISchema @this, MethodInfo methodInfo) { - if (methodInfo.ReturnType.IsAny(typeof(void), typeof(Task), typeof(ValueTask))) + if (methodInfo.ReturnType.IsAny([typeof(void), typeof(Task), typeof(ValueTask)])) throw new ArgumentException($"{nameof(AddQuery)}: GraphQL endpoints cannot have a return type that is void, {nameof(Task)} or {nameof(ValueTask)}."); return @this.Query().AddField(methodInfo, new MethodFieldResolver(methodInfo)); @@ -884,8 +890,8 @@ public static FieldType AddSqlApiDeleteDataEndpoint(this ISchema @this, IData var name = dataSource.CreateName(table); 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("data", description: "The data to be deleted."); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U, description: "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -919,9 +925,9 @@ public static FieldType AddSqlApiDeleteEndpoint(this ISchema @this, IDataSour var name = dataSource.CreateName(table); 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("parameters", nullable: true, description: "Used to reference user input values from the where clause."); + arguments.Add("where", nullable: true, description: "If `where` is omitted, all records will be deleted!"); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U, description: "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -955,9 +961,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>>>>("data", null, "The data to be inserted."); - arguments.Add>(nameof(SqlCommand.Timeout), null, "SQL Command timeout in seconds."); + arguments.Add("columns", description: "The columns to insert data into."); + arguments.Add("data", description: "The data to be inserted."); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U, description: "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -999,22 +1005,24 @@ public static FieldType AddSqlApiInsertEndpoint(this ISchema @this, IDataSour var propertyName = property.GraphQLName(); var propertyDeprecationReason = property.GraphQLDeprecationReason(); + var ascending = Sort.Ascending.ToSQL(); + graphOrderByEnum.Add(Invariant($"{propertyName}_{ascending}"), Invariant($"{propertyName} {ascending}"), Invariant($"{propertyName} {ascending}"), propertyDeprecationReason); graphOrderByEnum.AddOrderBy(new(propertyName, Sort.Ascending), propertyDeprecationReason); graphOrderByEnum.AddOrderBy(new(propertyName, Sort.Descending), propertyDeprecationReason); } var arguments = new QueryArguments(); - arguments.Add>>>("parameters", null, "Used to reference user input values from the where clause."); + arguments.Add("parameters", nullable: true, description: "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), nullable: true); - 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), defaultValue: false); + arguments.Add(nameof(SelectQuery.From), description: "The table or view to pull the data from to insert."); + arguments.Add(nameof(SelectQuery.Where), nullable: true, description: "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), defaultValue: 0U); + arguments.Add(nameof(SelectQuery.Offset), defaultValue: 0U); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U, description: "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -1061,16 +1069,16 @@ 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."); + arguments.Add("parameters", nullable: true, description: "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.Distinct), false); - arguments.Add>(nameof(SelectQuery.Where), null, "If `where` is omitted, all records will be returned."); + arguments.Add(nameof(SelectQuery.Distinct), defaultValue: false); + arguments.Add(nameof(SelectQuery.Where), nullable: true, description: "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), defaultValue: 0U); + arguments.Add(nameof(SelectQuery.Offset), defaultValue: 0U); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U, description: "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -1104,8 +1112,8 @@ public static FieldType AddSqlApiUpdateDataEndpoint(this ISchema @this, IData var name = dataSource.CreateName(table); 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("set", description: "The columns to be updated."); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U, description: "SQL Command timeout in seconds."); var fieldType = new FieldType { @@ -1139,10 +1147,10 @@ public static FieldType AddSqlApiUpdateEndpoint(this ISchema @this, IDataSour var name = dataSource.CreateName(table); 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("parameters", nullable: true, description: "Used to reference user input values from the where clause."); + arguments.Add("set", description: "SET [Column1] = 111, [Column2] = N'111', [Column3] = GETDATE()"); + arguments.Add("where", nullable: true, description: "If `where` is omitted, all records will be updated."); + arguments.Add(nameof(SqlCommand.Timeout), defaultValue: 120U, description: "SQL Command timeout in seconds."); var fieldType = new FieldType { diff --git a/src/TypeCache.GraphQL/Resolvers/BatchLoaderFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/BatchLoaderFieldResolver.cs index ac60557c..717eef60 100644 --- a/src/TypeCache.GraphQL/Resolvers/BatchLoaderFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/BatchLoaderFieldResolver.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; +using GraphQL; using GraphQL.DataLoader; using Microsoft.Extensions.DependencyInjection; using TypeCache.Collections; @@ -24,8 +25,8 @@ public sealed class BatchLoaderFieldResolver : FieldResolv private readonly Func _GetChildKey; private readonly bool _ReturnCollection; - /// /// + /// public BatchLoaderFieldResolver(MethodInfo methodInfo, Func getParentKey, Func getChildKey, bool returnCollection) { methodInfo.AssertNotNull(); @@ -40,44 +41,37 @@ public BatchLoaderFieldResolver(MethodInfo methodInfo, Func getPa this._ReturnCollection = returnCollection; } - protected override ValueTask ResolveAsync(IResolveFieldContext context) + /// + protected override async ValueTask ResolveAsync(IResolveFieldContext context) { context.RequestServices.AssertNotNull(); + context.Source.AssertNotNull(); var dataLoaderAccessor = context.RequestServices.GetRequiredService(); dataLoaderAccessor.Context.AssertNotNull(); - var dataLoaderResult = this._ReturnCollection - ? this.GetCollectionBatchLoaderResult(dataLoaderAccessor.Context, context) - : this.GetBatchLoaderResult(dataLoaderAccessor.Context, context); - return new ValueTask(dataLoaderResult.GetResultAsync(context.CancellationToken)); - } - - private IDataLoaderResult GetBatchLoaderResult(DataLoaderContext dataLoaderContext, IResolveFieldContext resolveFieldContext) - { - resolveFieldContext.Source.AssertNotNull(); - var key = this._GetParentKey((PARENT)resolveFieldContext.Source); - var dataLoader = dataLoaderContext.GetOrAddBatchLoader( - this._DataLoaderKey, keys => this.GetData(resolveFieldContext, keys), this._GetChildKey); - return (IDataLoaderResult)dataLoader.LoadAsync(key); - } - - private IDataLoaderResult GetCollectionBatchLoaderResult(DataLoaderContext dataLoaderContext, IResolveFieldContext resolveFieldContext) - { - resolveFieldContext.Source.AssertNotNull(); - var key = this._GetParentKey((PARENT)resolveFieldContext.Source); - var dataLoader = dataLoaderContext.GetOrAddCollectionBatchLoader( - this._DataLoaderKey, keys => this.GetData(resolveFieldContext, keys), this._GetChildKey); - return (IDataLoaderResult)dataLoader.LoadAsync(key); + var key = this._GetParentKey((PARENT)context.Source); + if (this._ReturnCollection) + { + var dataLoader = dataLoaderAccessor.Context.GetOrAddCollectionBatchLoader( + this._DataLoaderKey, keys => this.LoadData(context, keys), this._GetChildKey); + return await dataLoader.LoadAsync(key).GetResultAsync(context.CancellationToken); + } + else + { + var dataLoader = dataLoaderAccessor.Context.GetOrAddBatchLoader( + this._DataLoaderKey, keys => this.LoadData(context, keys), this._GetChildKey); + return await dataLoader.LoadAsync(key).GetResultAsync(context.CancellationToken); + } } - private Task> GetData(IResolveFieldContext context, IEnumerable keys) + private Task> LoadData(IResolveFieldContext context, IEnumerable keys) { var arguments = context.GetArguments(this._MethodInfo, keys); if (!this._MethodInfo.IsStatic) { var controller = context.RequestServices!.GetRequiredService(this._MethodInfo.DeclaringType!); - arguments = arguments.Prepend(controller); + arguments = [controller, ..arguments]; } var result = this._MethodInfo.InvokeMethod(arguments.ToArray()); return result switch @@ -85,10 +79,10 @@ private Task> GetData(IResolveFieldContext context, IEnumerab ValueTask> valueTask => valueTask.AsTask(), ValueTask valueTask => Task.Run>(async () => (IEnumerable)await valueTask), Task> task => task, - Task valueTask => Task.Run>(async () => (IEnumerable)await valueTask), + Task task => Task.Run>(async () => (IEnumerable)await task), IAsyncEnumerable items => Task.FromResult(items.ToBlockingEnumerable()), IEnumerable items => Task.FromResult(items), - _ => Task.FromResult((IEnumerable)Array.Empty) + _ => Task.FromResult>(Array.Empty) }; } } diff --git a/src/TypeCache.GraphQL/Resolvers/ItemLoaderFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/ItemLoaderFieldResolver.cs index 1d820752..3f0f6850 100644 --- a/src/TypeCache.GraphQL/Resolvers/ItemLoaderFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/ItemLoaderFieldResolver.cs @@ -9,22 +9,27 @@ using TypeCache.Extensions; using TypeCache.GraphQL.Extensions; using static System.FormattableString; +using IResolveFieldContext = global::GraphQL.IResolveFieldContext; namespace TypeCache.GraphQL.Resolvers; public sealed class ItemLoaderFieldResolver : FieldResolver { - private readonly MethodInfo _MethodInfo; + private readonly RuntimeMethodHandle _MethodHandle; + private readonly string _Name; /// public ItemLoaderFieldResolver(MethodInfo methodInfo) { + methodInfo.IsStatic.AssertTrue(); methodInfo.ReturnType.IsAny, ValueTask>().AssertTrue(); - this._MethodInfo = methodInfo; + + this._MethodHandle = methodInfo.MethodHandle; + this._Name = methodInfo.GraphQLName(); } /// - protected override async ValueTask ResolveAsync(global::GraphQL.IResolveFieldContext context) + protected override async ValueTask ResolveAsync(IResolveFieldContext context) { context.RequestServices.AssertNotNull(); context.Source.AssertNotNull(); @@ -33,22 +38,23 @@ public ItemLoaderFieldResolver(MethodInfo methodInfo) var dataLoaderContext = dataLoaderAccessor.Context; dataLoaderContext.AssertNotNull(); - var loaderKey = Invariant($"{context.Source.GetType().GraphQLName()}.{this._MethodInfo.GraphQLName()}"); - var dataLoader = dataLoaderContext.GetOrAddLoader(loaderKey, () => - { - var arguments = context.GetArguments(this._MethodInfo).ToArray(); - var sourceType = !this._MethodInfo.IsStatic ? this._MethodInfo.DeclaringType : null; - var controller = sourceType is not null ? context.RequestServices.GetRequiredService(sourceType) : null; - var result = this._MethodInfo.InvokeMethod(controller, arguments); - return result switch - { - ValueTask valueTask => valueTask.AsTask(), - Task task => task, - T item => Task.FromResult(item), - _ => Task.FromResult(default(T))! - }; - }); + var loaderKey = Invariant($"{context.Source.GetType().GraphQLName()}.{this._Name}"); + var dataLoader = dataLoaderContext.GetOrAddLoader(loaderKey, () => this.LoadData(context)); return await dataLoader.LoadAsync().GetResultAsync(context.CancellationToken); } + + private Task LoadData(IResolveFieldContext context) + { + var methodInfo = (MethodInfo)this._MethodHandle.ToMethodBase(); + var arguments = context.GetArguments(methodInfo).ToArray(); + var result = methodInfo.InvokeMethod(arguments); + return result switch + { + ValueTask valueTask => valueTask.AsTask(), + Task task => task, + T item => Task.FromResult(item), + _ => Task.FromResult(default(T))! + }; + } } diff --git a/src/TypeCache.GraphQL/Resolvers/MethodFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/MethodFieldResolver.cs index 6a743eab..5b707546 100644 --- a/src/TypeCache.GraphQL/Resolvers/MethodFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/MethodFieldResolver.cs @@ -6,32 +6,29 @@ using Microsoft.Extensions.DependencyInjection; using TypeCache.Extensions; using TypeCache.GraphQL.Extensions; +using IResolveFieldContext = global::GraphQL.IResolveFieldContext; namespace TypeCache.GraphQL.Resolvers; -public sealed class MethodFieldResolver : FieldResolver +public sealed class MethodFieldResolver(MethodInfo methodInfo) : FieldResolver { - private readonly MethodInfo _MethodInfo; - - public MethodFieldResolver(MethodInfo methodInfo) - { - this._MethodInfo = methodInfo; - } - - protected override ValueTask ResolveAsync(global::GraphQL.IResolveFieldContext context) + protected override async ValueTask ResolveAsync(IResolveFieldContext context) { context.RequestServices.AssertNotNull(); - var sourceType = !this._MethodInfo.IsStatic ? this._MethodInfo.DeclaringType : null; - var controller = sourceType is not null ? context.RequestServices.GetRequiredService(sourceType) : null; - var arguments = context.GetArguments(sourceType, this._MethodInfo).ToArray(); - var result = this._MethodInfo.InvokeMethod(controller, arguments); + var arguments = context.GetArguments(methodInfo).ToArray(); + if (!methodInfo.IsStatic) + { + var controller = context.RequestServices.GetRequiredService(methodInfo.DeclaringType!); + arguments = [controller, ..arguments]; + } + var result = methodInfo.InvokeMethod(arguments); return result switch { - ValueTask valueTask => valueTask, - Task task => new ValueTask(task), - _ => ValueTask.FromResult(result) + ValueTask valueTask => await valueTask, + Task task => await task, + _ => result }; } } diff --git a/src/TypeCache.GraphQL/Resolvers/MethodSourceStreamResolver.cs b/src/TypeCache.GraphQL/Resolvers/MethodSourceStreamResolver.cs index 6467d2db..41bb472e 100644 --- a/src/TypeCache.GraphQL/Resolvers/MethodSourceStreamResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/MethodSourceStreamResolver.cs @@ -35,10 +35,12 @@ public MethodSourceStreamResolver(MethodInfo methodInfo) { context.RequestServices.AssertNotNull(); - var sourceType = !this._MethodInfo.IsStatic ? this._MethodInfo.DeclaringType : null; - var controller = sourceType is not null ? context.RequestServices.GetRequiredService(sourceType) : null; - var arguments = context.GetArguments(this._MethodInfo).ToArray(); - var result = this._MethodInfo.InvokeMethod(controller, arguments); + var controller = !this._MethodInfo.IsStatic ? context.RequestServices.GetRequiredService(this._MethodInfo.DeclaringType!) : null; + var arguments = context.GetArguments(this._MethodInfo).ToArray(); + if (!this._MethodInfo.IsStatic) + arguments = [controller, .. arguments]; + + var result = this._MethodInfo.InvokeMethod(arguments); return result switch { diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiDeleteFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiDeleteFieldResolver.cs index cad9b34f..51ee5a53 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiDeleteFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiDeleteFieldResolver.cs @@ -29,8 +29,8 @@ public sealed class SqlApiDeleteFieldResolver : FieldResolver .Where(column => selections.Any(_ => _.Left(Invariant($"output.{column.Name}")))) .Select(column => objectSchema.DataSource.Type switch { - PostgreSql => objectSchema.DataSource.EscapeIdentifier(column.Name), - _ or SqlServer => Invariant($"DELETED.{objectSchema.DataSource.EscapeIdentifier(column.Name)}") + PostgreSql => column.Name.EscapeIdentifier(objectSchema.DataSource.Type), + _ or SqlServer => Invariant($"DELETED.{column.Name.EscapeIdentifier(objectSchema.DataSource.Type)}") }) .ToArray(); var data = context.GetArgumentAsDataTable("data", objectSchema); @@ -69,8 +69,8 @@ public sealed class SqlApiDeleteFieldResolver : FieldResolver .Where(column => selections.Any(_ => _.Left(Invariant($"output.{column.Name}")))) .Select(column => objectSchema.DataSource.Type switch { - PostgreSql => objectSchema.DataSource.EscapeIdentifier(column.Name), - _ or SqlServer => Invariant($"DELETED.{objectSchema.DataSource.EscapeIdentifier(column.Name)}") + PostgreSql => column.Name.EscapeIdentifier(objectSchema.DataSource.Type), + _ or SqlServer => Invariant($"DELETED.{column.Name.EscapeIdentifier(objectSchema.DataSource.Type)}") }) .ToArray(); var data = context.GetArgument("data"); diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiInsertFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiInsertFieldResolver.cs index ac763433..ff747488 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiInsertFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiInsertFieldResolver.cs @@ -30,8 +30,8 @@ public sealed class SqlApiInsertFieldResolver : FieldResolver .Where(column => selections.Any(_ => _.Left(Invariant($"output.{column.Name}")))) .Select(column => objectSchema.DataSource.Type switch { - PostgreSql => objectSchema.DataSource.EscapeIdentifier(column.Name), - _ or SqlServer => Invariant($"INSERTED.{objectSchema.DataSource.EscapeIdentifier(column.Name)}") + PostgreSql => column.Name.EscapeIdentifier(objectSchema.DataSource.Type), + _ or SqlServer => Invariant($"INSERTED.{column.Name.EscapeIdentifier(objectSchema.DataSource.Type)}") }) .ToArray(); var columns = context.GetArgument("columns"); @@ -49,7 +49,7 @@ public sealed class SqlApiInsertFieldResolver : FieldResolver .Where(column => select.Any(_ => _.Right(Invariant($"{nameof(SelectQuery.Select)}.{column.Name}")))) .Select(column => column.Name) .ToArray(), - TableHints = objectSchema.DataSource.Type is SqlServer ? "WITH(NOLOCK)" : null, + TableHints = objectSchema.DataSource.Type is SqlServer ? "NOLOCK" : null, Top = context.GetArgument(nameof(SelectQuery.Top)), Where = context.GetArgument(nameof(SelectQuery.Where)) }, output); @@ -87,8 +87,8 @@ public sealed class SqlApiInsertFieldResolver : FieldResolver .Where(column => selections.Any(_ => _.Left(Invariant($"output.{column.Name}")))) .Select(column => objectSchema.DataSource.Type switch { - PostgreSql => objectSchema.DataSource.EscapeIdentifier(column.Name), - _ or SqlServer => Invariant($"INSERTED.{objectSchema.DataSource.EscapeIdentifier(column.Name)}") + PostgreSql => column.Name.EscapeIdentifier(objectSchema.DataSource.Type), + _ or SqlServer => Invariant($"INSERTED.{column.Name.EscapeIdentifier(objectSchema.DataSource.Type)}") }) .ToArray(); var columns = context.GetArgument("columns"); @@ -106,7 +106,7 @@ public sealed class SqlApiInsertFieldResolver : FieldResolver .Where(column => select.Any(_ => _.Right(Invariant($"{nameof(SelectQuery.Select)}.{column.Name}")))) .Select(column => column.Name) .ToArray(), - TableHints = objectSchema.DataSource.Type is SqlServer ? "WITH(NOLOCK)" : null, + TableHints = objectSchema.DataSource.Type is SqlServer ? "NOLOCK" : null, Top = context.GetArgument(nameof(SelectQuery.Top)), Where = context.GetArgument(nameof(SelectQuery.Where)) }, output); diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs index 0a3ec393..ceacdea5 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs @@ -36,7 +36,7 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver || _.Right(Invariant($"{nameof(Connection.Edges)}.{nameof(Edge.Node)}.{column.Name}")))) .Select(column => column.Name) .ToArray(), - TableHints = objectSchema.DataSource.Type is SqlServer ? "WITH(NOLOCK)" : null, + TableHints = objectSchema.DataSource.Type is SqlServer ? "NOLOCK" : null, Top = context.GetArgument(nameof(SelectQuery.Top)), Where = context.GetArgument(nameof(SelectQuery.Where)) }; @@ -112,7 +112,7 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver || _.Right(Invariant($"{nameof(Connection.Edges)}.{nameof(Edge.Node)}.{column.Name}")))) .Select(column => column.Name) .ToArray(), - TableHints = objectSchema.DataSource.Type is SqlServer ? "WITH(NOLOCK)" : null, + TableHints = objectSchema.DataSource.Type is SqlServer ? "NOLOCK" : null, Top = context.GetArgument(nameof(SelectQuery.Top)), Where = context.GetArgument(nameof(SelectQuery.Where)) }; diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiUpdateFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiUpdateFieldResolver.cs index d9465eb2..2e2cab3b 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiUpdateFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiUpdateFieldResolver.cs @@ -31,8 +31,8 @@ public sealed class SqlApiUpdateFieldResolver : FieldResolver .Where(column => selections.Any(_ => _.Left(Invariant($"output.{column.Name}")))) .Select(column => objectSchema.DataSource.Type switch { - PostgreSql => objectSchema.DataSource.EscapeIdentifier(column.Name), - _ or SqlServer => Invariant($"INSERTED.{objectSchema.DataSource.EscapeIdentifier(column.Name)}") + PostgreSql => column.Name.EscapeIdentifier(objectSchema.DataSource.Type), + _ or SqlServer => Invariant($"INSERTED.{column.Name.EscapeIdentifier(objectSchema.DataSource.Type)}") }) .ToArray(); var data = context.GetArgumentAsDataTable("data", objectSchema); @@ -86,8 +86,8 @@ public sealed class SqlApiUpdateFieldResolver : FieldResolver .Where(column => selections.Any(_ => _.Left(Invariant($"output.{column.Name}")))) .Select(column => objectSchema.DataSource.Type switch { - PostgreSql => objectSchema.DataSource.EscapeIdentifier(column.Name), - _ or SqlServer => Invariant($"INSERTED.{objectSchema.DataSource.EscapeIdentifier(column.Name)}") + PostgreSql => column.Name.EscapeIdentifier(objectSchema.DataSource.Type), + _ or SqlServer => Invariant($"INSERTED.{column.Name.EscapeIdentifier(objectSchema.DataSource.Type)}") }) .ToArray(); var data = context.GetArgument("data"); diff --git a/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj b/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj index e67039b7..25a179cb 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.1.0 + 8.1.3 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache GraphQL @@ -35,8 +35,8 @@ Automatic generation of SQL related endpoints. - - + + diff --git a/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs b/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs index 5ca4c387..e1aa5604 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs @@ -10,9 +10,7 @@ namespace TypeCache.GraphQL.Types; -/// /// -/// public sealed class GraphQLEnumType : EnumerationGraphType where T : struct, Enum { @@ -41,13 +39,18 @@ _ when Enum.Attributes.TryFirst(out var attribute) => at .ForEach(this.Add); } + /// + public override object? Serialize(object? value) + => value is not null ? (T)value : null; + /// public override bool CanParseValue(object? value) => value switch { null => true, - T token => Enum.IsDefined(token), + T token => Enum.IsValid(token), string text => Enum.TryParse(text, true, out _), + _ when value.GetType() == typeof(T).GetEnumUnderlyingType() => true, _ => false }; @@ -56,7 +59,7 @@ public override bool CanParseValue(object? value) => value switch { null => null, - T token when Enum.IsDefined(token) => token, + T token when Enum.IsValid(token) => token, T token => null, string text => Enum.Parse(text, true), _ => Enum.ToObject(typeof(T), value) diff --git a/src/TypeCache.GraphQL/Types/GraphQLObjectType.cs b/src/TypeCache.GraphQL/Types/GraphQLObjectType.cs index f9cec24f..fabf18d3 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLObjectType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLObjectType.cs @@ -49,14 +49,17 @@ public GraphQLObjectType(string name) /// Adds a field that is based on the result of the .
/// The name of the field is the name or of the . /// + /// The must be a static method. + /// + /// public FieldType AddField(MethodInfo methodInfo) { var type = methodInfo.ReturnType; - if (type.IsGenericType && type.IsAny(typeof(Task<>), typeof(ValueTask<>))) + if (type.IsGenericType && type.IsAny([typeof(Task<>), typeof(ValueTask<>)])) type = type.GenericTypeArguments[0]; var resolverType = typeof(ItemLoaderFieldResolver<>).MakeGenericType(type); - var resolver = (IFieldResolver)resolverType.Create(methodInfo)!; + var resolver = (IFieldResolver)resolverType.Create([methodInfo])!; return this.AddField(methodInfo, resolver); } diff --git a/src/TypeCache.Web/Attributes/RequireClaimAttribute.cs b/src/TypeCache.Web/Attributes/RequireClaimAttribute.cs index 10ad8203..30a5b863 100644 --- a/src/TypeCache.Web/Attributes/RequireClaimAttribute.cs +++ b/src/TypeCache.Web/Attributes/RequireClaimAttribute.cs @@ -18,7 +18,7 @@ public class RequireClaimAttribute : AuthorizeAttribute { public IDictionary Claims { get; } - public RequireClaimAttribute(params string[] claims) : base(nameof(ClaimAuthorizationRequirement)) + public RequireClaimAttribute(string[] claims) : base(nameof(ClaimAuthorizationRequirement)) { const char separator = '='; @@ -35,7 +35,7 @@ public RequireClaimAttribute(params string[] claims) : base(nameof(ClaimAuthoriz if (key.IsNotBlank()) { if (this.Claims.TryGetValue(key, out var values)) - this.Claims[key] = values.Append(value).ToArray()!; + this.Claims[key] = [..values, value!]; else this.Claims.Add(key!, [value!]); } diff --git a/src/TypeCache.Web/Attributes/RequireHeaderAttribute.cs b/src/TypeCache.Web/Attributes/RequireHeaderAttribute.cs index 28c5b5d9..23d63ef6 100644 --- a/src/TypeCache.Web/Attributes/RequireHeaderAttribute.cs +++ b/src/TypeCache.Web/Attributes/RequireHeaderAttribute.cs @@ -15,7 +15,7 @@ public class RequireHeaderAttribute : AuthorizeAttribute public string Key { get; set; } - public RequireHeaderAttribute(string key, params string[] allowedValues) : base(nameof(HeaderAuthorizationRequirement)) + public RequireHeaderAttribute(string key, string[] allowedValues) : base(nameof(HeaderAuthorizationRequirement)) { key.AssertNotBlank(); allowedValues?.ForEach(allowedValue => allowedValue.AssertNotBlank()); diff --git a/src/TypeCache.Web/Extensions/AuthorizationOptionsExtensions.cs b/src/TypeCache.Web/Extensions/AuthorizationOptionsExtensions.cs index 52c748f5..b0e27af4 100644 --- a/src/TypeCache.Web/Extensions/AuthorizationOptionsExtensions.cs +++ b/src/TypeCache.Web/Extensions/AuthorizationOptionsExtensions.cs @@ -12,7 +12,7 @@ namespace TypeCache.Web.Extensions; public static class AuthorizationOptionsExtensions { - private static AuthorizationPolicy AddAuthorizationPolicy(this AuthorizationOptions @this, IAuthorizationRequirement requirement, params string[]? authenticationSchemas) + private static AuthorizationPolicy AddAuthorizationPolicy(this AuthorizationOptions @this, IAuthorizationRequirement requirement, string[]? authenticationSchemas) { var builder = new AuthorizationPolicyBuilder(); if (authenticationSchemas?.Any() is true) @@ -26,10 +26,10 @@ private static AuthorizationPolicy AddAuthorizationPolicy(this AuthorizationOpti } [MethodImpl(AggressiveInlining), DebuggerHidden] - public static AuthorizationPolicy AddClaimAuthorizationPolicy(this AuthorizationOptions @this, params string[]? authenticationSchemas) + public static AuthorizationPolicy AddClaimAuthorizationPolicy(this AuthorizationOptions @this, string[]? authenticationSchemas) => @this.AddAuthorizationPolicy(new ClaimAuthorizationRequirement(), authenticationSchemas); [MethodImpl(AggressiveInlining), DebuggerHidden] - public static AuthorizationPolicy AddHeaderAuthorizationPolicy(this AuthorizationOptions @this, params string[]? authenticationSchemas) + public static AuthorizationPolicy AddHeaderAuthorizationPolicy(this AuthorizationOptions @this, string[]? authenticationSchemas) => @this.AddAuthorizationPolicy(new HeaderAuthorizationRequirement(), authenticationSchemas); } diff --git a/src/TypeCache.Web/Extensions/ClaimsPrincipalExtensions.cs b/src/TypeCache.Web/Extensions/ClaimsPrincipalExtensions.cs index a2988c6f..2c761544 100644 --- a/src/TypeCache.Web/Extensions/ClaimsPrincipalExtensions.cs +++ b/src/TypeCache.Web/Extensions/ClaimsPrincipalExtensions.cs @@ -8,7 +8,7 @@ namespace TypeCache.Web.Extensions; public static class ClaimsPrincipalExtensions { - public static bool Any(this ClaimsPrincipal @this, string claimType, params string[] values) + public static bool Any(this ClaimsPrincipal @this, string claimType, string[] values) { claimType.AssertNotBlank(); diff --git a/src/TypeCache.Web/Extensions/EndpointRouteBuilderExtensions.cs b/src/TypeCache.Web/Extensions/EndpointRouteBuilderExtensions.cs index e71fe759..8bdedc07 100644 --- a/src/TypeCache.Web/Extensions/EndpointRouteBuilderExtensions.cs +++ b/src/TypeCache.Web/Extensions/EndpointRouteBuilderExtensions.cs @@ -52,7 +52,7 @@ public static IEndpointConventionBuilder MapGetRequestHeaders(this IEndpointRout var responseBuilder = new StringBuilder(); foreach (var pair in context.Request.Headers) { - responseBuilder.Append(Invariant($"{pair.Key}: {pair.Value.ToString()}
")); + responseBuilder.Append(Invariant($"{pair.Key}: {pair.Value}
")); } response = responseBuilder.ToString(); @@ -119,9 +119,9 @@ public static IEndpointConventionBuilder MapGetRequestHeaderValue(this IEndpoint }; response = (context.Response.StatusCode, acceptRequestHeader) switch { - (StatusCodes.Status200OK, Text.Plain) => Invariant($"{key}: {value.ToString()}"), + (StatusCodes.Status200OK, Text.Plain) => Invariant($"{key}: {value}"), (StatusCodes.Status204NoContent, Text.Plain) => Invariant($"{key}: "), - (StatusCodes.Status200OK, Text.Html) => Invariant($"

{key}


{value.ToString()}"), + (StatusCodes.Status200OK, Text.Html) => Invariant($"

{key}


{value}"), (StatusCodes.Status204NoContent, Text.Html) => Invariant($"

{key}


"), (StatusCodes.Status200OK, Application.Xml or Text.Xml) => new XDocument(new XElement(key.Replace(' ', '_'), value.ToString())).ToString(), (StatusCodes.Status204NoContent, Application.Xml or Text.Xml) => new XDocument(new XElement(key.Replace(' ', '_'))).ToString(), diff --git a/src/TypeCache.Web/Handlers/SqlApiHandler.cs b/src/TypeCache.Web/Handlers/SqlApiHandler.cs index 18fedb28..cb85e186 100644 --- a/src/TypeCache.Web/Handlers/SqlApiHandler.cs +++ b/src/TypeCache.Web/Handlers/SqlApiHandler.cs @@ -69,7 +69,7 @@ HttpContext httpContext , [FromQuery] string where) { var objectSchema = httpContext.GetObjectSchema(); - var sql = objectSchema.CreateDeleteSQL(where, output); + var sql = objectSchema.CreateDeleteSQL(where, [output]); var sqlCommand = objectSchema.DataSource.CreateSqlCommand(sql); foreach (var pair in httpContext.Request.Query.Where(pair => pair.Key.StartsWith('@'))) @@ -129,7 +129,7 @@ HttpContext httpContext , [FromBody] SelectQuery selectQuery) { var objectSchema = httpContext.GetObjectSchema(); - var sql = objectSchema.CreateInsertSQL(columns.Split(','), selectQuery, output); + var sql = objectSchema.CreateInsertSQL(columns.Split(','), selectQuery, [output]); var sqlCommand = objectSchema.DataSource.CreateSqlCommand(sql); foreach (var pair in httpContext.Request.Query.Where(pair => pair.Key.StartsWith('@'))) @@ -160,7 +160,7 @@ HttpContext httpContext , [FromBody] JsonArray data) { var objectSchema = httpContext.GetObjectSchema(); - var sql = objectSchema.CreateInsertSQL(data, output); + var sql = objectSchema.CreateInsertSQL(data, [output]); var sqlCommand = objectSchema.DataSource.CreateSqlCommand(sql); var mediator = httpContext.GetMediator(); @@ -188,7 +188,7 @@ HttpContext httpContext , [FromQuery] string where) { var objectSchema = httpContext.GetObjectSchema(); - return Results.Text(objectSchema.CreateDeleteSQL(where, output), Text.Plain, UTF8); + return Results.Text(objectSchema.CreateDeleteSQL(where, [output]), Text.Plain, UTF8); } public static IResult GetDeleteBatchSQL( @@ -253,7 +253,7 @@ HttpContext httpContext Top = top, Where = where }; - var sql = objectSchema.CreateInsertSQL(columns.Split(','), selectQuery, output); + var sql = objectSchema.CreateInsertSQL(columns.Split(','), selectQuery, [output]); return Results.Text(sql, Text.Plain, UTF8); } @@ -267,7 +267,7 @@ HttpContext httpContext , [FromBody] JsonArray data) { var objectSchema = httpContext.GetObjectSchema(); - var sql = objectSchema.CreateInsertSQL(data, output); + var sql = objectSchema.CreateInsertSQL(data, [output]); return Results.Text(sql, Text.Plain, UTF8); } @@ -339,7 +339,7 @@ HttpContext httpContext , [FromQuery] string where) { var objectSchema = httpContext.GetObjectSchema(); - var sql = objectSchema.CreateUpdateSQL(set.Split(','), where, output); + var sql = objectSchema.CreateUpdateSQL(set.Split(','), where, [output]); return Results.Text(sql, Text.Plain, UTF8); } @@ -353,7 +353,7 @@ HttpContext httpContext , [FromBody] JsonArray data) { var objectSchema = httpContext.GetObjectSchema(); - var sql = objectSchema.CreateUpdateSQL(data, output); + var sql = objectSchema.CreateUpdateSQL(data, [output]); return Results.Text(sql, Text.Plain, UTF8); } @@ -419,7 +419,7 @@ HttpContext httpContext , [FromQuery] string where) { var objectSchema = httpContext.GetObjectSchema(); - var sql = objectSchema.CreateUpdateSQL(set.Split(','), where, output); + var sql = objectSchema.CreateUpdateSQL(set.Split(','), where, [output]); var sqlCommand = objectSchema.DataSource.CreateSqlCommand(sql); foreach (var pair in httpContext.Request.Query.Where(pair => pair.Key.StartsWith('@'))) @@ -450,7 +450,7 @@ HttpContext httpContext , [FromBody] JsonArray data) { var objectSchema = httpContext.GetObjectSchema(); - var sql = objectSchema.CreateUpdateSQL(data, output); + var sql = objectSchema.CreateUpdateSQL(data, [output]); var sqlCommand = objectSchema.DataSource.CreateSqlCommand(sql); var mediator = httpContext.GetMediator(); diff --git a/src/TypeCache.Web/TypeCache.Web.csproj b/src/TypeCache.Web/TypeCache.Web.csproj index 0379df7f..ef96ad48 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.1.1 + 8.1.3 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache Web Library diff --git a/src/TypeCache/Collections/EnumComparer.cs b/src/TypeCache/Collections/EnumComparer.cs index 3c7278e1..976d1eb8 100644 --- a/src/TypeCache/Collections/EnumComparer.cs +++ b/src/TypeCache/Collections/EnumComparer.cs @@ -22,7 +22,7 @@ static EnumComparer() private static Comparison CreateCompare(Type underlyingType) => LambdaFactory.CreateComparison((value1, value2) => - value1.Cast(underlyingType).Call(nameof(IComparable.CompareTo), value2.Cast(underlyingType))).Compile(); + value1.Cast(underlyingType).Call(nameof(IComparable.CompareTo), [value2.Cast(underlyingType)])).Compile(); private static Func CreateEquals(Type underlyingType) => LambdaFactory.CreateFunc((value1, value2) => diff --git a/src/TypeCache/Data/DataSource.cs b/src/TypeCache/Data/DataSource.cs index 56c71bea..0f1ae4c1 100644 --- a/src/TypeCache/Data/DataSource.cs +++ b/src/TypeCache/Data/DataSource.cs @@ -131,39 +131,21 @@ 2 when databaseObject.Contains("..") => this.CreateName(items[0], this.DefaultSc public DatabaseObject CreateName(string schema, string 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)}")) + MySql => new(Invariant($"{schema.EscapeIdentifier(this.Type)}.{objectName.EscapeIdentifier(this.Type)}")), + _ => new(Invariant($"{this.DefaultDatabase.EscapeIdentifier(this.Type)}.{schema.EscapeIdentifier(this.Type)}.{objectName.EscapeIdentifier(this.Type)}")) }; public DatabaseObject CreateName(string database, string schema, string objectName) { this.Databases.Contains(database).AssertTrue(); - return new(Invariant($"{this.EscapeIdentifier(database)}.{this.EscapeIdentifier(schema)}.{this.EscapeIdentifier(objectName)}")); + return new(Invariant($"{database.EscapeIdentifier(this.Type)}.{schema.EscapeIdentifier(this.Type)}.{objectName.EscapeIdentifier(this.Type)}")); } [MethodImpl(AggressiveInlining), DebuggerHidden] public SqlCommand CreateSqlCommand(string sql) => new(this, sql); - [DebuggerHidden] - public string EscapeIdentifier([NotNull] string identifier) - => this.Type switch - { - SqlServer => Invariant($"[{identifier.Replace("]", "]]")}]"), - Oracle or PostgreSql => Invariant($"\"{identifier.Replace("\"", "\"\"")}\""), - MySql => Invariant($"`{identifier.Replace("`", "``")}`"), - _ => identifier - }; - - [MethodImpl(AggressiveInlining), DebuggerHidden] - public string EscapeLikeValue([NotNull] string text) - => text.Replace("'", "''").Replace("[", "[[]").Replace("%", "[%]").Replace("_", "[_]"); - - [MethodImpl(AggressiveInlining), DebuggerHidden] - public string EscapeValue([NotNull] string text) - => text.Replace("'", "''"); - public async Task GetDatabaseSchemaAsync(string? database = null, CancellationToken token = default) { var schemaSet = new DataSet(SchemaCollection.MetaDataCollections.Name()); @@ -249,7 +231,6 @@ private IReadOnlyDictionary GetObjectSchemas() connection.Open(); using var command = connection.CreateCommand(); - command.Connection = connection; command.CommandType = CommandType.Text; using var adapter = this.Factory.CreateDataAdapter()!; diff --git a/src/TypeCache/Data/Extensions/SqlExtensions.cs b/src/TypeCache/Data/Extensions/SqlExtensions.cs index 882a3d58..5276eaa4 100644 --- a/src/TypeCache/Data/Extensions/SqlExtensions.cs +++ b/src/TypeCache/Data/Extensions/SqlExtensions.cs @@ -2,14 +2,32 @@ using System.Collections; using System.Data; +using System.Diagnostics; using System.Text.Json; using System.Text.Json.Nodes; using TypeCache.Extensions; +using static TypeCache.Data.DataSourceType; namespace TypeCache.Data.Extensions; public static class SqlExtensions { + public static string EscapeIdentifier([NotNull] this string @this, DataSourceType type) + => type switch + { + SqlServer => Invariant($"[{@this.Replace("]", "]]")}]"), + Oracle or PostgreSql => Invariant($"\"{@this.Replace("\"", "\"\"")}\""), + MySql => Invariant($"`{@this.Replace("`", "``")}`"), + _ => @this + }; + + /// + /// => .EscapeValue(@).Replace("[", "[[]").Replace("%", "[%]").Replace("_", "[_]"); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static string EscapeLikeValue([NotNull] this string @this) + => @this.Replace("'", "''").Replace("[", "[[]").Replace("%", "[%]").Replace("_", "[_]"); + /// /// => @.Replace("'", "''"); /// diff --git a/src/TypeCache/Data/Extensions/StringBuilderExtensions.cs b/src/TypeCache/Data/Extensions/StringBuilderExtensions.cs index 79ef373f..d50b52e6 100644 --- a/src/TypeCache/Data/Extensions/StringBuilderExtensions.cs +++ b/src/TypeCache/Data/Extensions/StringBuilderExtensions.cs @@ -14,8 +14,9 @@ public static class StringBuilderExtensions public static StringBuilder AppendOutputSQL(this StringBuilder @this, DataSourceType dataSourceType, StringValues output) => @this.AppendLine(dataSourceType switch { - PostgreSql => Invariant($"RETURNING {output.ToCSV()}"), - _ => Invariant($"OUTPUT {output.ToCSV()}") + SqlServer or PostgreSql => Invariant($"OUTPUT {output.ToCSV()}"), + Oracle => Invariant($"RETURNING {output.ToCSV()}"), + _ => string.Empty }); public static StringBuilder AppendStatementEndSQL(this StringBuilder @this) diff --git a/src/TypeCache/Data/IDataSource.cs b/src/TypeCache/Data/IDataSource.cs index 592a7e07..c2274ca7 100644 --- a/src/TypeCache/Data/IDataSource.cs +++ b/src/TypeCache/Data/IDataSource.cs @@ -49,18 +49,6 @@ public interface IDataSource : IEquatable SqlCommand CreateSqlCommand(string sql); - string EscapeIdentifier([NotNull] string identifier); - - /// - /// => .EscapeValue().Replace("[", "[[]").Replace("%", "[%]").Replace("_", "[_]"); - /// - string EscapeLikeValue([NotNull] string text); - - /// - /// => .Replace("'", "''"); - /// - string EscapeValue([NotNull] string text); - Task GetDatabaseSchemaAsync(string? database = null, CancellationToken token = default); Task GetDatabaseSchemaAsync(SchemaCollection collection, string? database = null, CancellationToken token = default); diff --git a/src/TypeCache/Data/ObjectSchema.cs b/src/TypeCache/Data/ObjectSchema.cs index cf0f97ea..20c53e71 100644 --- a/src/TypeCache/Data/ObjectSchema.cs +++ b/src/TypeCache/Data/ObjectSchema.cs @@ -56,106 +56,132 @@ public DataTable CreateDataTable() } public string CreateCountSQL(string? distinctColumn, string where) - => new StringBuilder() - .AppendLineIf(distinctColumn.IsBlank(), "SELECT COUNT(*)", Invariant($"SELECT COUNT(DISTINCT {(distinctColumn.IsNotBlank() ? this.DataSource.EscapeIdentifier(distinctColumn!) : null)})")) - .AppendLineIf(this.DataSource.Type is not SqlServer, Invariant($"FROM {this.Name}"), Invariant($"FROM {this.Name} WITH(NOLOCK)")) + => new StringBuilder("SELECT ") + .AppendLineIf(distinctColumn.IsBlank(), "COUNT(*)", Invariant($"COUNT(DISTINCT {(distinctColumn.IsNotBlank() ? distinctColumn?.EscapeIdentifier(this.DataSource.Type!) : null)})")) + .Append("FROM ").Append(this.Name).AppendIf(this.DataSource.Type is SqlServer, " WITH(NOLOCK)").AppendLine() .AppendLineIf(where.IsNotBlank(), Invariant($"WHERE {where}")) .AppendStatementEndSQL() .ToString(); - public string CreateDeleteSQL(string where, params string[] output) - => new StringBuilder() - .AppendLine(Invariant($"DELETE {this.Name}")) + public string CreateDeleteSQL(string where, string[] output) + => new StringBuilder("DELETE ").AppendLine(this.Name) .AppendOutputSQL(this.DataSource.Type, output) .AppendLineIf(where.IsNotBlank(), Invariant($"WHERE {where}")) .AppendStatementEndSQL() .ToString(); - public string CreateDeleteSQL(DataTable data, params string[] output) + /// + public string CreateDeleteSQL(DataTable data, string[] output) { - var columns = data.PrimaryKey.OfType().Select(column => this.DataSource.EscapeIdentifier(column.ColumnName)); + var primaryKeys = this.Columns.Where(column => column.PrimaryKey).ToArray(); - return new StringBuilder() - .AppendLine(Invariant($"DELETE {this.Name}")) + (primaryKeys.Length > 0).AssertTrue(); + + if (primaryKeys.Length == 1) + { + var values = data.Rows.Cast().Select(row => row[primaryKeys[0].Name].ToSQL()).ToCSV(); + var column = primaryKeys[0].Name.EscapeIdentifier(this.DataSource.Type); + return new StringBuilder("DELETE FROM ").AppendLine(this.Name) + .AppendOutputSQL(this.DataSource.Type, output) + .Append("WHERE ").Append(column).Append(" IN (").Append(values).Append(')') + .AppendStatementEndSQL() + .ToString(); + } + + var conditions = data.Rows.Cast().Select(row => Invariant($"({string.Join(" AND ", primaryKeys.Select(column => + Invariant($"{column.Name.EscapeIdentifier(this.DataSource.Type)} = {row[column.Name].ToSQL()}")))})")); + + return new StringBuilder("DELETE FROM ").AppendLine(this.Name) .AppendOutputSQL(this.DataSource.Type, output) - .AppendLine(Invariant($"FROM {this.Name} AS _")) - .AppendLine("INNER JOIN") - .Append('(').AppendLine() - .Append(data.ToSQL()) - .AppendLine(Invariant($") AS data ({data.Columns.OfType().Select(column => this.DataSource.EscapeIdentifier(column.ColumnName)).ToCSV()})")) - .Append("ON ").AppendJoin(" AND ", columns.Select(column => Invariant($"data.{column} = _.{column}"))).AppendLine() + .Append("WHERE ").AppendJoin(" OR ", conditions) .AppendStatementEndSQL() .ToString(); } + /// public string CreateDeleteSQL(JsonArray data, StringValues output) { - var columns = this.Columns.Where(column => column.PrimaryKey).Select(column => this.DataSource.EscapeIdentifier(column.Name)); + var primaryKeys = this.Columns.Where(column => column.PrimaryKey).ToArray(); + + (primaryKeys.Length > 0).AssertTrue(); - return new StringBuilder() - .AppendLine(Invariant($"DELETE {this.Name}")) + if (primaryKeys.Length == 1) + { + var values = data.Select(row => row![primaryKeys[0].Name].ToSQL()).ToCSV(); + var column = primaryKeys[0].Name.EscapeIdentifier(this.DataSource.Type); + return new StringBuilder("DELETE FROM ").AppendLine(this.Name) + .AppendOutputSQL(this.DataSource.Type, output) + .Append("WHERE ").Append(column).Append(" IN (").Append(values).Append(')') + .AppendStatementEndSQL() + .ToString(); + } + + var conditions = data.Select(row => Invariant($"({string.Join(" AND ", primaryKeys.Select(column => + Invariant($"{column.Name.EscapeIdentifier(this.DataSource.Type)} = {row![column.Name].ToSQL()}")))})")); + + return new StringBuilder("DELETE FROM ").AppendLine(this.Name) .AppendOutputSQL(this.DataSource.Type, output) - .AppendLine(Invariant($"FROM {this.Name} AS _")) - .AppendLine("INNER JOIN") - .Append('(').AppendLine() - .Append(data.ToSQL()) - .AppendLine(Invariant($") AS data ({data[0]!.AsObject().Select(pair => this.DataSource.EscapeIdentifier(pair.Key)).ToCSV()})")) - .Append("ON ").AppendJoin(" AND ", columns.Select(column => Invariant($"data.{column} = _.{column}"))).AppendLine() + .Append("WHERE ").AppendJoin(" OR ", conditions) .AppendStatementEndSQL() .ToString(); } + /// public string CreateDeleteSQL(T[] data, StringValues output) { - var primaryKeys = this.Columns - .Where(column => column.PrimaryKey) - .Select(column => column.Name) - .ToArray(); - var escapedPrimaryKeys = primaryKeys.Select(this.DataSource.EscapeIdentifier).ToArray(); + var primaryKeys = this.Columns.Where(column => column.PrimaryKey).ToArray(); + + (primaryKeys.Length > 0).AssertTrue(); - return new StringBuilder() - .AppendLine(Invariant($"DELETE {this.Name}")) + var type = typeof(T); + + if (primaryKeys.Length == 1) + { + var values = data.Select(row => type.GetPropertyValue(primaryKeys[0].Name, row!).ToSQL()).ToCSV(); + var column = primaryKeys[0].Name.EscapeIdentifier(this.DataSource.Type); + return new StringBuilder("DELETE ").AppendLine(this.Name) + .AppendOutputSQL(this.DataSource.Type, output) + .Append("WHERE ").Append(column).Append(" IN (").Append(values).Append(')') + .AppendStatementEndSQL() + .ToString(); + } + + var conditions = data.Select(row => Invariant($"({string.Join(" AND ", primaryKeys.Select(column => + Invariant($"{column.Name.EscapeIdentifier(this.DataSource.Type)} = {type.GetPropertyValue(column.Name, row!).ToSQL()}")))})")); + + return new StringBuilder("DELETE ").AppendLine(this.Name) .AppendOutputSQL(this.DataSource.Type, output) - .AppendLine(Invariant($"FROM {this.Name} AS _")) - .AppendLine("INNER JOIN") - .Append('(').AppendLine() - .AppendValuesSQL(data, primaryKeys) - .AppendLine(Invariant($") AS data ({escapedPrimaryKeys.ToCSV()})")) - .Append("ON ").AppendJoin(" AND ", escapedPrimaryKeys.Select(column => Invariant($"data.{column} = _.{column}"))).AppendLine() + .Append("WHERE ").AppendJoin(" OR ", conditions) .AppendStatementEndSQL() .ToString(); } - public string CreateInsertSQL(string[] columns, SelectQuery selectQuery, params string[] output) - => new StringBuilder() - .AppendLine(Invariant($"INSERT INTO {this.Name}")) - .AppendLine(Invariant($"({columns.Select(this.DataSource.EscapeIdentifier).ToCSV()})")) + public string CreateInsertSQL(string[] columns, SelectQuery selectQuery, string[] output) + => new StringBuilder("INSERT INTO ").AppendLine(this.Name) + .AppendLine(Invariant($"({columns.Select(column => column.EscapeIdentifier(this.DataSource.Type)).ToCSV()})")) .AppendOutputSQL(this.DataSource.Type, output) .Append(this.CreateSelectSQL(selectQuery)) .ToString(); - public string CreateInsertSQL(JsonArray data, params string[] output) - => new StringBuilder() - .AppendLine(Invariant($"INSERT INTO {this.Name}")) - .AppendLine(Invariant($"({data[0]!.AsObject().Select(pair => this.DataSource.EscapeIdentifier(pair.Key)).ToCSV()}")) + public string CreateInsertSQL(JsonArray data, string[] output) + => new StringBuilder("INSERT INTO ").AppendLine(this.Name) + .AppendLine(Invariant($"({data[0]!.AsObject().Select(pair => pair.Key.EscapeIdentifier(this.DataSource.Type)).ToCSV()}")) .AppendOutputSQL(this.DataSource.Type, output) .Append(data.ToSQL()) .AppendStatementEndSQL() .ToString(); - public string CreateInsertSQL(DataTable data, params string[] output) - => new StringBuilder() - .AppendLine(Invariant($"INSERT INTO {this.Name}")) - .AppendLine(Invariant($"({data.Columns.OfType().Select(column => this.DataSource.EscapeIdentifier(column.ColumnName)).ToCSV()})")) + public string CreateInsertSQL(DataTable data, string[] output) + => new StringBuilder("INSERT INTO ").AppendLine(this.Name) + .AppendLine(Invariant($"({data.Columns.OfType().Select(column => column.ColumnName.EscapeIdentifier(this.DataSource.Type)).ToCSV()})")) .AppendOutputSQL(this.DataSource.Type, output) .Append(data.ToSQL()) .AppendStatementEndSQL() .ToString(); - public string CreateInsertSQL(string[] columns, T[] data, params string[] output) - => new StringBuilder() - .AppendLine(Invariant($"INSERT INTO {this.Name}")) - .AppendLine(Invariant($"({columns.Select(this.DataSource.EscapeIdentifier).ToCSV()})")) + public string CreateInsertSQL(string[] columns, T[] data, string[] output) + => new StringBuilder("INSERT INTO ").AppendLine(this.Name) + .AppendLine(Invariant($"({columns.Select(column => column.EscapeIdentifier(this.DataSource.Type)).ToCSV()})")) .AppendOutputSQL(this.DataSource.Type, output) .AppendValuesSQL(data, columns) .AppendStatementEndSQL() @@ -163,24 +189,42 @@ public string CreateInsertSQL(string[] columns, T[] data, params string[] out public string CreateSelectSQL(SelectQuery select) { - var sqlBuilder = new StringBuilder("SELECT"); + var sqlBuilder = new StringBuilder("SELECT "); - if (this.DataSource.Type is PostgreSql && select.DistinctOn.IsNotBlank()) - sqlBuilder.Append(Invariant($" DISTINCT ON {select.DistinctOn}")); - else - sqlBuilder.AppendIf(select.Distinct, " DISTINCT"); - - if (this.DataSource.Type is SqlServer && select.Top.IsNotBlank()) - sqlBuilder.Append(Invariant($" TOP {select.Top}")); + switch (this.DataSource.Type) + { + case SqlServer: + sqlBuilder + .AppendIf(select.Distinct, "DISTINCT ") + .AppendIf(select.Top.IsNotBlank(), Invariant($"TOP {select.Top} ")); + break; + case Oracle: + case MySql: + sqlBuilder + .AppendIf(select.TableHints.IsNotBlank(), Invariant($"/*+ {select.TableHints} */ ")) + .AppendIf(select.Distinct, "DISTINCT "); + break; + case PostgreSql: + if (select.DistinctOn.IsNotBlank()) + sqlBuilder.Append("DISTINCT ON ").Append(select.DistinctOn); + else + sqlBuilder.AppendIf(select.Distinct, "DISTINCT "); + + break; + } return sqlBuilder - .Append(' ') - .AppendLine(select.Select?.Any() is true ? select.Select.ToCSV() : "*") - .AppendLine(Invariant($"FROM {select.From} {select.TableHints}")) + .AppendLineIf(select.Select?.Any() is true, select.Select.ToCSV(), "*") + .Append("FROM ").Append(select.From) + .AppendIf(select.TableHints.IsNotBlank(), _ => _ + .AppendIf(this.DataSource.Type is Oracle, Invariant($" /*+ {select.TableHints} */")) + .AppendIf(this.DataSource.Type is SqlServer, Invariant($" WITH({select.TableHints})")) + .AppendIf(this.DataSource.Type is PostgreSql, Invariant($" WITH({select.TableHints})"))) + .AppendLine() .AppendLineIf(select.Where.IsNotBlank(), Invariant($"WHERE {select.Where}")) .AppendLineIf(select.GroupBy?.Any() is true, select.GroupByOption switch { - GroupBy.Cube => Invariant($"GROUP BY CUBE({select.GroupBy.ToCSV()})"), + GroupBy.Cube when this.DataSource.Type is not MySql => Invariant($"GROUP BY CUBE({select.GroupBy.ToCSV()})"), GroupBy.Rollup => Invariant($"GROUP BY ROLLUP({select.GroupBy.ToCSV()})"), _ => Invariant($"GROUP BY {select.GroupBy.ToCSV()}") }) @@ -200,32 +244,28 @@ public string CreateTruncateSQL(string partitions) _ => Invariant($"TRUNCATE TABLE {this.Name};") }; - public string CreateUpdateSQL(string[] set, string where, params string[] output) - => new StringBuilder() - .AppendLine(this.DataSource.Type is SqlServer - ? Invariant($"UPDATE {this.Name} WITH(UPDLOCK)") - : Invariant($"UPDATE {this.Name}")) + public string CreateUpdateSQL(string[] set, string where, string[] output) + => new StringBuilder("UPDATE ").Append(this.Name) + .AppendIf(this.DataSource.Type is SqlServer, " WITH(UPDLOCK)").AppendLine() .AppendLine(Invariant($"SET {set.ToCSV()}")) .AppendOutputSQL(this.DataSource.Type, output) .AppendLineIf(where.IsNotBlank(), Invariant($"WHERE {where}")) .AppendStatementEndSQL() .ToString(); - public string CreateUpdateSQL(JsonArray data, params string[] output) + public string CreateUpdateSQL(JsonArray data, string[] output) { var primaryKeys = this.Columns .Where(column => column.PrimaryKey) - .Select(column => this.DataSource.EscapeIdentifier(column.Name)); + .Select(column => column.Name.EscapeIdentifier(this.DataSource.Type)); var setColumns = data[0]!.AsObject() .Where(pair => this.Columns.Any(column => column.PrimaryKey && column.Name.Is(pair.Key))) - .Select(pair => this.DataSource.EscapeIdentifier(pair.Key)); + .Select(pair => pair.Key.EscapeIdentifier(this.DataSource.Type)); var columns = data[0]!.AsObject() - .Select(pair => this.DataSource.EscapeIdentifier(pair.Key)); + .Select(pair => pair.Key.EscapeIdentifier(this.DataSource.Type)); - return new StringBuilder() - .AppendLine(this.DataSource.Type is SqlServer - ? Invariant($"UPDATE {this.Name} WITH(UPDLOCK)") - : Invariant($"UPDATE {this.Name}")) + return new StringBuilder("UPDATE ").Append(this.Name) + .AppendIf(this.DataSource.Type is SqlServer, " WITH(UPDLOCK)").AppendLine() .AppendLine(Invariant($"SET {setColumns.Select(column => Invariant($"{column} = data.{column}")).ToCSV()}")) .AppendOutputSQL(this.DataSource.Type, output) .AppendLine(Invariant($"FROM {this.Name} AS _")) @@ -238,42 +278,38 @@ public string CreateUpdateSQL(JsonArray data, params string[] output) .ToString(); } - public string CreateUpdateSQL(DataTable data, params string[] output) + public string CreateUpdateSQL(DataTable data, string[] output) { - var primaryKeys = data.PrimaryKey.Select(column => this.DataSource.EscapeIdentifier(column.ColumnName)); + var primaryKeys = data.PrimaryKey.Select(column => column.ColumnName.EscapeIdentifier(this.DataSource.Type)); var setColumns = data.Columns.OfType() .Where(dataColumn => this.Columns.Any(column => column.PrimaryKey && column.Name.Is(dataColumn.ColumnName))) - .Select(dataColumn => this.DataSource.EscapeIdentifier(dataColumn.ColumnName)); + .Select(dataColumn => dataColumn.ColumnName.EscapeIdentifier(this.DataSource.Type)); var columns = data.Columns.OfType() - .Select(dataColumn => this.DataSource.EscapeIdentifier(dataColumn.ColumnName)); + .Select(dataColumn => dataColumn.ColumnName.EscapeIdentifier(this.DataSource.Type)); - return new StringBuilder() - .AppendLine(this.DataSource.Type is SqlServer - ? Invariant($"UPDATE {this.Name} WITH(UPDLOCK)") - : Invariant($"UPDATE {this.Name}")) - .AppendLine(Invariant($"SET {setColumns.Select(column => Invariant($"{column} = data.{column}")).ToCSV()}")) + return new StringBuilder("UPDATE ").Append(this.Name) + .AppendIf(this.DataSource.Type is SqlServer, " WITH(UPDLOCK)").AppendLine() + .Append("SET ").AppendLine(setColumns.Select(column => Invariant($"{column} = data.{column}")).ToCSV()) .AppendOutputSQL(this.DataSource.Type, output) - .AppendLine(Invariant($"FROM {this.Name} AS _")) + .Append("FROM ").Append(this.Name).AppendLine(" AS _") .AppendLine("INNER JOIN") .Append('(').AppendLine() .Append(data.ToSQL()) - .AppendLine(Invariant($") AS data ({columns.ToCSV()})")) - .Append("ON ").AppendJoin(" AND ", primaryKeys.Select(column => Invariant($"data.{column} = _.{column}"))).AppendLine() + .Append(") AS data (").Append(columns.ToCSV()).Append(')').AppendLine() + .Append("ON ").AppendJoin(" AND ", primaryKeys.Select(column => Invariant($"data.{column} = _.{column}"))) .AppendStatementEndSQL() .ToString(); } - public string CreateUpdateSQL(string[] columns, T[] data, params string[] output) + public string CreateUpdateSQL(string[] columns, T[] data, string[] output) { var primaryKeys = this.Columns .Where(column => column.PrimaryKey) - .Select(column => this.DataSource.EscapeIdentifier(column.Name)); - var escapedColumns = columns.Select(this.DataSource.EscapeIdentifier); + .Select(column => column.Name.EscapeIdentifier(this.DataSource.Type)); + var escapedColumns = columns.Select(column => column.EscapeIdentifier(this.DataSource.Type)); - return new StringBuilder() - .AppendLine(this.DataSource.Type is SqlServer - ? Invariant($"UPDATE {this.Name} WITH(UPDLOCK)") - : Invariant($"UPDATE {this.Name}")) + return new StringBuilder("UPDATE ").Append(this.Name) + .AppendIf(this.DataSource.Type is SqlServer, " WITH(UPDLOCK)").AppendLine() .AppendLine(Invariant($"SET {escapedColumns.Select(column => Invariant($"{column} = data.{column}")).ToCSV()}")) .AppendOutputSQL(this.DataSource.Type, output) .AppendLine(Invariant($"FROM {this.Name} AS _")) @@ -301,7 +337,7 @@ public bool Equals([NotNullWhen(true)] ObjectSchema? other) [MethodImpl(AggressiveInlining), DebuggerHidden] public override int GetHashCode() - => HashCode.Combine(this.DataSource, this.Name); + => HashCode.Combine(this.DataSource.Name, this.Name); [MethodImpl(AggressiveInlining), DebuggerHidden] public override string ToString() diff --git a/src/TypeCache/Extensions/CharExtensions.cs b/src/TypeCache/Extensions/CharExtensions.cs index f1103b7b..b61429a2 100644 --- a/src/TypeCache/Extensions/CharExtensions.cs +++ b/src/TypeCache/Extensions/CharExtensions.cs @@ -203,7 +203,7 @@ public static bool IsWhiteSpace(this char @this) /// => .Join(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static string Join(this char @this, params string[] values) + public static string Join(this char @this, string[] values) => string.Join(@this, values); /// @@ -219,7 +219,7 @@ public static string Join(this char @this, IEnumerable values) /// => .Join(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static string Join(this char @this, params object[] values) + public static string Join(this char @this, object[] values) => string.Join(@this, values); /// diff --git a/src/TypeCache/Extensions/CsvExtensions.cs b/src/TypeCache/Extensions/CsvExtensions.cs index 0793c3b1..9240e489 100644 --- a/src/TypeCache/Extensions/CsvExtensions.cs +++ b/src/TypeCache/Extensions/CsvExtensions.cs @@ -66,7 +66,7 @@ public static string[] ToCSV(this IEnumerable @this) var headerRow = string.Join(',', propertyInfos.Select(propertyInfo => propertyInfo.Name.EscapeCSV())); var dataRows = @this.Select(row => string.Join(',', propertyInfos.Select(propertyInfo => propertyInfo.GetPropertyValue(row).EscapeCSV()))); - return dataRows.Prepend(headerRow).ToArray(); + return [headerRow, ..dataRows]; } var fieldInfos = typeof(T).GetPublicFields(); @@ -75,7 +75,7 @@ public static string[] ToCSV(this IEnumerable @this) var headerRow = string.Join(',', fieldInfos.Select(fieldInfo => fieldInfo.Name.EscapeCSV())); var dataRows = @this.Select(row => string.Join(',', fieldInfos.Select(fieldInfo => fieldInfo.GetFieldValue(row).EscapeCSV()))); - return dataRows.Prepend(headerRow).ToArray(); + return [headerRow, .. dataRows]; } return Array.Empty; diff --git a/src/TypeCache/Extensions/EnumExtensions.cs b/src/TypeCache/Extensions/EnumExtensions.cs index 0d6ba794..ea439398 100644 --- a/src/TypeCache/Extensions/EnumExtensions.cs +++ b/src/TypeCache/Extensions/EnumExtensions.cs @@ -2,6 +2,7 @@ using TypeCache.Collections; using TypeCache.Extensions; +using TypeCache.Utilities; using static System.Reflection.BindingFlags; namespace TypeCache.Extensions; @@ -11,12 +12,12 @@ public static class EnumExtensions [DebuggerHidden] public static Attribute[] Attributes(this T @this) where T : struct, Enum - => Enum.IsDefined(@this) + => Enum.IsValid(@this) ? typeof(T).GetField(@this.Name(), Public | Static)!.GetCustomAttributes(false).Cast().ToArray() : Array.Empty; [DebuggerHidden] - public static bool HasAnyFlag(this T @this, params T[] flags) + public static bool HasAnyFlag(this T @this, T[] flags) where T : struct, Enum => flags?.Any(flag => @this.HasFlag(flag)) ?? false; @@ -36,6 +37,14 @@ public static bool IsDefined(this T @this) where T : struct, Enum => Enum.IsDefined(@this); + /// + /// => <>.IsValid(@); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static bool IsValid(this T @this) + where T : struct, Enum + => Enum.IsValid(@this); + /// /// => @.ToString("F"); /// diff --git a/src/TypeCache/Extensions/ExpressionExtensions.ArrayExpressionBuilder.cs b/src/TypeCache/Extensions/ExpressionExtensions.ArrayExpressionBuilder.cs index 5474acbb..e79bc682 100644 --- a/src/TypeCache/Extensions/ExpressionExtensions.ArrayExpressionBuilder.cs +++ b/src/TypeCache/Extensions/ExpressionExtensions.ArrayExpressionBuilder.cs @@ -26,7 +26,7 @@ public IndexExpression this[int index] /// => .ArrayAccess(._Expression, .Select(index => .Constant(index))); /// [DebuggerHidden] - public IndexExpression this[params int[] indexes] + public IndexExpression this[int[] indexes] => Expression.ArrayAccess(this._Expression, indexes.Select(index => Expression.Constant(index))); /// @@ -42,7 +42,7 @@ public IndexExpression this[long index] /// => .ArrayAccess(._Expression, .Select(index => .Constant(index))); /// [DebuggerHidden] - public IndexExpression this[params long[] indexes] + public IndexExpression this[long[] indexes] => Expression.ArrayAccess(this._Expression, indexes.Select(index => Expression.Constant(index))); /// @@ -66,7 +66,7 @@ public IndexExpression this[IEnumerable indexes] /// => .ArrayAccess(._Expression, ); /// [DebuggerHidden] - public IndexExpression this[params Expression[] indexes] + public IndexExpression this[Expression[] indexes] => Expression.ArrayAccess(this._Expression, indexes); /// diff --git a/src/TypeCache/Extensions/ExpressionExtensions.cs b/src/TypeCache/Extensions/ExpressionExtensions.cs index 4cf64c4a..3b85f652 100644 --- a/src/TypeCache/Extensions/ExpressionExtensions.cs +++ b/src/TypeCache/Extensions/ExpressionExtensions.cs @@ -36,7 +36,7 @@ public static BinaryExpression Array(this Expression @this, int index) /// /// => .ArrayIndex(@, .Select(index => ().Constant(index))); /// - public static MethodCallExpression Array(this Expression @this, params int[] indexes) + public static MethodCallExpression Array(this Expression @this, int[] indexes) => Expression.ArrayIndex(@this, indexes.Select(index => (Expression)Expression.Constant(index))); /// @@ -51,7 +51,7 @@ public static BinaryExpression Array(this Expression @this, long index) /// /// => .ArrayIndex(@, .Select(index => ().Constant(index))); /// - public static MethodCallExpression Array(this Expression @this, params long[] indexes) + public static MethodCallExpression Array(this Expression @this, long[] indexes) => Expression.ArrayIndex(@this, indexes.Select(index => (Expression)Expression.Constant(index))); /// @@ -75,7 +75,7 @@ public static MethodCallExpression Array(this Expression @this, IEnumerable=> .ArrayIndex(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression Array(this Expression @this, params Expression[] indexes) + public static MethodCallExpression Array(this Expression @this, Expression[] indexes) => Expression.ArrayIndex(@this, indexes); /// @@ -145,7 +145,7 @@ public static MethodCallExpression Call(this Expression @this, MethodInfo method /// => .Call(@, , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression Call(this Expression @this, MethodInfo methodInfo, IEnumerable? arguments) + public static MethodCallExpression Call(this Expression @this, MethodInfo methodInfo, IEnumerable? arguments = null) => Expression.Call(@this, methodInfo, arguments); /// @@ -153,7 +153,7 @@ public static MethodCallExpression Call(this Expression @this, MethodInfo method /// => .Call(@, , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression Call(this Expression @this, MethodInfo methodInfo, params Expression[]? arguments) + public static MethodCallExpression Call(this Expression @this, MethodInfo methodInfo, Expression[]? arguments = null) => Expression.Call(@this, methodInfo, arguments); /// @@ -161,7 +161,7 @@ public static MethodCallExpression Call(this Expression @this, MethodInfo method /// => .Call(@, , , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression Call(this Expression @this, string method, params Expression[]? arguments) + public static MethodCallExpression Call(this Expression @this, string method, Expression[]? arguments = null) => Expression.Call(@this, method, Type.EmptyTypes, arguments); /// @@ -169,7 +169,7 @@ public static MethodCallExpression Call(this Expression @this, string method, pa /// => .Call(@, , , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression Call(this Expression @this, string method, Type[]? genericTypes, params Expression[]? arguments) + public static MethodCallExpression Call(this Expression @this, string method, Type[]? genericTypes, Expression[]? arguments = null) => Expression.Call(@this, method, genericTypes, arguments); /// @@ -226,40 +226,40 @@ public static Expression Convert(this Expression @this, Type targetType) return @this.Cast(targetType); if (@this.Type != typeof(object)) - return ValueConverter.CreateConversionExpression(@this, targetType); + return @this.CreateConversionExpression(targetType); var targetScalarType = targetType.GetScalarType(); var expression = targetScalarType switch { - ScalarType.BigInteger => (Expression)typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToBigInteger), @this), - ScalarType.Boolean => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToBoolean), @this), - ScalarType.Byte => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToByte), @this), - ScalarType.Char => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToChar), @this), - 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.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), - ScalarType.Guid => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToGuid), @this), - ScalarType.Half => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToHalf), @this), - ScalarType.Index => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToIndex), @this), - ScalarType.Int16 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToInt16), @this), - ScalarType.Int32 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToInt32), @this), - ScalarType.Int64 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToInt64), @this), - ScalarType.Int128 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToInt128), @this), - ScalarType.IntPtr => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToIntPtr), @this), - ScalarType.SByte => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToSByte), @this), - ScalarType.Single => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToSingle), @this), - ScalarType.String => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToString), @this), - ScalarType.TimeOnly => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToTimeOnly), @this), - ScalarType.TimeSpan => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToTimeSpan), @this), - ScalarType.UInt16 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUInt16), @this), - ScalarType.UInt32 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUInt32), @this), - ScalarType.UInt64 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUInt64), @this), - ScalarType.UInt128 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUInt128), @this), - ScalarType.UIntPtr => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUIntPtr), @this), - ScalarType.Uri => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUri), @this), + ScalarType.BigInteger => (Expression)typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToBigInteger), [@this]), + ScalarType.Boolean => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToBoolean), [@this]), + ScalarType.Byte => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToByte), [@this]), + ScalarType.Char => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToChar), [@this]), + 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.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]), + ScalarType.Guid => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToGuid), [@this]), + ScalarType.Half => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToHalf), [@this]), + ScalarType.Index => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToIndex), [@this]), + ScalarType.Int16 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToInt16), [@this]), + ScalarType.Int32 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToInt32), [@this]), + ScalarType.Int64 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToInt64), [@this]), + ScalarType.Int128 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToInt128), [@this]), + ScalarType.IntPtr => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToIntPtr), [@this]), + ScalarType.SByte => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToSByte), [@this]), + ScalarType.Single => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToSingle), [@this]), + ScalarType.String => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToString), [@this]), + ScalarType.TimeOnly => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToTimeOnly), [@this]), + ScalarType.TimeSpan => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToTimeSpan), [@this]), + ScalarType.UInt16 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUInt16), [@this]), + ScalarType.UInt32 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUInt32), [@this]), + ScalarType.UInt64 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUInt64), [@this]), + ScalarType.UInt128 => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUInt128), [@this]), + ScalarType.UIntPtr => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUIntPtr), [@this]), + ScalarType.Uri => typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToUri), [@this]), _ => @this.Cast(targetType) }; @@ -322,7 +322,7 @@ public static InvocationExpression Invoke(this LambdaExpression @this, IEnumerab /// => .Invoke(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static InvocationExpression Invoke(this LambdaExpression @this, params Expression[]? parameters) + public static InvocationExpression Invoke(this LambdaExpression @this, Expression[]? parameters) => Expression.Invoke(@this, parameters); /// @@ -378,7 +378,7 @@ public static Expression Lambda(this Expression @this, IEnumerable=> .Lambda(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static LambdaExpression Lambda(this Expression @this, params ParameterExpression[]? parameters) + public static LambdaExpression Lambda(this Expression @this, ParameterExpression[]? parameters = null) => Expression.Lambda(@this, parameters); /// @@ -386,7 +386,7 @@ public static LambdaExpression Lambda(this Expression @this, params ParameterExp /// => .Lambda<>(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static Expression Lambda(this Expression @this, params ParameterExpression[]? parameters) + public static Expression Lambda(this Expression @this, ParameterExpression[]? parameters = null) => Expression.Lambda(@this, parameters); /// @@ -402,40 +402,40 @@ public static LambdaExpression LambdaAction(this Expression @this, IEnumerable

=> .Lambda(.GetActionType(.Select(parameter => parameter.Type).ToArray(), /// @, ); /// - public static LambdaExpression LambdaAction(this Expression @this, params ParameterExpression[] parameters) - => Expression.Lambda(Expression.GetActionType(parameters.Select(parameter => parameter.Type).ToArray()), @this, parameters); + public static LambdaExpression LambdaAction(this Expression @this, ParameterExpression[]? parameters = null) + => Expression.Lambda(Expression.GetActionType(parameters?.Select(parameter => parameter.Type).ToArray()), @this, parameters); /// /// - /// => .Lambda(.GetFuncType(.Select(parameter => parameter.Type).Append(()).ToArray(), + /// => .Lambda(.GetFuncType([...Select(parameter => parameter.Type), ()]), /// @, ); /// public static LambdaExpression LambdaFunc(this Expression @this, IEnumerable parameters) - => Expression.Lambda(Expression.GetFuncType(parameters?.Select(parameter => parameter.Type).Append(typeof(T)).ToArray()), @this, parameters); + => Expression.Lambda(Expression.GetFuncType([..parameters?.Select(parameter => parameter.Type), typeof(T)]), @this, parameters); /// /// - /// => .Lambda(.GetFuncType(.Select(parameter => parameter.Type).Append(()).ToArray(), + /// => .Lambda(.GetFuncType([...Select(parameter => parameter.Type), ()]), /// @, ); /// - public static LambdaExpression LambdaFunc(this Expression @this, params ParameterExpression[]? parameters) - => Expression.Lambda(Expression.GetFuncType(parameters?.Select(parameter => parameter.Type).Append(typeof(T)).ToArray()), @this, parameters); + public static LambdaExpression LambdaFunc(this Expression @this, ParameterExpression[]? parameters = null) + => Expression.Lambda(Expression.GetFuncType([..parameters?.Select(parameter => parameter.Type), typeof(T)]), @this, parameters); /// /// - /// => .Lambda(.GetFuncType(.Select(parameter => parameter.Type).Append().ToArray(), + /// => .Lambda(.GetFuncType([...Select(parameter => parameter.Type), ]), /// @, ); /// public static LambdaExpression LambdaFunc(this Expression @this, Type returnType, IEnumerable parameters) - => Expression.Lambda(Expression.GetFuncType(parameters?.Select(parameter => parameter.Type).Append(returnType).ToArray()), @this, parameters); + => Expression.Lambda(Expression.GetFuncType([..parameters?.Select(parameter => parameter.Type), returnType]), @this, parameters); /// /// - /// => .Lambda(.GetFuncType(.Select(parameter => parameter.Type).Append().ToArray(), + /// => .Lambda(.GetFuncType([...Select(parameter => parameter.Type), ]), /// @, ); /// - public static LambdaExpression LambdaFunc(this Expression @this, Type returnType, params ParameterExpression[]? parameters) - => Expression.Lambda(Expression.GetFuncType(parameters?.Select(parameter => parameter.Type).Append(returnType).ToArray()), @this, parameters); + public static LambdaExpression LambdaFunc(this Expression @this, Type returnType, ParameterExpression[]? parameters = null) + => Expression.Lambda(Expression.GetFuncType([..parameters?.Select(parameter => parameter.Type), returnType]), @this, parameters); /// /// @@ -458,7 +458,7 @@ public static MemberInitExpression MemberInit(this NewExpression @this, IEnumera /// => .MemberInit(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MemberInitExpression MemberInit(this NewExpression @this, params MemberBinding[] bindings) + public static MemberInitExpression MemberInit(this NewExpression @this, MemberBinding[] bindings) => Expression.MemberInit(@this, bindings); /// diff --git a/src/TypeCache/Extensions/LoggerExtensions.cs b/src/TypeCache/Extensions/LoggerExtensions.cs index dc878a78..2a844860 100644 --- a/src/TypeCache/Extensions/LoggerExtensions.cs +++ b/src/TypeCache/Extensions/LoggerExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) 2021 Samuel Abraham using Microsoft.Extensions.Logging; +using TypeCache.Collections; namespace TypeCache.Extensions; @@ -9,11 +10,13 @@ public static class LoggerExtensions ///

/// Logs the and each inner exception. /// - public static void LogAggregateException(this ILogger @this, EventId eventId, AggregateException error, string message, params object?[] args) + public static void LogAggregateException(this ILogger @this, EventId eventId, AggregateException error, string message, object?[]? args = null) { @this.AssertNotNull(); error.AssertNotNull(); + args ??= Array.Empty; + if (error.InnerExceptions.Count > 1) error.InnerExceptions.AsArray().ForEach(exception => @this.LogError(eventId, exception, message, args)); else if (error.InnerException is not null) @@ -25,11 +28,13 @@ public static void LogAggregateException(this ILogger @this, EventId event /// /// Logs the and each inner exception. /// - public static void LogAggregateException(this ILogger @this, AggregateException error, string message, params object?[] args) + public static void LogAggregateException(this ILogger @this, AggregateException error, string message, object?[]? args = null) { @this.AssertNotNull(); error.AssertNotNull(); + args ??= Array.Empty; + if (error.InnerExceptions.Count > 1) error.InnerExceptions.AsArray().ForEach(exception => @this.LogError(exception, message, args)); else if (error.InnerException is not null) diff --git a/src/TypeCache/Extensions/MapExtensions.cs b/src/TypeCache/Extensions/MapExtensions.cs index a310e53b..9f50e5f0 100644 --- a/src/TypeCache/Extensions/MapExtensions.cs +++ b/src/TypeCache/Extensions/MapExtensions.cs @@ -197,8 +197,8 @@ private static T MapModel(S source, T target, BindingFlags bindings) if (value is null && !targetPropertyInfo.PropertyType.IsNullable()) continue; - //if (value is not null && !value.GetType().IsConvertibleTo(targetPropertyInfo.PropertyType)) - // continue; + if (value is not null && !value.GetType().IsAssignableTo(targetPropertyInfo.PropertyType)) + continue; targetPropertyInfo.SetPropertyValue(target, value); } diff --git a/src/TypeCache/Extensions/ReadOnlySpanExtensions.cs b/src/TypeCache/Extensions/ReadOnlySpanExtensions.cs index f73aa7bc..004342af 100644 --- a/src/TypeCache/Extensions/ReadOnlySpanExtensions.cs +++ b/src/TypeCache/Extensions/ReadOnlySpanExtensions.cs @@ -122,7 +122,7 @@ public static string Join(this scoped ReadOnlySpan @this, IEnumerable=> @.Join(()); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static string Join(this scoped ReadOnlySpan @this, params string[] values) + public static string Join(this scoped ReadOnlySpan @this, string[] values) => @this.Join((IEnumerable)values); /// diff --git a/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs index c2740de9..5bd21b04 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs @@ -5,7 +5,7 @@ namespace TypeCache.Extensions; -partial class ReflectionExtensions +public partial class ReflectionExtensions { public static Expression> ToFuncExpression(this ConstructorInfo @this) { @@ -15,7 +15,7 @@ partial class ReflectionExtensions .Select((parameterInfo, i) => arguments.Array()[i].Convert(parameterInfo.ParameterType)) .ToArray(); - return @this.ToExpression(parameters).As().Lambda>(arguments); + return @this.ToExpression(parameters).As().Lambda>([arguments]); } public static LambdaExpression ToLambdaExpression(this ConstructorInfo @this) @@ -41,7 +41,7 @@ public static NewExpression ToExpression(this ConstructorInfo @this, IEnumerable /// => .New(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static NewExpression ToExpression(this ConstructorInfo @this, params Expression[]? parameters) + public static NewExpression ToExpression(this ConstructorInfo @this, Expression[]? parameters = null) => Expression.New(@this, parameters); /// @@ -57,6 +57,6 @@ public static NewExpression ToExpression(this ConstructorInfo @this, IEnumerable /// => .New(@, , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static NewExpression ToExpression(this ConstructorInfo @this, IEnumerable parameters, params MemberInfo[]? memberInfos) + public static NewExpression ToExpression(this ConstructorInfo @this, IEnumerable parameters, MemberInfo[]? memberInfos = null) => Expression.New(@this, parameters, memberInfos); } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.Delegate.cs b/src/TypeCache/Extensions/ReflectionExtensions.Delegate.cs index 39b4232d..8000cfc9 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.Delegate.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.Delegate.cs @@ -4,7 +4,7 @@ namespace TypeCache.Extensions; -partial class ReflectionExtensions +public partial class ReflectionExtensions { [DebuggerHidden] public static MethodInfo ToMethodInfo(this Delegate @this) diff --git a/src/TypeCache/Extensions/ReflectionExtensions.FieldInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.FieldInfo.cs index 784f5dd8..8a711613 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.FieldInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.FieldInfo.cs @@ -6,7 +6,7 @@ namespace TypeCache.Extensions; -partial class ReflectionExtensions +public partial class ReflectionExtensions { /// /// @@ -23,14 +23,14 @@ partial class ReflectionExtensions public static Expression> GetFieldValueFuncExpression(this FieldInfo @this) { ParameterExpression instance = nameof(instance).ToParameterExpression(); - var field = !@this.IsStatic ? instance.Cast(@this.DeclaringType!).Field(@this) : @this.ToStaticFieldExpression(); - return field.As().Lambda>(instance); + var field = !@this.IsStatic ? instance.Cast(@this.DeclaringType!).Field(@this) : @this.ToExpression(null); + return field.As().Lambda>([instance]); } public static LambdaExpression GetFieldValueLambdaExpression(this FieldInfo @this) => !@this.IsStatic ? LambdaFactory.Create([@this.DeclaringType!], parameters => parameters[0].Field(@this)) - : @this.ToStaticFieldExpression().Lambda(); + : @this.ToExpression(null).Lambda(); /// /// @@ -52,8 +52,8 @@ public static void SetFieldValue(this FieldInfo @this, object? instance, object? ParameterExpression instance = nameof(instance).ToParameterExpression(); ParameterExpression value = nameof(value).ToParameterExpression(); - var field = !@this.IsStatic ? instance.Convert(@this.DeclaringType!).Field(@this) : @this.ToStaticFieldExpression(); - return field.Assign(value.Convert(@this.FieldType)).Lambda>(instance, value); + var field = !@this.IsStatic ? instance.Convert(@this.DeclaringType!).Field(@this) : @this.ToExpression(null); + return field.Assign(value.Convert(@this.FieldType)).Lambda>([instance, value]); } public static LambdaExpression SetFieldValueLambdaExpression(this FieldInfo @this) @@ -63,14 +63,15 @@ public static LambdaExpression SetFieldValueLambdaExpression(this FieldInfo @thi return !@this.IsStatic ? LambdaFactory.CreateAction([@this.DeclaringType!, @this.FieldType], parameters => parameters[0].Field(@this).Assign(parameters[1])) - : LambdaFactory.CreateAction([@this.FieldType], parameters => @this.ToStaticFieldExpression().Assign(parameters[0])); + : LambdaFactory.CreateAction([@this.FieldType], parameters => @this.ToExpression(null).Assign(parameters[0])); } /// /// - /// => .Field(, @); + /// => .Field(, @); /// + /// Pass for static field access. [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MemberExpression ToStaticFieldExpression(this FieldInfo @this) - => Expression.Field(null, @this); + public static MemberExpression ToExpression(this FieldInfo @this, Expression? instance) + => Expression.Field(instance, @this); } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs index 797fd2b1..2ee2fb44 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs @@ -5,7 +5,7 @@ namespace TypeCache.Extensions; -partial class ReflectionExtensions +public partial class ReflectionExtensions { /// /// @.GetCustomAttribute<>() ; diff --git a/src/TypeCache/Extensions/ReflectionExtensions.MethodBase.cs b/src/TypeCache/Extensions/ReflectionExtensions.MethodBase.cs index 710e6c7e..f29bbe68 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.MethodBase.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.MethodBase.cs @@ -7,7 +7,7 @@ namespace TypeCache.Extensions; partial class ReflectionExtensions { - public static bool IsCallableWith(this MethodBase @this, params object?[]? arguments) + public static bool IsCallableWith(this MethodBase @this, object?[]? arguments) { var parameterInfos = @this.GetParameters(); if (parameterInfos.Any(_ => _.IsOut)) @@ -28,7 +28,7 @@ public static bool IsCallableWith(this MethodBase @this, params object?[]? argum /// The method arguments. /// - public static object? InvokeMethod(this MethodBase @this, params object?[]? arguments) + public static object? InvokeMethod(this MethodBase @this, object?[]? arguments) { @this.DeclaringType.AssertNotNull(); var method = TypeStore.MethodFuncs[(@this.DeclaringType.TypeHandle, @this.MethodHandle)]; diff --git a/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs index ca6d042b..1cbfe88f 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs @@ -5,14 +5,14 @@ namespace TypeCache.Extensions; -partial class ReflectionExtensions +public partial class ReflectionExtensions { /// - /// @.ReturnType.IsAny((Task), (ValueTask), (void)); + /// @.ReturnType.IsAny([(Task), (ValueTask), (void)]); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool HasNoReturnValue(this MethodInfo @this) - => @this.ReturnType.IsAny(typeof(Task), typeof(ValueTask), typeof(void)); + => @this.ReturnType.IsAny([typeof(Task), typeof(ValueTask), typeof(void)]); /// /// => (()@).IsInvokable() && @.ReturnType.IsInvokable(); @@ -92,12 +92,12 @@ public static MethodInfo MakeGenericMethod(this MethodIn ? parameterInfos.Select((parameterInfo, i) => arguments.Array()[i].Convert(parameterInfo.ParameterType)) : parameterInfos.Select((parameterInfo, i) => arguments.Array()[i + 1].Convert(parameterInfo.ParameterType)); var call = @this.IsStatic - ? @this.ToStaticMethodCallExpression(parameters) + ? @this.ToExpression(null, parameters) : arguments.Array()[0].Cast(@this.DeclaringType!).Call(@this, parameters); return @this.ReturnType != typeof(void) - ? call.As().Lambda>(arguments) - : call.Block(Expression.Constant(null)).Lambda>(arguments); + ? call.As().Lambda>([arguments]) + : call.Block(Expression.Constant(null)).Lambda>([arguments]); } public static LambdaExpression ToLambdaExpression(this MethodInfo @this) @@ -109,31 +109,24 @@ public static LambdaExpression ToLambdaExpression(this MethodInfo @this) .ToArray(); return !@this.IsStatic - ? instance.Call(@this, parameters).Lambda(parameters.Prepend(instance)) - : @this.ToStaticMethodCallExpression(parameters).Lambda(parameters); + ? instance.Call(@this, parameters).Lambda([instance, ..parameters]) + : @this.ToExpression(null, parameters).Lambda(parameters); } - /// - /// - /// => .Call(@); - /// - [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression ToStaticMethodCallExpression(this MethodInfo @this) - => Expression.Call(@this); - /// /// /// => .Call(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression ToStaticMethodCallExpression(this MethodInfo @this, IEnumerable arguments) - => Expression.Call(@this, arguments); + public static MethodCallExpression ToExpression(this MethodInfo @this, Expression? instance, IEnumerable arguments) + => Expression.Call(instance, @this, arguments); /// /// - /// => .Call(@, ); + /// => .Call(@, , ); /// + /// Pass for static method call. [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression ToStaticMethodCallExpression(this MethodInfo @this, params Expression[]? arguments) - => Expression.Call(@this, arguments); + public static MethodCallExpression ToExpression(this MethodInfo @this, Expression? instance, Expression[]? arguments = null) + => Expression.Call(instance, @this, arguments); } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.PropertyInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.PropertyInfo.cs index 10bf10cd..6257a6be 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.PropertyInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.PropertyInfo.cs @@ -6,11 +6,11 @@ namespace TypeCache.Extensions; -partial class ReflectionExtensions +public partial class ReflectionExtensions { /// Pass if this is a static property getter. /// Property indexer arguments; otherwise if not an indexer. - public static object? GetPropertyValue(this PropertyInfo @this, object? instance, params object?[]? index) + public static object? GetPropertyValue(this PropertyInfo @this, object? instance, object?[]? index = null) { @this.CanRead.AssertTrue(); @@ -18,7 +18,7 @@ partial class ReflectionExtensions { null when index?.Any() is true => index, null => Array.Empty, - _ when index?.Any() is true => index.Prepend(instance).ToArray(), + _ when index?.Any() is true => [instance, ..index], _ => [instance] }; return @this.GetMethod!.InvokeMethod(arguments); @@ -26,15 +26,15 @@ partial class ReflectionExtensions /// Pass if this is a static property setter. /// Property indexer arguments; otherwise if not an indexer. - public static void SetPropertyValue(this PropertyInfo @this, object? instance, object? value, params object?[]? index) + public static void SetPropertyValue(this PropertyInfo @this, object? instance, object? value, object?[]? index = null) { @this.CanWrite.AssertTrue(); - var arguments = instance switch + object?[] arguments = instance switch { - null when index?.Any() is true => index.Append(value).ToArray(), + null when index?.Any() is true => [..index, value], null => [value], - _ when index?.Any() is true => index.Prepend(instance).Append(value).ToArray(), + _ when index?.Any() is true => [instance, ..index, value], _ => [instance, value] }; @this.SetMethod!.InvokeMethod(arguments); @@ -42,17 +42,19 @@ public static void SetPropertyValue(this PropertyInfo @this, object? instance, o /// /// - /// => .Property(, @); + /// => .Property(, @); /// + /// Pass for static property access. [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MemberExpression ToStaticPropertyExpression(this PropertyInfo @this) - => Expression.Property(null, @this); + public static MemberExpression ToExpression(this PropertyInfo @this, Expression? instance) + => Expression.Property(instance, @this); - /// + /// /// - /// => .Property(, @, ); + /// => .Property(, @, ); /// + /// Pass for static property access. [MethodImpl(AggressiveInlining), DebuggerHidden] - public static IndexExpression ToStaticPropertyExpression(this PropertyInfo @this, ParameterExpression index) - => Expression.Property(null, @this, index); + public static IndexExpression ToExpression(this PropertyInfo @this, Expression? instance, Expression[] indexes) + => Expression.Property(instance, @this, indexes); } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.Type.cs b/src/TypeCache/Extensions/ReflectionExtensions.Type.cs index 89ad8f44..cdec1b9a 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.Type.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.Type.cs @@ -3,16 +3,17 @@ using System.Collections; using System.Linq.Expressions; using System.Reflection; +using TypeCache.Collections; using TypeCache.Extensions; using TypeCache.Utilities; using static System.Reflection.BindingFlags; namespace TypeCache.Extensions; -partial class ReflectionExtensions +public partial class ReflectionExtensions { /// - public static object? Create(this Type @this, params object?[]? parameters) + public static object? Create(this Type @this, object?[]? parameters = null) => @this.FindConstructor(parameters) switch { null when @this.IsValueType && parameters?.Any() is not true => TypeStore.DefaultValueTypeConstructorFuncs[@this.TypeHandle].Invoke(), @@ -21,36 +22,36 @@ partial class ReflectionExtensions }; [DebuggerHidden] - public static ConstructorInfo? FindConstructor(this Type @this, params object?[]? arguments) + public static ConstructorInfo? FindConstructor(this Type @this, object?[]? arguments = null) => @this.GetConstructors(INSTANCE_BINDING_FLAGS) .FirstOrDefault(constructor => constructor.IsCallableWith(arguments)); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodInfo? FindMethod(this Type @this, string name, params Type[] argumentTypes) + public static MethodInfo? FindMethod(this Type @this, string name, Type[] argumentTypes) => @this.GetMethod(name, INSTANCE_BINDING_FLAGS, argumentTypes); /// [DebuggerHidden] - public static MethodInfo? FindMethod(this Type @this, string name, params object?[]? arguments) + public static MethodInfo? FindMethod(this Type @this, string name, object?[]? arguments = null) => @this.GetMethods(INSTANCE_BINDING_FLAGS) .FirstOrDefault(method => method.Name.Is(name) && method.IsCallableWith(arguments)); /// [DebuggerHidden] - public static MethodInfo? FindMethod(this Type @this, string name, Type[] genericTypes, params object?[]? arguments) + public static MethodInfo? FindMethod(this Type @this, string name, Type[] genericTypes, object?[]? arguments = null) => @this.GetMethods(INSTANCE_BINDING_FLAGS) .Where(method => method.Name.Is(name) && method.IsGenericMethod) .FirstOrDefault(method => method.MakeGenericMethod(genericTypes)?.IsCallableWith(arguments) is true); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodInfo? FindStaticMethod(this Type @this, string name, params Type[] argumentTypes) + public static MethodInfo? FindStaticMethod(this Type @this, string name, Type[] argumentTypes) => @this.GetMethod(name, STATIC_BINDING_FLAGS, argumentTypes); /// [DebuggerHidden] - public static MethodInfo? FindStaticMethod(this Type @this, string name, params object?[]? arguments) + public static MethodInfo? FindStaticMethod(this Type @this, string name, object?[]? arguments = null) => @this.GetMethods(STATIC_BINDING_FLAGS) .FirstOrDefault(method => method.Name.Is(name) && method.IsCallableWith(arguments)); @@ -131,7 +132,7 @@ public static PropertyInfo[] GetPublicStaticProperties(this Type @this) .Where(propertyInfo => propertyInfo.GetMethod?.IsStatic is true || propertyInfo.SetMethod?.IsStatic is true) .ToArray(); - public static object? GetPropertyValue(this Type @this, string name, object instance, params object?[]? index) + public static object? GetPropertyValue(this Type @this, string name, object instance, object?[]? index = null) => @this.GetProperty(name, INSTANCE_BINDING_FLAGS)? .GetPropertyValue(instance, index); @@ -139,7 +140,7 @@ public static PropertyInfo[] GetPublicStaticProperties(this Type @this) => @this.GetField(name, STATIC_BINDING_FLAGS)? .GetFieldValue(null); - public static object? GetStaticPropertyValue(this Type @this, string name, params object?[]? index) + public static object? GetStaticPropertyValue(this Type @this, string name, object?[]? index = null) => @this.GetProperty(name, STATIC_BINDING_FLAGS)? .GetPropertyValue(null, index); @@ -168,83 +169,83 @@ static bool isDescendantOf(Type? baseType, Type type) } } - public static object? InvokeMethod(this Type @this, string name, object instance, params object?[]? arguments) + public static object? InvokeMethod(this Type @this, string name, object instance, object?[]? arguments = null) => @this.FindMethod(name, arguments)? - .InvokeMethod(arguments?.Any() is true ? arguments.Prepend(instance).ToArray() : [instance]); + .InvokeMethod([instance, .. arguments ?? Array.Empty]); - public static object? InvokeMethod(this Type @this, string name, Type[] genericTypes, object instance, params object?[]? arguments) + public static object? InvokeMethod(this Type @this, string name, Type[] genericTypes, object instance, object?[]? arguments = null) => @this.FindMethod(name, genericTypes, arguments)? .MakeGenericMethod(genericTypes) - .InvokeMethod(arguments?.Any() is true ? arguments.Prepend(instance).ToArray() : [instance]); + .InvokeMethod([instance, .. arguments ?? Array.Empty]); - public static object? InvokeMethod(this Type @this, string name, object instance, params object?[]? arguments) + public static object? InvokeMethod(this Type @this, string name, object instance, object?[]? arguments = null) => @this.FindMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(arguments?.Any() is true ? arguments.Prepend(instance).ToArray() : [instance]); + .InvokeMethod([instance, .. arguments ?? Array.Empty]); - public static object? InvokeMethod(this Type @this, string name, object instance, params object?[]? arguments) + public static object? InvokeMethod(this Type @this, string name, object instance, object?[]? arguments = null) => @this.FindMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(arguments?.Any() is true ? arguments.Prepend(instance).ToArray() : [instance]); + .InvokeMethod([instance, .. arguments ?? Array.Empty]); - public static object? InvokeMethod(this Type @this, string name, object instance, params object?[]? arguments) + public static object? InvokeMethod(this Type @this, string name, object instance, object?[]? arguments = null) => @this.FindMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(arguments?.Any() is true ? arguments.Prepend(instance).ToArray() : [instance]); + .InvokeMethod([instance, .. arguments ?? Array.Empty]); - public static object? InvokeMethod(this Type @this, string name, object instance, params object?[]? arguments) + public static object? InvokeMethod(this Type @this, string name, object instance, object?[]? arguments = null) => @this.FindMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(arguments?.Any() is true ? arguments.Prepend(instance).ToArray() : [instance]); + .InvokeMethod([instance, .. arguments ?? Array.Empty]); - public static object? InvokeMethod(this Type @this, string name, object instance, params object?[]? arguments) + public static object? InvokeMethod(this Type @this, string name, object instance, object?[]? arguments = null) => @this.FindMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(arguments?.Any() is true ? arguments.Prepend(instance).ToArray() : [instance]); + .InvokeMethod([instance, .. arguments ?? Array.Empty]); - public static object? InvokeMethod(this Type @this, string name, object instance, params object?[]? arguments) + public static object? InvokeMethod(this Type @this, string name, object instance, object?[]? arguments = null) => @this.FindMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(arguments?.Any() is true ? arguments.Prepend(instance).ToArray() : [instance]); + .InvokeMethod([instance, .. arguments ?? Array.Empty]); - public static object? InvokeStaticMethod(this Type @this, string name, params object?[]? arguments) + public static object? InvokeStaticMethod(this Type @this, string name, object?[]? arguments = null) => @this.FindStaticMethod(name, arguments)? - .InvokeMethod(null, arguments); + .InvokeMethod(arguments); - public static object? InvokeStaticMethod(this Type @this, string name, Type[] genericTypes, params object?[]? arguments) + public static object? InvokeStaticMethod(this Type @this, string name, Type[] genericTypes, object?[]? arguments = null) => @this.FindMethod(name, arguments)? .MakeGenericMethod(genericTypes) - .InvokeMethod(null, arguments); + .InvokeMethod(arguments); - public static object? InvokeStaticMethod(this Type @this, string name, params object?[]? arguments) + public static object? InvokeStaticMethod(this Type @this, string name, object?[]? arguments = null) => @this.FindStaticMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(null, arguments); + .InvokeMethod(arguments); - public static object? InvokeStaticMethod(this Type @this, string name, params object?[]? arguments) + public static object? InvokeStaticMethod(this Type @this, string name, object?[]? arguments = null) => @this.FindStaticMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(null, arguments); + .InvokeMethod(arguments); - public static object? InvokeStaticMethod(this Type @this, string name, params object?[]? arguments) + public static object? InvokeStaticMethod(this Type @this, string name, object?[]? arguments = null) => @this.FindStaticMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(null, arguments); + .InvokeMethod(arguments); - public static object? InvokeStaticMethod(this Type @this, string name, params object?[]? arguments) + public static object? InvokeStaticMethod(this Type @this, string name, object?[]? arguments = null) => @this.FindStaticMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(null, arguments); + .InvokeMethod(arguments); - public static object? InvokeStaticMethod(this Type @this, string name, params object?[]? arguments) + public static object? InvokeStaticMethod(this Type @this, string name, object?[]? arguments = null) => @this.FindStaticMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(null, arguments); + .InvokeMethod(arguments); - public static object? InvokeStaticMethod(this Type @this, string name, params object?[]? arguments) + public static object? InvokeStaticMethod(this Type @this, string name, object?[]? arguments = null) => @this.FindStaticMethod(name, arguments)? .MakeGenericMethod() - .InvokeMethod(null, arguments); + .InvokeMethod(arguments); /// /// => @ == (); @@ -267,65 +268,65 @@ public static bool Is(this Type @this, Type type) /// => .Any(@.Is); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static bool IsAny(this Type @this, params Type[] types) + public static bool IsAny(this Type @this, Type[] types) => types.Any(@this.Is); /// - /// => @.Is(()); + /// => @.Is([()]); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool IsAny(this Type @this) => @this.Is(typeof(T1)); /// - /// => @.IsAny((), - /// ()); + /// => @.IsAny([(), + /// ()]); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool IsAny(this Type @this) - => @this.IsAny(typeof(T1), typeof(T2)); + => @this.IsAny([typeof(T1), typeof(T2)]); /// - /// => @.IsAny((), + /// => @.IsAny([(), /// (), - /// ()); + /// ()]); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool IsAny(this Type @this) - => @this.IsAny(typeof(T1), typeof(T2), typeof(T3)); + => @this.IsAny([typeof(T1), typeof(T2), typeof(T3)]); /// - /// => @.IsAny((), + /// => @.IsAny([(), /// (), /// (), - /// ()); + /// ()]); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool IsAny(this Type @this) - => @this.IsAny(typeof(T1), typeof(T2), typeof(T3), typeof(T4)); + => @this.IsAny([typeof(T1), typeof(T2), typeof(T3), typeof(T4)]); /// - /// => @.IsAny((), + /// => @.IsAny([(), /// (), /// (), /// (), - /// ()); + /// ()]); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool IsAny(this Type @this) - => @this.IsAny(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)); + => @this.IsAny([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)]); /// - /// => @.IsAny((), + /// => @.IsAny([(), /// (), /// (), /// (), /// (), - /// ()); + /// ()]); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool IsAny(this Type @this) - => @this.IsAny(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)); + => @this.IsAny([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)]); /// /// @@ -402,7 +403,7 @@ public static void SetFieldValue(this Type @this, string name, object instance, .SetFieldValue(instance, value); [DebuggerHidden] - public static void SetPropertyValue(this Type @this, string name, object instance, object? value, params object?[]? index) + public static void SetPropertyValue(this Type @this, string name, object instance, object? value, object?[]? index = null) => @this.GetProperty(name, INSTANCE_BINDING_FLAGS)? .SetPropertyValue(instance, value, index); @@ -412,7 +413,7 @@ public static void SetStaticFieldValue(this Type @this, string name, object? val .SetFieldValue(null, value); [DebuggerHidden] - public static void SetStaticPropertyValue(this Type @this, string name, object? value, params object?[]? index) + public static void SetStaticPropertyValue(this Type @this, string name, object? value, object?[]? index = null) => @this.GetProperty(name, STATIC_BINDING_FLAGS)? .SetPropertyValue(null, value, index); @@ -442,11 +443,12 @@ public static LabelTarget ToLabelTarget(this Type @this, string? name) /// [DebuggerHidden] - 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())!.ToExpression(parameters) - }; + public static NewExpression ToNewExpression(this Type @this, Expression[]? parameters = null) + => parameters switch + { + null or { Length: 0 } => Expression.New(@this), + _ => @this.GetConstructor(parameters.Select(parameter => parameter.Type).ToArray())!.ToExpression(parameters) + }; /// /// @@ -461,7 +463,7 @@ public static MemberExpression ToStaticFieldExpression(this Type @this, string n /// => .Call(@, , , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression ToStaticMethodCallExpression(this Type @this, string method, params Expression[]? arguments) + public static MethodCallExpression ToStaticMethodCallExpression(this Type @this, string method, Expression[]? arguments = null) => Expression.Call(@this, method, Type.EmptyTypes, arguments); /// @@ -469,7 +471,7 @@ public static MethodCallExpression ToStaticMethodCallExpression(this Type @this, /// => .Call(@, , , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static MethodCallExpression ToStaticMethodCallExpression(this Type @this, string method, Type[]? genericTypes, params Expression[]? arguments) + public static MethodCallExpression ToStaticMethodCallExpression(this Type @this, string method, Type[]? genericTypes, Expression[]? arguments = null) => Expression.Call(@this, method, genericTypes, arguments); /// diff --git a/src/TypeCache/Extensions/StringBuilderExtensions.cs b/src/TypeCache/Extensions/StringBuilderExtensions.cs index 40e5f574..d553e3cf 100644 --- a/src/TypeCache/Extensions/StringBuilderExtensions.cs +++ b/src/TypeCache/Extensions/StringBuilderExtensions.cs @@ -21,14 +21,27 @@ 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); + /// + /// => ? @.Append() : @; + /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static StringBuilder AppendIf(this StringBuilder @this, bool condition, ReadOnlySpan value) => condition ? @this.Append(value) : @this; + /// + /// => @.Append( ? : ); + /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static StringBuilder AppendIf(this StringBuilder @this, bool condition, ReadOnlySpan trueValue, ReadOnlySpan falseValue) => condition ? @this.Append(trueValue) : @this.Append(falseValue); + /// + /// => ? action(@) : @; + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static StringBuilder AppendIf(this StringBuilder @this, bool condition, Func action) + => condition ? action(@this) : @this; + [DebuggerHidden] public static StringBuilder AppendIf(this StringBuilder @this, bool condition, T value) => value switch diff --git a/src/TypeCache/Extensions/StringExtensions.cs b/src/TypeCache/Extensions/StringExtensions.cs index 9fd8437b..9e28a5c6 100644 --- a/src/TypeCache/Extensions/StringExtensions.cs +++ b/src/TypeCache/Extensions/StringExtensions.cs @@ -89,7 +89,7 @@ public static bool AnyWhiteSpace(this string @this) /// => ?.Any(@.Contains) ; /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static bool ContainsAny(this string @this, params char[] chars) + public static bool ContainsAny(this string @this, char[]? chars = null) => chars?.Any(@this.Contains) is true; /// @@ -163,7 +163,7 @@ public static string Join(this string? @this, IEnumerable values) /// => @.AsSpan().Join(); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static string Join(this string? @this, params string[] values) + public static string Join(this string? @this, string[] values) => @this.AsSpan().Join(values); /// @@ -205,9 +205,9 @@ public static string Mask(this string @this, char mask = '*') return new string(span); } - public static string MaskHide(this string @this, char mask = '*', StringComparison comparison = StringComparison.OrdinalIgnoreCase, params string[] hideTerms) + public static string MaskHide(this string @this, char mask = '*', StringComparison comparison = StringComparison.OrdinalIgnoreCase, string[]? hideTerms = null) { - if (@this.IsBlank() || !hideTerms.Any()) + if (@this.IsBlank() || hideTerms?.Any() is not true) return @this; var count = 0; @@ -233,7 +233,7 @@ public static string MaskHide(this string @this, char mask = '*', StringComparis return new string(span); } - public static string MaskShow(this string @this, char mask = '*', StringComparison comparison = StringComparison.OrdinalIgnoreCase, params string[] showTerms) + public static string MaskShow(this string @this, char mask = '*', StringComparison comparison = StringComparison.OrdinalIgnoreCase, string[]? showTerms = null) { if (@this.IsBlank()) return @this; diff --git a/src/TypeCache/Mediation/Mediator.cs b/src/TypeCache/Mediation/Mediator.cs index dbfd07a2..79f54680 100644 --- a/src/TypeCache/Mediation/Mediator.cs +++ b/src/TypeCache/Mediation/Mediator.cs @@ -31,12 +31,12 @@ public async Task Execute(REQUEST request, CancellationToken token = de } catch (AggregateException error) { - logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.Execute)); + 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.Execute), error.Message); + logger?.LogError(error, "{mediator}.{function} failure: {message}", [nameof(Mediator), nameof(Mediator.Execute), error.Message]); await Task.FromException(error); } } @@ -63,12 +63,12 @@ public async Task Map(REQUEST request, Cancellation } catch (AggregateException error) { - logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.Map)); + 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.Map), error.Message); + logger?.LogError(error, "{mediator}.{function} failure: {message}", [nameof(Mediator), nameof(Mediator.Map), error.Message]); await Task.FromException(error); } @@ -76,7 +76,7 @@ public async Task Map(REQUEST request, Cancellation } public Task Map(IRequest request, CancellationToken token = default) - => (Task)this.GetType().InvokeMethod(nameof(Mediator.Map), [request.GetType(), typeof(RESPONSE)], this, request, token)!; + => (Task)this.GetType().InvokeMethod(nameof(Mediator.Map), [request.GetType(), typeof(RESPONSE)], this, [request, token])!; public void Validate(REQUEST request, CancellationToken token = default) where REQUEST : notnull @@ -95,7 +95,7 @@ public void Validate(REQUEST request, CancellationToken token = default } catch (AggregateException error) { - logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", nameof(Mediator), nameof(Mediator.Validate)); + logger?.LogAggregateException(error, "{mediator}.{function} aggregate failure.", [nameof(Mediator), nameof(Mediator.Validate)]); if (error.InnerExceptions.Count == 1) throw error.InnerException!; @@ -103,7 +103,7 @@ public void Validate(REQUEST request, CancellationToken token = default } catch (Exception error) { - logger?.LogError(error, "{mediator}.{function} failure: {message}", nameof(Mediator), nameof(Mediator.Validate), error.Message); + logger?.LogError(error, "{mediator}.{function} failure: {message}", [nameof(Mediator), nameof(Mediator.Validate), error.Message]); throw; } } diff --git a/src/TypeCache/TypeCache.csproj b/src/TypeCache/TypeCache.csproj index 3a0248f5..c8e985ed 100644 --- a/src/TypeCache/TypeCache.csproj +++ b/src/TypeCache/TypeCache.csproj @@ -6,7 +6,7 @@ TypeCache true TypeCache - 8.1.2 + 8.1.3 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache Reflection diff --git a/src/TypeCache/Utilities/Enum.cs b/src/TypeCache/Utilities/Enum.cs index 6287b5fb..d6822201 100644 --- a/src/TypeCache/Utilities/Enum.cs +++ b/src/TypeCache/Utilities/Enum.cs @@ -24,4 +24,7 @@ public static class Enum [DebuggerHidden] public static Type UnderlyingType => UnderlyingTypeHandle.ToType(); + + public static bool IsValid(T value) + => Enum.IsDefined(value) || (Flags && Values.Count(_ => value.HasFlag(_)) > 1); } diff --git a/src/TypeCache/Utilities/EventHandler.cs b/src/TypeCache/Utilities/EventHandler.cs index c010c386..174b1a61 100644 --- a/src/TypeCache/Utilities/EventHandler.cs +++ b/src/TypeCache/Utilities/EventHandler.cs @@ -31,7 +31,7 @@ public static long Add(T instance, string eventMemberName, Delegate handler) var key = DateTime.UtcNow.Ticks; var reference = new EventHandlerReference(instance.ToWeakReference(), eventInfo.AddMethod.MethodHandle, eventInfo.RemoveMethod?.MethodHandle, handler); EventHandlers.Add(key, reference); - eventInfo.AddMethod.InvokeMethod(instance, handler); + eventInfo.AddMethod.InvokeMethod([instance, handler]); return key; } @@ -47,7 +47,7 @@ public static bool Remove(long key) return false; if (handler.Instance.TryGetTarget(out var target)) - handler.RemoveMethodHandle?.ToMethodBase().InvokeMethod(target, handler.EventHandler); + handler.RemoveMethodHandle?.ToMethodBase().InvokeMethod([target, handler.EventHandler]); return EventHandlers.Remove(key); } diff --git a/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj b/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj index 85a68e5a..a8088e42 100644 --- a/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj +++ b/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/tests/TypeCache.Tests/Data/Extensions/SqlExtensions.cs b/tests/TypeCache.Tests/Data/Extensions/SqlExtensions.cs index bb1eb301..7a65968b 100644 --- a/tests/TypeCache.Tests/Data/Extensions/SqlExtensions.cs +++ b/tests/TypeCache.Tests/Data/Extensions/SqlExtensions.cs @@ -53,14 +53,7 @@ public void CreateDeleteBatchSQL() var expected = Invariant($@"DELETE {table} OUTPUT INSERTED.[First Name] AS [First Name], DELETED.[Last_Name] AS [Last_Name], INSERTED.ID -FROM {table} AS _ -INNER JOIN -( -VALUES (1) - , (2) - , (3) -) AS data (EscapeIdentifier) -ON data.EscapeIdentifier = _.EscapeIdentifier; +WHERE [ID] IN (1, 2, 3); "); Person[] data = [new() { ID = 1 }, new() { ID = 2 }, new() { ID = 3 }]; var actual = objectSchema.CreateDeleteSQL(data, new[] { "INSERTED.[First Name] AS [First Name]", "DELETED.[Last_Name] AS [Last_Name]", "INSERTED.ID" }); @@ -84,7 +77,7 @@ public void CreateDeleteSQL() OUTPUT DELETED.[ID], DELETED.[First Name], DELETED.[Last_Name] WHERE [First Name] = N'Sarah' AND [Last_Name] = N'Marshal'; "); - var actual = objectSchema.CreateDeleteSQL("[First Name] = N'Sarah' AND [Last_Name] = N'Marshal'", "DELETED.[ID]", "DELETED.[First Name]", "DELETED.[Last_Name]"); + var actual = objectSchema.CreateDeleteSQL("[First Name] = N'Sarah' AND [Last_Name] = N'Marshal'", ["DELETED.[ID]", "DELETED.[First Name]", "DELETED.[Last_Name]"]); Assert.Equal(expected, actual); } @@ -108,13 +101,13 @@ public void CreateBatchInsertSQL() ]; var expected = Invariant($@"INSERT INTO {table} -(EscapeIdentifier, EscapeIdentifier) +([FirstName], [LastName]) OUTPUT INSERTED.ID, INSERTED.[LastName] VALUES (N'FirstName1', N'LastName1') , (N'FirstName2', N'LastName2') , (N'FirstName3', N'LastName3'); "); - var actual = objectSchema.CreateInsertSQL(["FirstName", "LastName"], data, "INSERTED.ID", "INSERTED.[LastName]"); + var actual = objectSchema.CreateInsertSQL(["FirstName", "LastName"], data, ["INSERTED.ID", "INSERTED.[LastName]"]); Assert.Equal(expected, actual); } @@ -136,12 +129,12 @@ public void CreateInsertSQL() Having = "MAX([Age]) > 40", OrderBy = ["[First Name] ASC", "Last_Name DESC"], Select = ["ID", "TRIM([First Name]) AS [First Name]", "UPPER([LastName]) AS LastName", "40 Age", "Amount AS Amount"], - TableHints = "WITH(NOLOCK)", + TableHints = "NOLOCK", Where = "[First Name] = N'Sarah' AND [Last_Name] = N'Marshal'", }; var expected = Invariant($@"INSERT INTO {table} -(EscapeIdentifier, EscapeIdentifier, EscapeIdentifier, EscapeIdentifier) +([[First Name]]], [[Last_Name]]], [Age], [Amount]) OUTPUT INSERTED.[First Name] AS [First Name], INSERTED.[ID] AS [ID] SELECT ID, TRIM([First Name]) AS [First Name], UPPER([LastName]) AS LastName, 40 Age, Amount AS Amount FROM [dbo].[NonCustomers] WITH(NOLOCK) @@ -149,7 +142,7 @@ FROM [dbo].[NonCustomers] WITH(NOLOCK) HAVING MAX([Age]) > 40 ORDER BY [First Name] ASC, Last_Name DESC; "); - var actual = objectSchema.CreateInsertSQL(["[First Name]", "[Last_Name]", "Age", "Amount"], selectQuery, "INSERTED.[First Name] AS [First Name]", "INSERTED.[ID] AS [ID]"); + var actual = objectSchema.CreateInsertSQL(["[First Name]", "[Last_Name]", "Age", "Amount"], selectQuery, ["INSERTED.[First Name] AS [First Name]", "INSERTED.[ID] AS [ID]"]); Assert.Equal(expected, actual); } @@ -173,7 +166,7 @@ public void CreateSelectSQL() OrderBy = ["[FirstName] ASC", "LastName DESC"], Fetch = 100, Offset = 0, - TableHints = "WITH(NOLOCK)" + TableHints = "NOLOCK" }; var expected = Invariant($@"SELECT ID, TRIM([FirstName]) AS [FirstName], UPPER([LastName]) AS LastName, 40 Age, Amount AS Amount @@ -207,7 +200,7 @@ public void CreateBatchUpdateSQL() ]; var expected = Invariant($@"UPDATE {table} WITH(UPDLOCK) -SET EscapeIdentifier = data.EscapeIdentifier, EscapeIdentifier = data.EscapeIdentifier +SET [FirstName] = data.[FirstName], [LastName] = data.[LastName] OUTPUT INSERTED.[FirstName] AS [FirstName], DELETED.LastName AS LastName, INSERTED.[ID] AS [ID] FROM {table} AS _ INNER JOIN @@ -215,11 +208,11 @@ INNER JOIN VALUES (N'FirstName1', N'LastName1') , (N'FirstName2', N'LastName2') , (N'FirstName3', N'LastName3') -) AS data (EscapeIdentifier, EscapeIdentifier) -ON data.EscapeIdentifier = _.EscapeIdentifier; +) AS data ([FirstName], [LastName]) +ON data.[ID] = _.[ID]; "); var actual = objectSchema.CreateUpdateSQL(["FirstName", "LastName"], data - , "INSERTED.[FirstName] AS [FirstName]", "DELETED.LastName AS LastName", "INSERTED.[ID] AS [ID]"); + , ["INSERTED.[FirstName] AS [FirstName]", "DELETED.LastName AS LastName", "INSERTED.[ID] AS [ID]"]); Assert.Equal(expected, actual); } @@ -252,9 +245,6 @@ private static IDataSource CreateDataSourceMock(DataSourceType dataSourceType) { var dataSourceMock = Substitute.For(); dataSourceMock.Type.Returns(dataSourceType); - dataSourceMock.EscapeIdentifier(Arg.Any()).Returns(nameof(dataSourceMock.EscapeIdentifier)); - dataSourceMock.EscapeLikeValue(Arg.Any()).Returns(nameof(dataSourceMock.EscapeLikeValue)); - dataSourceMock.EscapeValue(Arg.Any()).Returns(nameof(dataSourceMock.EscapeValue)); return dataSourceMock; } } diff --git a/tests/TypeCache.Tests/Extensions/CharExtensions.cs b/tests/TypeCache.Tests/Extensions/CharExtensions.cs index 211cc1af..0811205c 100644 --- a/tests/TypeCache.Tests/Extensions/CharExtensions.cs +++ b/tests/TypeCache.Tests/Extensions/CharExtensions.cs @@ -13,9 +13,9 @@ public class CharExtensions [Fact] public void Join() { - Assert.Equal("A,b,C,1,2,3", ','.Join("A", "b", "C", "1", "2", "3")); + Assert.Equal("A,b,C,1,2,3", ','.Join(["A", "b", "C", "1", "2", "3"])); Assert.Equal("A.b.C.1.2.3", '.'.Join(new[] { "A", "b", "C", "1", "2", "3" }.AsEnumerable())); - Assert.Equal("A;b;C;1;2;3", ';'.Join('A', 'b', 'C', 1, 2, 3)); + Assert.Equal("A;b;C;1;2;3", ';'.Join(['A', 'b', 'C', 1, 2, 3])); Assert.Equal("A|b|C|1|2|3", '|'.Join(new object[] { 'A', 'b', 'C', 1, 2, 3 }.AsEnumerable())); } diff --git a/tests/TypeCache.Tests/Extensions/StringExtensions.cs b/tests/TypeCache.Tests/Extensions/StringExtensions.cs index f94cfe91..abe8dd51 100644 --- a/tests/TypeCache.Tests/Extensions/StringExtensions.cs +++ b/tests/TypeCache.Tests/Extensions/StringExtensions.cs @@ -62,8 +62,8 @@ public void IsNotBlank() [Fact] public void Join() { - Assert.Equal(TEST_STRING, " ".Join("AaBbCc", "123", "`~!#$%^\t\r\n")); - Assert.Equal(TEST_STRING, " ".Join(TEST_STRING)); + Assert.Equal(TEST_STRING, " ".Join(["AaBbCc", "123", "`~!#$%^\t\r\n"])); + Assert.Equal(TEST_STRING, " ".Join([TEST_STRING])); } [Fact] @@ -89,13 +89,13 @@ public void Mask() [Fact] public void MaskHide() { - Assert.Equal("--Bb-- 123 `~!#$%^\t\r\n", TEST_STRING.MaskHide('-', StringComparison.OrdinalIgnoreCase, "A", "C", "\t\r\n")); + Assert.Equal("--Bb-- 123 `~!#$%^\t\r\n", TEST_STRING.MaskHide('-', StringComparison.OrdinalIgnoreCase, ["A", "C", "\t\r\n"])); } [Fact] public void MaskShow() { - Assert.Equal("ooBboo 123 `~!#$%^\t\r\n", TEST_STRING.MaskShow('o', StringComparison.Ordinal, "Bb", " ", "123", "`~!#$%^")); + Assert.Equal("ooBboo 123 `~!#$%^\t\r\n", TEST_STRING.MaskShow('o', StringComparison.Ordinal, ["Bb", " ", "123", "`~!#$%^"])); } [Fact] diff --git a/tests/TypeCache.Tests/TypeCache.Tests.csproj b/tests/TypeCache.Tests/TypeCache.Tests.csproj index 310fd43b..98356b27 100644 --- a/tests/TypeCache.Tests/TypeCache.Tests.csproj +++ b/tests/TypeCache.Tests/TypeCache.Tests.csproj @@ -9,14 +9,14 @@ - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all