diff --git a/src/TypeCache.GraphQL/Data/Connection.cs b/src/TypeCache.GraphQL/Data/Connection.cs new file mode 100644 index 00000000..ce566933 --- /dev/null +++ b/src/TypeCache.GraphQL/Data/Connection.cs @@ -0,0 +1,76 @@ +// Copyright (c) 2021 Samuel Abraham + +using System.Linq; +using GraphQL.Resolvers; +using GraphQL.Types; +using TypeCache.GraphQL.Attributes; +using TypeCache.GraphQL.Extensions; +using TypeCache.GraphQL.Types; +using static System.FormattableString; + +namespace TypeCache.GraphQL.Data; + +public class Connection + where T : notnull +{ + public Connection() + { + } + + public Connection(uint offset, T[] items) + { + offset += 1; + this.Items = items; + this.Edges = items.Select((row, i) => new Edge((int)offset + i, row)).ToArray(); + } + + [GraphQLDescription("The total number of records available. Returns `null` if the total number is unknown.")] + public int? TotalCount { get; init; } + + [GraphQLDescription("Pagination information for this result data set.")] + public PageInfo? PageInfo { get; init; } + + [GraphQLDescription("The result data set, stored as a list of edges containing a node (the data) and a cursor (a unique identifier for the data).")] + public Edge[]? Edges { get; init; } + + [GraphQLDescription("The result data set.")] + public T[]? Items { get; init; } + + public static ObjectGraphType CreateGraphType(string name, IGraphType dataGraphType) + { + var graphType = new ObjectGraphType + { + Name = Invariant($"{name}{nameof(Connection)}"), + Description = typeof(Connection).GraphQLDescription() + }; + var edgeGraphType = Edge.CreateGraphType(name, dataGraphType); + + graphType.AddField(new() + { + Name = nameof(Connection.Edges), + ResolvedType = new ListGraphType(new NonNullGraphType(edgeGraphType)), + Resolver = new FuncFieldResolver, Edge[]?>(context => context.Source.Edges) + }); + graphType.AddField(new() + { + Name = nameof(Connection.Items), + ResolvedType = new ListGraphType(new NonNullGraphType(dataGraphType)), + Resolver = new FuncFieldResolver, T[]?>(context => context.Source.Items) + }); + graphType.AddField(new() + { + Name = nameof(Connection.PageInfo), + ResolvedType = new GraphQLObjectType(Invariant($"{name}{nameof(PageInfo)}")), + //Type = typeof(GraphQLObjectType), + Resolver = new FuncFieldResolver, PageInfo?>(context => context.Source.PageInfo) + }); + graphType.AddField(new() + { + Name = nameof(Connection.TotalCount), + Type = typeof(IntGraphType), + Resolver = new FuncFieldResolver, int?>(context => context.Source.TotalCount) + }); + + return graphType; + } +} diff --git a/src/TypeCache.GraphQL/Data/Edge.cs b/src/TypeCache.GraphQL/Data/Edge.cs new file mode 100644 index 00000000..ac9da357 --- /dev/null +++ b/src/TypeCache.GraphQL/Data/Edge.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2021 Samuel Abraham + +using GraphQL.Resolvers; +using GraphQL.Types; +using TypeCache.GraphQL.Attributes; +using TypeCache.GraphQL.Extensions; +using static System.FormattableString; + +namespace TypeCache.GraphQL.Data; + +[GraphQLDescription("An edge in a connection that associates a cursor ID with data.")] +public readonly record struct Edge( + [GraphQLDescription("An identification number for use in pagination.")] int Cursor + , [GraphQLDescription("The data item associated with the cursor value.")] T Node) + where T : notnull +{ + public static ObjectGraphType CreateGraphType(string name, IGraphType dataGraphType) + { + var graphType = new ObjectGraphType + { + Name = Invariant($"{name}{nameof(Edge)}"), + Description = typeof(Edge).GraphQLDescription() + }; + + graphType.AddField(new() + { + Name = nameof(Edge.Cursor), + Type = typeof(IntGraphType), + Resolver = new FuncFieldResolver, int>(context => context.Source.Cursor) + }); + graphType.AddField(new() + { + Name = nameof(Edge.Node), + ResolvedType = new NonNullGraphType(dataGraphType), + Resolver = new FuncFieldResolver, T>(context => context.Source.Node) + }); + + return graphType; + } +} diff --git a/src/TypeCache.GraphQL/Data/PageInfo.cs b/src/TypeCache.GraphQL/Data/PageInfo.cs new file mode 100644 index 00000000..3619a060 --- /dev/null +++ b/src/TypeCache.GraphQL/Data/PageInfo.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2021 Samuel Abraham + +using GraphQL.Resolvers; +using GraphQL.Types; +using TypeCache.GraphQL.Attributes; +using static System.FormattableString; + +namespace TypeCache.GraphQL.Data; + +[GraphQLDescription("Information about pagination in a connection.")] +public readonly record struct PageInfo( + [GraphQLDescription("The first cursor value of the current page.")] int StartCursor + , [GraphQLDescription("The last cursor value of the current page.")] int EndCursor + , [GraphQLDescription("Whether a page exists before the current page.")] bool HasPreviousPage + , [GraphQLDescription("Whether a page exists after the current page.")] bool HasNextPage) +{ + public PageInfo(uint offset, uint fetch, int totalCount) + : this((int)offset + 1, (int)(offset + fetch), offset > 0, (offset + fetch) < totalCount) + { + } +} diff --git a/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs b/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs index 54616021..749b820e 100644 --- a/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/EnumExtensions.cs @@ -8,30 +8,29 @@ namespace TypeCache.GraphQL.Extensions; public static class EnumExtensions { - public static Type? ToGraphType(this SystemType @this) - => @this switch - { - SystemType.String or SystemType.Char or SystemType.Range => typeof(StringGraphType), - SystemType.Uri => typeof(UriGraphType), - SystemType.Boolean => typeof(BooleanGraphType), - SystemType.SByte => typeof(SByteGraphType), - SystemType.Int16 => typeof(ShortGraphType), - SystemType.Int32 or SystemType.Index => typeof(IntGraphType), - SystemType.Int64 or SystemType.IntPtr => typeof(LongGraphType), - SystemType.Int128 or SystemType.UInt128 or SystemType.BigInteger => typeof(BigIntGraphType), - SystemType.Byte => typeof(ByteGraphType), - SystemType.UInt16 => typeof(UShortGraphType), - SystemType.UInt32 => typeof(UIntGraphType), - SystemType.UInt64 or SystemType.UIntPtr => typeof(ULongGraphType), - SystemType.Half => typeof(HalfGraphType), - SystemType.Single or SystemType.Double => typeof(FloatGraphType), - SystemType.Decimal => typeof(DecimalGraphType), - SystemType.DateOnly => typeof(DateOnlyGraphType), - SystemType.DateTime => typeof(DateTimeGraphType), - SystemType.DateTimeOffset => typeof(DateTimeOffsetGraphType), - SystemType.TimeOnly => typeof(TimeOnlyGraphType), - SystemType.TimeSpan => typeof(TimeSpanSecondsGraphType), - SystemType.Guid => typeof(GuidGraphType), - _ => null - }; + public static Type? ToGraphType(this SystemType @this) => @this switch + { + SystemType.String or SystemType.Char or SystemType.Range => typeof(StringGraphType), + SystemType.Uri => typeof(UriGraphType), + SystemType.Boolean => typeof(BooleanGraphType), + SystemType.SByte => typeof(SByteGraphType), + SystemType.Int16 => typeof(ShortGraphType), + SystemType.Int32 or SystemType.Index => typeof(IntGraphType), + SystemType.Int64 or SystemType.IntPtr => typeof(LongGraphType), + SystemType.Int128 or SystemType.UInt128 or SystemType.BigInteger => typeof(BigIntGraphType), + SystemType.Byte => typeof(ByteGraphType), + SystemType.UInt16 => typeof(UShortGraphType), + SystemType.UInt32 => typeof(UIntGraphType), + SystemType.UInt64 or SystemType.UIntPtr => typeof(ULongGraphType), + SystemType.Half => typeof(HalfGraphType), + SystemType.Single or SystemType.Double => typeof(FloatGraphType), + SystemType.Decimal => typeof(DecimalGraphType), + SystemType.DateOnly => typeof(DateOnlyGraphType), + SystemType.DateTime => typeof(DateTimeGraphType), + SystemType.DateTimeOffset => typeof(DateTimeOffsetGraphType), + SystemType.TimeOnly => typeof(TimeOnlyGraphType), + SystemType.TimeSpan => typeof(TimeSpanSecondsGraphType), + SystemType.Guid => typeof(GuidGraphType), + _ => null + }; } diff --git a/src/TypeCache.GraphQL/Extensions/GraphQLAttributeExtensions.cs b/src/TypeCache.GraphQL/Extensions/GraphQLAttributeExtensions.cs index 1dbbebd9..025c334b 100644 --- a/src/TypeCache.GraphQL/Extensions/GraphQLAttributeExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/GraphQLAttributeExtensions.cs @@ -42,15 +42,15 @@ public static bool GraphQLIgnore(this ParameterInfo @this) public static string GraphQLInputName(this Type @this) => @this.GetCustomAttribute()?.Name ?? (@this.IsGenericType - ? Invariant($"{@this.Name()}{string.Join(string.Empty, @this.GenericTypeArguments.Select(_ => _.GraphQLName()))}") + ? Invariant($"{@this.Name()}{@this.GenericTypeArguments.Select(_ => _.GraphQLName()).Concat()}") : Invariant($"{@this.GraphQLName()}Input")); public static string GraphQLName(this MemberInfo @this) => @this.GetCustomAttribute()?.Name ?? @this switch { - MethodInfo methodInfo => methodInfo.Name().TrimEnd("Async"), - Type type when type.IsGenericType => Invariant($"{type.Name()}{string.Join(string.Empty, type.GenericTypeArguments.Select(_ => _.GraphQLName()))}"), - _ => @this.Name() + MethodInfo methodInfo => methodInfo.Name.TrimEnd("Async"), + Type type when type.IsGenericType => Invariant($"{type.Name()}{type.GenericTypeArguments.Select(_ => _.GraphQLName()).Concat()}"), + _ => @this.Name }; [MethodImpl(AggressiveInlining), DebuggerHidden] diff --git a/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs b/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs index 2cb1dd66..00b457ff 100644 --- a/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs @@ -7,11 +7,13 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using GraphQL; using GraphQL.Resolvers; using GraphQL.Types; -using GraphQL.Types.Relay.DataObjects; +using GraphQLParser.AST; using TypeCache.Extensions; +using TypeCache.GraphQL.Data; using TypeCache.GraphQL.Resolvers; using TypeCache.GraphQL.SqlApi; using TypeCache.GraphQL.Types; @@ -51,31 +53,18 @@ public static void AddOrderBy(this EnumerationGraphType @this, OrderBy orderBy, /// /// . /// The data. - /// The total record count of the record set being paged. /// The number of records to skip. + /// The total record count of the record set being paged. /// The . - public static Connection ToConnection(this IEnumerable data, int totalCount, uint offset) + public static Connection ToConnection(this IEnumerable data, uint offset, int totalCount) + where T : notnull { var items = data.AsArray(); - var start = offset + 1; - var end = start + items.Length; - var connection = new Connection + return new(offset, items) { - Edges = items.Select((item, i) => new Edge - { - Cursor = (start + i).ToString(), - Node = item - }).ToList(), - PageInfo = new() - { - StartCursor = start.ToString(), - EndCursor = end.ToString(), - HasNextPage = end < totalCount, - HasPreviousPage = offset > 0 - }, + PageInfo = new(offset, offset + (uint)items.Length, totalCount), TotalCount = totalCount }; - return connection; } /// @@ -130,39 +119,36 @@ public static Type ToGraphQLType(this Type @this, bool isInputType) (objectType is ObjectType.Delegate).AssertFalse(); (objectType is ObjectType.Object).AssertFalse(); - if (objectType.IsAny(ObjectType.Dictionary, ObjectType.ReadOnlyDictionary)) + if (objectType is ObjectType.Dictionary || objectType is ObjectType.ReadOnlyDictionary) return typeof(KeyValuePair<,>).MakeGenericType(@this.GenericTypeArguments).ToGraphQLType(isInputType).ToNonNullGraphType().ToListGraphType(); if (@this.IsEnum) return @this.ToGraphQLEnumType(); var systemType = @this.GetSystemType(); + if (systemType is SystemType.Task || systemType is SystemType.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.")); + + if (systemType is SystemType.Nullable) + return @this.GenericTypeArguments.First()!.ToGraphQLType(isInputType); + var systemGraphType = systemType.ToGraphType(); if (systemGraphType is not null) return systemGraphType; if (@this.HasElementType) { - var elementType = @this.GetElementType()!; - elementType = elementType.GetSystemType() is not SystemType.Nullable && elementType.IsValueType - ? elementType.ToGraphQLType(isInputType).ToNonNullGraphType() - : elementType.ToGraphQLType(isInputType); + var elementType = @this.GetElementType()!.ToGraphQLType(isInputType); + if (elementType.IsValueType && elementType.GetSystemType() is not SystemType.Nullable) + elementType = elementType.ToNonNullGraphType(); + return elementType.ToListGraphType(); } - if (@this.IsGenericType) - { - @this.GenericTypeArguments.Length.AssertEquals(1); - - var genericType = @this.GenericTypeArguments.First()!; - return (systemType, genericType.GetSystemType()) switch - { - (SystemType.Nullable, _) => genericType.GetSystemType().ToGraphType()!, - (SystemType.Task or SystemType.ValueTask, _) => genericType.ToGraphQLType(false), - (_, not SystemType.Nullable) when genericType.IsValueType => genericType.ToGraphQLType(isInputType).ToNonNullGraphType().ToListGraphType(), - _ => genericType.ToGraphQLType(isInputType).ToListGraphType(), - }; - } + if (@this.IsGenericType && @this.IsOrImplements(typeof(IEnumerable<>))) + return @this.GenericTypeArguments.First()!.ToGraphQLType(isInputType).ToListGraphType(); if (@this.IsInterface) return @this.ToGraphQLInterfaceType(); diff --git a/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs b/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs index aed2b7b9..69e6c0b6 100644 --- a/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/SchemaExtensions.cs @@ -19,6 +19,7 @@ using TypeCache.GraphQL.Resolvers; using TypeCache.GraphQL.SqlApi; using TypeCache.GraphQL.Types; +using TypeCache.Mediation; using static System.FormattableString; namespace TypeCache.GraphQL.Extensions; @@ -214,9 +215,9 @@ public static void AddDatabaseEndpoints(this ISchema @this, IDataSource dataSour graphOrderByEnum.AddOrderBy(new(column.Name, Sort.Descending)); } - if (objectSchema.Type.IsAny(DatabaseObjectType.Table, DatabaseObjectType.View) && actions.HasFlag(SqlApiAction.Select)) + if ((objectSchema.Type is DatabaseObjectType.Table || objectSchema.Type is DatabaseObjectType.View) && actions.HasFlag(SqlApiAction.Select)) { - var selectResponseType = SelectResponse.CreateGraphType(table, $"{objectSchema.Type.Name()}: `{objectSchema.Name}`", resolvedType); + 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."); if (dataSource.Type is DataSourceType.SqlServer) @@ -774,15 +775,7 @@ public static FieldType[] AddSubscriptions(this ISchema @this, string method) /// The added . /// public static FieldType AddSubscription(this ISchema @this, MethodInfo methodInfo) - { - var returnsObservable = methodInfo.ReturnType.IsOrImplements(typeof(IObservable<>)); - if (!returnsObservable && methodInfo.ReturnType.GetSystemType().IsAny(SystemType.ValueTask, SystemType.Task)) - returnsObservable = methodInfo.ReturnType.GenericTypeArguments.Single().GetObjectType() == ObjectType.Observable; - - returnsObservable.AssertTrue(); - - return @this.Subscription().AddField(methodInfo, new MethodSourceStreamResolver(methodInfo)); - } + => @this.Subscription().AddField(methodInfo, new MethodSourceStreamResolver(methodInfo)); /// /// Creates the following GraphQL endpoints: @@ -797,7 +790,7 @@ public static FieldType AddSubscription(this ISchema @this, MethodInfo methodInf /// Mutation: update{Table}Data Updates a batch of records based on a table's Primary Key. /// /// Requires call to: - /// + /// /// /// /// @@ -834,7 +827,7 @@ public static void AddSqlApiEndpoints(this ISchema @this, IDataSource dataSou /// Mutation: call{Procedure} Calls the stored procedure and returns its results. /// /// Requires call to: - /// + /// /// /// /// @@ -879,7 +872,7 @@ public static FieldType AddSqlApiCallProcedureEndpoint(this ISchema @this, ID /// Mutation: delete{Table}Data Deletes records passed in based on primary key value(s). /// /// Requires call to: - /// + /// /// /// /// @@ -914,7 +907,7 @@ public static FieldType AddSqlApiDeleteDataEndpoint(this ISchema @this, IData /// Mutation: Delete{Table} Deletes records based on a WHERE clause. /// /// Requires call to: - /// + /// /// /// /// @@ -950,7 +943,7 @@ public static FieldType AddSqlApiDeleteEndpoint(this ISchema @this, IDataSour /// Mutation: insert{Table}Data Inserts a batch of records. /// /// Requires call to: - /// + /// /// /// /// @@ -986,7 +979,7 @@ public static FieldType AddSqlApiInsertDataEndpoint(this ISchema @this, IData /// Mutation: insert{Table}Data Inserts a batch of records. /// /// Requires call to: - /// + /// /// /// /// @@ -1043,7 +1036,7 @@ public static FieldType AddSqlApiInsertEndpoint(this ISchema @this, IDataSour /// Query: select{Table} Selects records based on a WHERE clause. /// /// Requires call to: - /// + /// /// /// /// @@ -1099,7 +1092,7 @@ public static FieldType AddSqlApiSelectEndpoint(this ISchema @this, IDataSour /// Mutation: update{Table}Data Updates a batch of records. /// /// Requires call to: - /// + /// /// /// /// @@ -1134,7 +1127,7 @@ public static FieldType AddSqlApiUpdateDataEndpoint(this ISchema @this, IData /// Mutation: update{Table} Updates records based on a WHERE clause. /// /// Requires call to: - /// + /// /// /// /// @@ -1170,6 +1163,6 @@ private static string FixName(string name) if (!name.Contains('_')) return name; - return string.Join(string.Empty, name.ToLowerInvariant().Split('_').Select(_ => _.ToPascalCase())); + return name.ToLowerInvariant().Split('_').Select(_ => _.ToPascalCase()).Concat(); } } diff --git a/src/TypeCache.GraphQL/Resolvers/MethodSourceStreamResolver.cs b/src/TypeCache.GraphQL/Resolvers/MethodSourceStreamResolver.cs index 15279b49..e1fff92e 100644 --- a/src/TypeCache.GraphQL/Resolvers/MethodSourceStreamResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/MethodSourceStreamResolver.cs @@ -18,13 +18,17 @@ public sealed class MethodSourceStreamResolver : SourceStreamResolver public MethodSourceStreamResolver(MethodInfo methodInfo) { - var returnsObservable = methodInfo.ReturnType.GetObjectType() == ObjectType.Observable; - if (!returnsObservable && methodInfo.ReturnType.GetSystemType().IsAny(SystemType.ValueTask, SystemType.Task)) - returnsObservable = methodInfo.ReturnType.GenericTypeArguments.Single().GetObjectType() == ObjectType.Observable; + var returnsObservable = methodInfo.ReturnType.IsOrImplements(typeof(IObservable<>)); + if (!returnsObservable) + { + var returnType = methodInfo.ReturnType.GetSystemType(); + if (returnType is SystemType.Task || returnType is SystemType.ValueTask) + returnsObservable = methodInfo.ReturnType.GenericTypeArguments.Single().GetObjectType() is ObjectType.Observable; - returnsObservable.AssertTrue(); + returnsObservable.AssertTrue(); + } - this._MethodInfo = methodInfo; + this._MethodInfo = methodInfo; } protected override ValueTask> ResolveAsync(global::GraphQL.IResolveFieldContext context) @@ -39,7 +43,7 @@ public MethodSourceStreamResolver(MethodInfo methodInfo) return result switch { ValueTask> valueTask => valueTask, - Task> task => new ValueTask>(task), + Task> task => new(task), IObservable item => ValueTask.FromResult(item), _ => throw new UnreachableException(Invariant($"Method {this._MethodInfo.Name()} returned a null for {this._MethodInfo.ReturnType.Name()}.")) }; diff --git a/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs index d3fd2523..740fa37f 100644 --- a/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/SqlApiSelectFieldResolver.cs @@ -4,11 +4,11 @@ using System.Linq; using System.Threading.Tasks; using GraphQL; -using GraphQL.Types.Relay.DataObjects; using Microsoft.Extensions.DependencyInjection; using TypeCache.Data; using TypeCache.Data.Extensions; using TypeCache.Extensions; +using TypeCache.GraphQL.Data; using TypeCache.GraphQL.Extensions; using TypeCache.GraphQL.SqlApi; using TypeCache.Mediation; @@ -32,8 +32,8 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver Offset = context.GetArgument(nameof(SelectQuery.Offset)), OrderBy = context.GetArgument(nameof(SelectQuery.OrderBy)), Select = objectSchema.Columns - .Where(column => selections.Any(_ => _.Right(Invariant($"{nameof(SelectResponse.Items)}.{column.Name}")) - || _.Right(Invariant($"{nameof(SelectResponse.Edges)}.{nameof(Edge.Node)}.{column.Name}")))) + .Where(column => selections.Any(_ => _.Right(Invariant($"{nameof(Connection.Items)}.{column.Name}")) + || _.Right(Invariant($"{nameof(Connection.Edges)}.{nameof(Edge.Node)}.{column.Name}")))) .Select(column => column.Name) .ToArray(), TableHints = objectSchema.DataSource.Type is SqlServer ? "WITH(NOLOCK)" : null, @@ -45,8 +45,8 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver context.GetArgument("parameters")?.ForEach(parameter => sqlCommand.Parameters[parameter.Name] = parameter.Value); - if (selections.Any(_ => _.Left(Invariant($"{nameof(SelectResponse.Items)}.")) - || _.Left(Invariant($"{nameof(SelectResponse.Edges)}.{nameof(Edge.Node)}.")))) + if (selections.Any(_ => _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.Items)}.")) + || _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.Edges)}.{nameof(Edge.Node)}.")))) { var result = await mediator.MapAsync(sqlCommand.ToSqlDataTableRequest(), context.CancellationToken); var totalCount = result.Rows.Count; @@ -58,46 +58,29 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver } var rows = result.Select(); - return new SelectResponse() + return new SelectResponse(rows, totalCount, select.Offset) { DataSource = objectSchema.DataSource.Name, - Edges = rows.Select((row, i) => new Edge - { - Cursor = (select.Offset + i + 1).ToString(), - Node = row - }).ToArray(), - Items = rows, - PageInfo = new PageInfo - { - StartCursor = (select.Offset + 1).ToString(), - EndCursor = (select.Fetch + select.Offset).ToString(), - HasNextPage = (select.Fetch + select.Offset) < result.Rows.Count, - HasPreviousPage = select.Offset > 0 - }, Sql = sql, Table = objectSchema.Name, - TotalCount = totalCount }; } - else if (selections.Any(_ => _.Is(nameof(SelectResponse.TotalCount)) - || _.Left(Invariant($"{nameof(SelectResponse.PageInfo)}.")))) + else if (selections.Any(_ => _.Is(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.TotalCount)}")) + || _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.PageInfo)}.")))) { var countSql = objectSchema.CreateCountSQL(null, select.Where); var countCommand = objectSchema.DataSource.CreateSqlCommand(countSql); var totalCount = (int?)await mediator.MapAsync(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; return new SelectResponse() { - DataSource = objectSchema.DataSource.Name, - PageInfo = new PageInfo + Data = new() { - StartCursor = (select.Offset + 1).ToString(), - EndCursor = (select.Fetch + select.Offset).ToString(), - HasNextPage = (select.Fetch + select.Offset) < totalCount, - HasPreviousPage = select.Offset > 0 + PageInfo = new(select.Offset, select.Fetch, totalCount), + TotalCount = totalCount }, + DataSource = objectSchema.DataSource.Name, Sql = countSql, - Table = objectSchema.Name, - TotalCount = totalCount + Table = objectSchema.Name }; } else @@ -110,7 +93,7 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver } public sealed class SqlApiSelectFieldResolver : FieldResolver - where T : new() + where T : notnull, new() { protected override async ValueTask ResolveAsync(IResolveFieldContext context) { @@ -125,8 +108,8 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver Offset = context.GetArgument(nameof(SelectQuery.Offset)), OrderBy = context.GetArgument(nameof(SelectQuery.OrderBy)), Select = objectSchema.Columns - .Where(column => selections.Any(_ => _.Right(Invariant($"{nameof(SelectResponse.Items)}.{column.Name}")) - || _.Right(Invariant($"{nameof(SelectResponse.Edges)}.{nameof(Edge.Node)}.{column.Name}")))) + .Where(column => selections.Any(_ => _.Right(Invariant($"{nameof(Connection.Items)}.{column.Name}")) + || _.Right(Invariant($"{nameof(Connection.Edges)}.{nameof(Edge.Node)}.{column.Name}")))) .Select(column => column.Name) .ToArray(), TableHints = objectSchema.DataSource.Type is SqlServer ? "WITH(NOLOCK)" : null, @@ -138,8 +121,8 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver context.GetArgument("parameters")?.ForEach(parameter => sqlCommand.Parameters[parameter.Name] = parameter.Value); - if (selections.Any(_ => _.Left(Invariant($"{nameof(SelectResponse.Items)}.")) - || _.Left(Invariant($"{nameof(SelectResponse.Edges)}.{nameof(Edge.Node)}.")))) + if (selections.Any(_ => _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.Items)}.")) + || _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.Edges)}.{nameof(Edge.Node)}.")))) { var result = await mediator.MapAsync(sqlCommand.ToSqlModelsRequest((int)select.Fetch), context.CancellationToken); var totalCount = result.Count; @@ -151,46 +134,29 @@ public sealed class SqlApiSelectFieldResolver : FieldResolver } var items = result.OfType().ToArray(); - return new SelectResponse() + return new SelectResponse(items, totalCount, select.Offset) { DataSource = objectSchema.DataSource.Name, - Edges = items.Select((row, i) => new Edge - { - Cursor = (select.Offset + i + 1).ToString(), - Node = row - }).ToArray(), - Items = items, - PageInfo = new PageInfo - { - StartCursor = (select.Offset + 1).ToString(), - EndCursor = (select.Fetch + select.Offset).ToString(), - HasNextPage = (select.Fetch + select.Offset) < result.Count, - HasPreviousPage = select.Offset > 0 - }, Sql = sql, - Table = objectSchema.Name, - TotalCount = totalCount + Table = objectSchema.Name }; } - else if (selections.Any(_ => _.Is(nameof(SelectResponse.TotalCount)) - || _.Left(Invariant($"{nameof(SelectResponse.PageInfo)}.")))) + else if (selections.Any(_ => _.Is(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.TotalCount)}")) + || _.Left(Invariant($"{nameof(SelectResponse.Data)}.{nameof(Connection.PageInfo)}.")))) { var countSql = objectSchema.CreateCountSQL(null, select.Where); var countCommand = objectSchema.DataSource.CreateSqlCommand(countSql); var totalCount = (int?)await mediator.MapAsync(countCommand.ToSqlScalarRequest(), context.CancellationToken) ?? 0; return new SelectResponse() { - DataSource = objectSchema.DataSource.Name, - PageInfo = new PageInfo + Data = new() { - StartCursor = (select.Offset + 1).ToString(), - EndCursor = (select.Fetch + select.Offset).ToString(), - HasNextPage = (select.Fetch + select.Offset) < totalCount, - HasPreviousPage = select.Offset > 0 + PageInfo = new(select.Offset, select.Fetch, totalCount), + TotalCount = totalCount }, + DataSource = objectSchema.DataSource.Name, Sql = countSql, - Table = objectSchema.Name, - TotalCount = totalCount + Table = objectSchema.Name }; } else diff --git a/src/TypeCache.GraphQL/SqlApi/SelectResponse.cs b/src/TypeCache.GraphQL/SqlApi/SelectResponse.cs index 70693ca4..bd14929e 100644 --- a/src/TypeCache.GraphQL/SqlApi/SelectResponse.cs +++ b/src/TypeCache.GraphQL/SqlApi/SelectResponse.cs @@ -1,19 +1,16 @@ // Copyright (c) 2021 Samuel Abraham -using System.Collections.Generic; -using System.Linq; using GraphQL.Resolvers; using GraphQL.Types; -using GraphQL.Types.Relay.DataObjects; -using TypeCache.Extensions; using TypeCache.GraphQL.Attributes; -using TypeCache.GraphQL.Types; +using TypeCache.GraphQL.Data; using static System.FormattableString; namespace TypeCache.GraphQL.SqlApi; [GraphQLDescription("Response for a SELECT SQL action.")] public class SelectResponse + where T : notnull { public SelectResponse() { @@ -22,36 +19,21 @@ public SelectResponse() public SelectResponse(T[] items, int totalCount, uint offset) { var start = offset + 1; - var end = start + items.Length; - this.Edges = items.Select((item, i) => new Edge + this.Data = new(offset, items) { - Cursor = (start + i).ToString(), - Node = item - }).ToArray(); - this.PageInfo = new() - { - StartCursor = start.ToString(), - EndCursor = end.ToString(), - HasNextPage = end < totalCount, - HasPreviousPage = offset > 0 + PageInfo = new(offset, (uint)items.Length, totalCount), + TotalCount = totalCount }; - this.TotalCount = totalCount; } - public string? DataSource { get; set; } - - public Edge[]? Edges { get; set; } + public Connection? Data { get; set; } - public IList? Items { get; set; } - - public PageInfo? PageInfo { get; set; } + public string? DataSource { get; set; } public string? Sql { get; set; } public string? Table { get; set; } - public long? TotalCount { get; set; } - public static ObjectGraphType CreateGraphType(string name, string description, IGraphType dataGraphType) { var graphType = new ObjectGraphType @@ -59,32 +41,18 @@ public static ObjectGraphType CreateGraphType(string name, string description, I Name = Invariant($"{name}{nameof(SelectResponse)}"), Description = description }; - var edgeType = CreateEdgeGraphType(name, description, dataGraphType); graphType.AddField(new() { - Name = nameof(SelectResponse.DataSource), - Type = typeof(StringGraphType), - Resolver = new FuncFieldResolver, string>(context => context.Source.DataSource) - }); - graphType.AddField(new() - { - Name = nameof(SelectResponse.Edges), - ResolvedType = new ListGraphType(new NonNullGraphType(edgeType)), - Resolver = new FuncFieldResolver, Edge[]?>(context => context.Source.Edges) + Name = nameof(SelectResponse.Data), + ResolvedType = Connection.CreateGraphType(name, dataGraphType), + Resolver = new FuncFieldResolver, Connection>(context => context.Source.Data) }); graphType.AddField(new() { - Name = nameof(SelectResponse.Items), - ResolvedType = new ListGraphType(new NonNullGraphType(dataGraphType)), - Resolver = new FuncFieldResolver, IList?>(context => context.Source.Items!) - }); - graphType.AddField(new() - { - Name = nameof(SelectResponse.PageInfo), - //ResolvedType = new GraphQLObjectType(), - Type = typeof(GraphQLObjectType), - Resolver = new FuncFieldResolver, PageInfo>(context => context.Source.PageInfo) + Name = nameof(SelectResponse.DataSource), + Type = typeof(StringGraphType), + Resolver = new FuncFieldResolver, string>(context => context.Source.DataSource) }); graphType.AddField(new() { @@ -98,35 +66,7 @@ public static ObjectGraphType CreateGraphType(string name, string description, I Type = typeof(StringGraphType), Resolver = new FuncFieldResolver, string>(context => context.Source.Table) }); - graphType.AddField(new() - { - Name = nameof(SelectResponse.TotalCount), - Type = typeof(IntGraphType), - Resolver = new FuncFieldResolver, long?>(context => context.Source.TotalCount) - }); return graphType; } - - public static ObjectGraphType CreateEdgeGraphType(string name, string description, IGraphType dataGraphType) - { - var graphType = new ObjectGraphType - { - Name = Invariant($"{name}Edge"), - Description = description - }; - graphType.AddField(new() - { - Name = nameof(Edge.Cursor), - Type = typeof(StringGraphType), - Resolver = new FuncFieldResolver, string>(context => context.Source.Cursor) - }); - graphType.AddField(new() - { - Name = nameof(Edge.Node), - ResolvedType = new NonNullGraphType(dataGraphType), - Resolver = new FuncFieldResolver, T>(context => context.Source.Node) - }); - return graphType; - } } diff --git a/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj b/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj index 8a8c9a0b..3edbb68b 100644 --- a/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj +++ b/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj @@ -4,7 +4,7 @@ enable TypeCache.GraphQL TypeCache.GraphQL - 7.3.12 + 7.3.13 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache GraphQL diff --git a/src/TypeCache.GraphQL/Types/GraphQLObjectType.cs b/src/TypeCache.GraphQL/Types/GraphQLObjectType.cs index d069cf73..f9cec24f 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLObjectType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLObjectType.cs @@ -18,14 +18,19 @@ namespace TypeCache.GraphQL.Types; /// -/// +/// /// public sealed class GraphQLObjectType : ObjectGraphType where T : notnull { public GraphQLObjectType() + : this(typeof(T).GraphQLName()) { - this.Name = typeof(T).GraphQLName(); + } + + public GraphQLObjectType(string name) + { + this.Name = name; this.Description = typeof(T).GraphQLDescription() ?? Invariant($"{typeof(T).Assembly.GetName().Name}: {typeof(T).Namespace}.{typeof(T).Name()}"); this.DeprecationReason = typeof(T).GraphQLDeprecationReason(); diff --git a/src/TypeCache.Web/Handlers/SqlApiHandler.cs b/src/TypeCache.Web/Handlers/SqlApiHandler.cs index 9b6d6ae9..1444ec64 100644 --- a/src/TypeCache.Web/Handlers/SqlApiHandler.cs +++ b/src/TypeCache.Web/Handlers/SqlApiHandler.cs @@ -38,7 +38,8 @@ HttpContext httpContext var sqlCommand = objectSchema.DataSource.CreateSqlCommand(procedure); sqlCommand.Type = CommandType.StoredProcedure; - foreach (var parameter in objectSchema.Parameters.Where(parameter => parameter.Direction.IsAny(ParameterDirection.Input, ParameterDirection.InputOutput))) + foreach (var parameter in objectSchema.Parameters.Where(parameter => + parameter.Direction is ParameterDirection.Input || parameter.Direction is ParameterDirection.InputOutput)) { if (httpContext.Request.Query.TryGetValue(parameter.Name, out var values)) sqlCommand.Parameters.Add(parameter.Name, values.First()); @@ -48,7 +49,8 @@ HttpContext httpContext var request = new SqlDataSetRequest { Command = sqlCommand }; var result = await mediator.MapAsync(request, httpContext.RequestAborted); - foreach (var parameter in objectSchema.Parameters.Where(parameter => parameter.Direction.IsAny(ParameterDirection.Output, ParameterDirection.InputOutput))) + foreach (var parameter in objectSchema.Parameters.Where(parameter => + parameter.Direction is ParameterDirection.InputOutput || parameter.Direction is ParameterDirection.Output)) { if (sqlCommand.Parameters.TryGetValue(parameter.Name, out var value)) httpContext.Items[parameter.Name] = value; diff --git a/src/TypeCache.Web/TypeCache.Web.csproj b/src/TypeCache.Web/TypeCache.Web.csproj index 4d13dc28..9718852d 100644 --- a/src/TypeCache.Web/TypeCache.Web.csproj +++ b/src/TypeCache.Web/TypeCache.Web.csproj @@ -5,7 +5,7 @@ enable TypeCache.Web TypeCache.Web - 7.3.11 + 7.3.13 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache Web Library diff --git a/src/TypeCache/Attributes/NameAttribute.cs b/src/TypeCache/Attributes/NameAttribute.cs deleted file mode 100644 index 20eabc2e..00000000 --- a/src/TypeCache/Attributes/NameAttribute.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -using System.Reflection; -using TypeCache.Extensions; - -namespace TypeCache.Attributes; - -/// -/// Rename any objects used within the TypeCache system.
-/// To access the new name use .Name() instead of . -///
-[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] -public sealed class NameAttribute : Attribute -{ - public NameAttribute(string name) - { - name.AssertNotBlank(); - this.Name = name; - } - - /// - /// The new name of the . - /// To access the new name use .Name() instead of . - /// - public string Name { get; } -} diff --git a/src/TypeCache/Data/Extensions/DbDataReaderExtensions.cs b/src/TypeCache/Data/Extensions/DbDataReaderExtensions.cs index 63528845..0a5c34e7 100644 --- a/src/TypeCache/Data/Extensions/DbDataReaderExtensions.cs +++ b/src/TypeCache/Data/Extensions/DbDataReaderExtensions.cs @@ -30,7 +30,7 @@ public static DataTable ReadDataTable(this DbDataReader @this) public static async Task ReadModelsAsync(this DbDataReader @this, IList rows, CancellationToken token = default) where T : notnull, new() { - var propertyMap = typeof(T).GetPublicProperties().ToDictionary(property => property.Name(), property => property); + var propertyMap = typeof(T).GetPublicProperties().ToDictionary(property => property.Name, property => property, StringComparer.OrdinalIgnoreCase); var properties = @this.GetColumns().Select(column => propertyMap[column]).ToArray(); var values = new object[@this.VisibleFieldCount]; @@ -45,7 +45,7 @@ public static async Task ReadModelsAsync(this DbDataReader @this, IList ro public static async Task ReadModelsAsync(this DbDataReader @this, Type modelType, IList rows, CancellationToken token = default) { - var propertyMap = modelType.GetProperties(Instance | Public).ToDictionary(property => property.Name(), property => property); + var propertyMap = modelType.GetProperties(Instance | Public).ToDictionary(property => property.Name, property => property, StringComparer.OrdinalIgnoreCase); var properties = @this.GetColumns().Select(column => propertyMap[column]).ToArray(); var values = new object[@this.VisibleFieldCount]; diff --git a/src/TypeCache/Data/Extensions/EnumExtensions.cs b/src/TypeCache/Data/Extensions/EnumExtensions.cs index bcc590a7..4d771b16 100644 --- a/src/TypeCache/Data/Extensions/EnumExtensions.cs +++ b/src/TypeCache/Data/Extensions/EnumExtensions.cs @@ -7,52 +7,38 @@ namespace TypeCache.Data.Extensions; public static class EnumExtensions { - private static readonly SystemType[] SQLSystemTypes = + [DebuggerHidden] + public static bool IsSQLType(this SystemType @this) => @this switch { - SystemType.Boolean, - SystemType.Byte, - SystemType.SByte, - SystemType.Int16, - SystemType.UInt16, - SystemType.Int32, - SystemType.UInt32, - SystemType.Int64, - SystemType.UInt64, - SystemType.Single, - SystemType.Double, - SystemType.Decimal, - SystemType.DateOnly, - SystemType.DateTime, - SystemType.DateTimeOffset, - SystemType.TimeOnly, - SystemType.TimeSpan, - SystemType.Guid, - SystemType.Char, - SystemType.String + SystemType.Boolean + or SystemType.SByte or SystemType.Byte + or SystemType.Int16 or SystemType.Int32 or SystemType.Int64 + or SystemType.UInt16 or SystemType.UInt32 or SystemType.UInt64 + or SystemType.Single or SystemType.Double or SystemType.Decimal + or SystemType.DateOnly or SystemType.DateTime or SystemType.DateTimeOffset + or SystemType.TimeOnly or SystemType.TimeSpan + or SystemType.Guid or SystemType.Char or SystemType.String => true, + _ => false }; - [MethodImpl(AggressiveInlining), DebuggerHidden] - public static bool IsSQLType(this SystemType @this) - => SQLSystemTypes.ContainsEnum(@this); - - public static Type ToType(this SqlDbType @this) - => @this switch - { - SqlDbType.Bit => typeof(bool), - SqlDbType.TinyInt => typeof(sbyte), - SqlDbType.SmallInt => typeof(short), - SqlDbType.Int => typeof(int), - SqlDbType.BigInt => typeof(long), - SqlDbType.Binary or SqlDbType.Image or SqlDbType.Timestamp or SqlDbType.VarBinary => typeof(byte[]), - SqlDbType.Char or SqlDbType.Text or SqlDbType.VarChar or SqlDbType.NChar or SqlDbType.NText or SqlDbType.NVarChar => typeof(string), - SqlDbType.Date => typeof(DateOnly), - SqlDbType.DateTime or SqlDbType.DateTime2 or SqlDbType.SmallDateTime => typeof(DateTime), - SqlDbType.DateTimeOffset => typeof(DateTimeOffset), - SqlDbType.Time => typeof(TimeOnly), - SqlDbType.Real => typeof(float), - SqlDbType.Float => typeof(double), - SqlDbType.Decimal or SqlDbType.Money or SqlDbType.SmallMoney => typeof(decimal), - SqlDbType.UniqueIdentifier => typeof(Guid), - _ => typeof(object) - }; + [DebuggerHidden] + public static Type ToType(this SqlDbType @this) => @this switch + { + SqlDbType.Bit => typeof(bool), + SqlDbType.TinyInt => typeof(sbyte), + SqlDbType.SmallInt => typeof(short), + SqlDbType.Int => typeof(int), + SqlDbType.BigInt => typeof(long), + SqlDbType.Binary or SqlDbType.Image or SqlDbType.Timestamp or SqlDbType.VarBinary => typeof(byte[]), + SqlDbType.Char or SqlDbType.Text or SqlDbType.VarChar or SqlDbType.NChar or SqlDbType.NText or SqlDbType.NVarChar => typeof(string), + SqlDbType.Date => typeof(DateOnly), + SqlDbType.DateTime or SqlDbType.DateTime2 or SqlDbType.SmallDateTime => typeof(DateTime), + SqlDbType.DateTimeOffset => typeof(DateTimeOffset), + SqlDbType.Time => typeof(TimeOnly), + SqlDbType.Real => typeof(float), + SqlDbType.Float => typeof(double), + SqlDbType.Decimal or SqlDbType.Money or SqlDbType.SmallMoney => typeof(decimal), + SqlDbType.UniqueIdentifier => typeof(Guid), + _ => typeof(object) + }; } diff --git a/src/TypeCache/Extensions/ArrayExtensions.cs b/src/TypeCache/Extensions/ArrayExtensions.cs index b5734268..34faa678 100644 --- a/src/TypeCache/Extensions/ArrayExtensions.cs +++ b/src/TypeCache/Extensions/ArrayExtensions.cs @@ -101,9 +101,12 @@ public static byte[] FromBase64(this char[] @this) public static byte[] FromBase64(this char[] @this, int offset, int length) => Convert.FromBase64CharArray(@this, offset, length); + /// /// public static IEnumerable Get(this T[] @this, Range range) { + @this.AssertNotNull(); + range = range.Normalize(@this.Length); if (!range.Any()) return Array.Empty; @@ -126,6 +129,8 @@ public static IEnumerable Get(this T[] @this, Range range) /// public static T[] GetCopy(this T[] @this) { + @this.AssertNotNull(); + if (!@this.Any()) return Array.Empty; @@ -209,9 +214,12 @@ public static void Sort(this T[] @this, IComparer? comparer = null) public static void Sort(this T[] @this, int start, int length = 0, IComparer? comparer = null) => Array.Sort(@this, start, length > 0 ? length : @this.Length, comparer); + /// /// public static T[] Subarray(this T[] @this, int sourceIndex, int length = 0) { + @this.AssertNotNull(); + if (sourceIndex + length > @this.Length) throw new IndexOutOfRangeException($"{nameof(Subarray)}: last index {sourceIndex + length} is more than array length {@this.Length}."); @@ -255,18 +263,18 @@ public static string ToHex(this byte[] @this) /// => .Create(@ ?? ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static ImmutableQueue ToImmutableQueue(this T[]? @this) + public static ImmutableQueue ToImmutableQueue(this T[] @this) where T : notnull - => ImmutableQueue.Create(@this ?? Array.Empty); + => ImmutableQueue.Create(@this); /// /// /// => .Create(@ ?? ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static ImmutableStack ToImmutableStack(this T[]? @this) + public static ImmutableStack ToImmutableStack(this T[] @this) where T : notnull - => ImmutableStack.Create(@this ?? Array.Empty); + => ImmutableStack.Create(@this); /// /// => .SerializeToNode(@, ) ; diff --git a/src/TypeCache/Extensions/BooleanExtensions.cs b/src/TypeCache/Extensions/BooleanExtensions.cs index 7c24ba7f..08b2dcdc 100644 --- a/src/TypeCache/Extensions/BooleanExtensions.cs +++ b/src/TypeCache/Extensions/BooleanExtensions.cs @@ -1,10 +1,17 @@ -namespace TypeCache.Extensions; +// Copyright (c) 2021 Samuel Abraham + +namespace TypeCache.Extensions; public static class BooleanExtensions { - [MethodImpl(AggressiveInlining), DebuggerHidden] + [DebuggerHidden] public static bool Else(this bool @this, Action doIfFalse) - => (!@this).Then(doIfFalse); + { + if (!@this) + doIfFalse(); + + return @this; + } [DebuggerHidden] public static bool Then(this bool @this, Action doIfTrue) diff --git a/src/TypeCache/Extensions/CsvExtensions.cs b/src/TypeCache/Extensions/CsvExtensions.cs index d7a4708a..edb34815 100644 --- a/src/TypeCache/Extensions/CsvExtensions.cs +++ b/src/TypeCache/Extensions/CsvExtensions.cs @@ -1,5 +1,6 @@ // Copyright (c) 2021 Samuel Abraham +using System.Numerics; using TypeCache.Collections; using TypeCache.Extensions; using static System.Globalization.CultureInfo; @@ -24,8 +25,8 @@ private static string EscapeCSV(this object? @this, CsvOptions options = default ',' or '"' => Invariant($"\"{@this}\""), char character => character.ToString(), sbyte or byte => ((IFormattable)@this).ToString(options.ByteFormatSpecifier, InvariantCulture), - short or int or nint or long or Int128 or ushort or uint or nuint or ulong or UInt128 => ((IFormattable)@this).ToString(options.IntegerFormatSpecifier, InvariantCulture), - float or double or Half or decimal => ((IFormattable)@this).ToString(options.DecimalFormatSpecifier, InvariantCulture), + IFormattable formattable when @this.GetType().Implements(typeof(IBinaryInteger<>)) => formattable.ToString(options.IntegerFormatSpecifier, InvariantCulture), + IFormattable formattable when @this.GetType().Implements(typeof(IFloatingPoint<>)) => formattable.ToString(options.DecimalFormatSpecifier, InvariantCulture), DateOnly => ((IFormattable)@this).ToString(options.DateOnlyFormatSpecifier, InvariantCulture), DateTime => ((IFormattable)@this).ToString(options.DateTimeFormatSpecifier, InvariantCulture), DateTimeOffset => ((IFormattable)@this).ToString(options.DateTimeOffsetFormatSpecifier, InvariantCulture), diff --git a/src/TypeCache/Extensions/DictionaryExtensions.cs b/src/TypeCache/Extensions/DictionaryExtensions.cs new file mode 100644 index 00000000..cf27c6ca --- /dev/null +++ b/src/TypeCache/Extensions/DictionaryExtensions.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2021 Samuel Abraham + +using System.Collections.ObjectModel; + +namespace TypeCache.Extensions; + +public static class DictionaryExtensions +{ + /// + public static bool If(this IDictionary @this, K key, Action action) + where K : notnull + { + @this.AssertNotNull(); + + var success = @this.ContainsKey(key); + if (success) + action(); + + return success; + } + + /// + public static bool If(this IDictionary @this, K key, Action action) + where K : notnull + { + @this.AssertNotNull(); + + var success = @this.TryGetValue(key, out var value); + if (success) + action(value!); + + return success; + } + + /// + public static bool If(this IDictionary @this, K key, Action action) + where K : notnull + { + @this.AssertNotNull(); + + var success = @this.TryGetValue(key, out var value); + if (success) + action(key, value!); + + return success; + } + + /// + /// => ReadOnlyDictionary<, >(@); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static IReadOnlyDictionary ToReadOnly(this IDictionary @this) + where K : notnull + => new ReadOnlyDictionary(@this); +} diff --git a/src/TypeCache/Extensions/EnumExtensions.cs b/src/TypeCache/Extensions/EnumExtensions.cs index 68f91a00..518add29 100644 --- a/src/TypeCache/Extensions/EnumExtensions.cs +++ b/src/TypeCache/Extensions/EnumExtensions.cs @@ -23,11 +23,6 @@ public static string Hex(this T @this) where T : struct, Enum => EnumOf.Tokens.TryGetValue(@this, out var token) ? token.Hex : @this.ToString("X"); - [DebuggerHidden] - public static bool IsAny(this T @this, params T[] tokens) - where T : struct, Enum - => tokens.Any(token => EnumOf.Comparer.EqualTo(@this, token)); - [DebuggerHidden] public static bool IsDefined(this T @this) where T : struct, Enum @@ -53,131 +48,96 @@ public static StringComparer ToStringComparer(this StringComparison @this) public static bool IsCollection(this SystemType @this) => @this switch { - SystemType.Array => true, - SystemType.ArrayList => true, - SystemType.BitArray => true, - SystemType.BlockingCollection => true, - SystemType.Collection => true, - SystemType.ConcurrentBag => true, - SystemType.ConcurrentDictionary => true, - SystemType.ConcurrentQueue => true, - SystemType.ConcurrentStack => true, - SystemType.Dictionary => true, - SystemType.HashSet => true, - SystemType.Hashtable => true, - SystemType.HybridDictionary => true, - SystemType.ImmutableArray => true, - SystemType.ImmutableDictionary => true, - SystemType.ImmutableHashSet => true, - SystemType.ImmutableList => true, - SystemType.ImmutableQueue => true, - SystemType.ImmutableSortedDictionary => true, - SystemType.ImmutableSortedSet => true, - SystemType.ImmutableStack => true, - SystemType.KeyedCollection => true, - SystemType.LinkedList => true, - SystemType.List => true, - SystemType.ListDictionary => true, - SystemType.NameObjectCollectionBase => true, - SystemType.NameValueCollection => true, - SystemType.ObservableCollection => true, - SystemType.OrderedDictionary => true, - SystemType.PriorityQueue => true, - SystemType.Queue => true, - SystemType.ReadOnlyCollection => true, - SystemType.ReadOnlyDictionary => true, - SystemType.ReadOnlyObservableCollection => true, - SystemType.SortedDictionary => true, - SystemType.SortedList => true, - SystemType.SortedSet => true, - SystemType.Stack => true, - SystemType.StringCollection => true, - SystemType.StringDictionary => true, + SystemType.Array + or SystemType.BitArray + or SystemType.BlockingCollection + or SystemType.Collection + or SystemType.ConcurrentBag or SystemType.ConcurrentDictionary or SystemType.ConcurrentQueue or SystemType.ConcurrentStack + or SystemType.Dictionary + or SystemType.HashSet + or SystemType.Hashtable + or SystemType.HybridDictionary + or SystemType.ImmutableArray or SystemType.ImmutableDictionary or SystemType.ImmutableHashSet or SystemType.ImmutableList + or SystemType.ImmutableQueue or SystemType.ImmutableSortedDictionary or SystemType.ImmutableSortedSet or SystemType.ImmutableStack + or SystemType.KeyedCollection + or SystemType.List or SystemType.LinkedList or SystemType.ArrayList + or SystemType.ListDictionary + or SystemType.NameObjectCollectionBase or SystemType.NameValueCollection + or SystemType.ObservableCollection + or SystemType.OrderedDictionary + or SystemType.PriorityQueue + or SystemType.Queue + or SystemType.ReadOnlyCollection or SystemType.ReadOnlyDictionary or SystemType.ReadOnlyObservableCollection + or SystemType.SortedDictionary or SystemType.SortedList or SystemType.SortedSet + or SystemType.Stack + or SystemType.StringCollection + or SystemType.StringDictionary => true, _ => false }; public static bool IsConcurrent(this SystemType @this) => @this switch { - SystemType.ConcurrentBag => true, - SystemType.ConcurrentDictionary => true, - SystemType.ConcurrentQueue => true, - SystemType.ConcurrentStack => true, + SystemType.ConcurrentBag or SystemType.ConcurrentDictionary + or SystemType.ConcurrentQueue or SystemType.ConcurrentStack => true, _ => false }; public static bool IsDictionary(this SystemType @this) => @this switch { - SystemType.ConcurrentDictionary => true, - SystemType.Dictionary => true, - SystemType.Hashtable => true, - SystemType.HybridDictionary => true, - SystemType.ImmutableDictionary => true, - SystemType.ImmutableSortedDictionary => true, - SystemType.KeyedCollection => true, - SystemType.ListDictionary => true, - SystemType.NameObjectCollectionBase => true, - SystemType.NameValueCollection => true, - SystemType.OrderedDictionary => true, - SystemType.ReadOnlyDictionary => true, - SystemType.SortedDictionary => true, - SystemType.SortedList => true, - SystemType.StringDictionary => true, + SystemType.Dictionary or SystemType.ConcurrentDictionary or SystemType.SortedDictionary + or SystemType.ImmutableDictionary or SystemType.ImmutableSortedDictionary + or SystemType.Hashtable or SystemType.HybridDictionary or SystemType.OrderedDictionary or SystemType.ReadOnlyDictionary + or SystemType.KeyedCollection or SystemType.ListDictionary or SystemType.StringDictionary + or SystemType.NameObjectCollectionBase or SystemType.NameValueCollection or SystemType.SortedList => true, + _ => false + }; + + public static bool IsEnumUnderlyingType(this SystemType @this) => @this switch + { + SystemType.SByte or SystemType.Byte + or SystemType.Int16 or SystemType.Int32 or SystemType.Int64 + or SystemType.UInt16 or SystemType.UInt32 or SystemType.UInt64 => true, _ => false }; public static bool IsImmutable(this SystemType @this) => @this switch { - SystemType.ImmutableArray => true, - SystemType.ImmutableDictionary => true, - SystemType.ImmutableHashSet => true, - SystemType.ImmutableList => true, - SystemType.ImmutableQueue => true, - SystemType.ImmutableSortedDictionary => true, - SystemType.ImmutableSortedSet => true, - SystemType.ImmutableStack => true, + SystemType.ImmutableArray + or SystemType.ImmutableDictionary or SystemType.ImmutableSortedDictionary + or SystemType.ImmutableHashSet or SystemType.ImmutableSortedSet + or SystemType.ImmutableList + or SystemType.ImmutableQueue + or SystemType.ImmutableStack => true, _ => false }; public static bool IsPrimitive(this SystemType @this) => @this switch { - SystemType.Boolean => true, - SystemType.SByte => true, - SystemType.Int16 => true, - SystemType.Int32 => true, - SystemType.Int64 => true, - SystemType.Int128 => true, - SystemType.IntPtr => true, - SystemType.Byte => true, - SystemType.UInt16 => true, - SystemType.UInt32 => true, - SystemType.UInt64 => true, - SystemType.UInt128 => true, - SystemType.UIntPtr => true, - SystemType.Char => true, - SystemType.Single => true, - SystemType.Double => true, + SystemType.Boolean + or SystemType.SByte or SystemType.Byte + or SystemType.Int16 or SystemType.Int32 or SystemType.Int64 or SystemType.Int128 + or SystemType.IntPtr or SystemType.UIntPtr + or SystemType.UInt16 or SystemType.UInt32 or SystemType.UInt64 or SystemType.UInt128 + or SystemType.Single or SystemType.Double + or SystemType.Char => true, + _ => false + }; + + public static bool IsQueue(this SystemType @this) => @this switch + { + SystemType.ConcurrentQueue or SystemType.ImmutableQueue or SystemType.PriorityQueue or SystemType.Queue => true, _ => false }; public static bool IsReadOnly(this SystemType @this) => @this switch { - SystemType.ReadOnlyCollection => true, - SystemType.ReadOnlyDictionary => true, - SystemType.ReadOnlyObservableCollection => true, + SystemType.ReadOnlyCollection or SystemType.ReadOnlyDictionary or SystemType.ReadOnlyObservableCollection => true, _ => false }; - public static bool IsEnumUnderlyingType(this SystemType @this) - => @this switch - { - SystemType.SByte => true, - SystemType.Int16 => true, - SystemType.Int32 => true, - SystemType.Int64 => true, - SystemType.Byte => true, - SystemType.UInt16 => true, - SystemType.UInt32 => true, - SystemType.UInt64 => true, - _ => false - }; + public static bool IsStack(this SystemType @this) => @this switch + { + SystemType.ConcurrentStack or SystemType.ImmutableStack or SystemType.Stack => true, + _ => false + }; } diff --git a/src/TypeCache/Extensions/LinqExtensions.cs b/src/TypeCache/Extensions/EnumerableExtensions.cs similarity index 81% rename from src/TypeCache/Extensions/LinqExtensions.cs rename to src/TypeCache/Extensions/EnumerableExtensions.cs index d3a9f215..15339278 100644 --- a/src/TypeCache/Extensions/LinqExtensions.cs +++ b/src/TypeCache/Extensions/EnumerableExtensions.cs @@ -2,13 +2,12 @@ using System.Collections; using System.Collections.Immutable; -using System.Collections.ObjectModel; using TypeCache.Collections; using TypeCache.Utilities; namespace TypeCache.Extensions; -public static class LinqExtensions +public static class EnumerableExtensions { /// /// => @.OfType<>().Any(); @@ -46,6 +45,22 @@ public static bool ContainsEnum(this IEnumerable @this, T value) where T : struct, Enum => @this.Contains(value, EnumOf.Comparer); + /// + /// + /// => .Concat(@); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static string Concat(this IEnumerable @this) + => string.Concat(@this); + + /// + /// + /// => .Concat(@); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static string Concat(this IEnumerable @this) + => string.Concat(@this); + public static void Deconstruct(this IEnumerable @this, out T? first, out IEnumerable rest) { using var enumerator = @this.GetEnumerator(); @@ -70,7 +85,7 @@ public static void Deconstruct(this IEnumerable @this, out T? first, out T rest = enumerator.Rest(); } - /// + /// /// /// => @.OfType<>().First(); /// @@ -78,7 +93,7 @@ public static void Deconstruct(this IEnumerable @this, out T? first, out T public static T? First(this IEnumerable @this) => @this.OfType().First(); - /// + /// /// /// => @.OfType<>().FirstOrDefault(); /// @@ -86,46 +101,7 @@ public static void Deconstruct(this IEnumerable @this, out T? first, out T public static T? FirstOrDefault(this IEnumerable @this) => @this.OfType().FirstOrDefault(); - /// - public static bool If(this IDictionary @this, K key, Action action) - where K : notnull - { - @this.AssertNotNull(); - - var success = @this.ContainsKey(key); - if (success) - action(); - - return success; - } - - /// - public static bool If(this IDictionary @this, K key, Action action) - where K : notnull - { - @this.AssertNotNull(); - - var success = @this.TryGetValue(key, out var value); - if (success) - action(value!); - - return success; - } - - /// - public static bool If(this IDictionary @this, K key, Action action) - where K : notnull - { - @this.AssertNotNull(); - - var success = @this.TryGetValue(key, out var value); - if (success) - action(key, value!); - - return success; - } - - /// + /// /// /// => @.OfType<>().Single(); /// @@ -133,7 +109,7 @@ public static bool If(this IDictionary @this, K key, Action ac public static T? Single(this IEnumerable @this) => @this.OfType().Single(); - /// + /// /// /// => @.OfType<>().SingleOrDefault(); /// @@ -150,14 +126,6 @@ public static Dictionary ToDictionary(this IEnumerab where TKey : notnull => new Dictionary(@this, comparer); - /// - /// => ReadOnlyDictionary<, >(@); - /// - [MethodImpl(AggressiveInlining), DebuggerHidden] - public static IReadOnlyDictionary ToReadOnly(this IDictionary @this) - where K : notnull - => new ReadOnlyDictionary(@this); - /// /// => @.Select(_ => _.AsTask()); /// diff --git a/src/TypeCache/Extensions/ArrayExpressionBuilder.cs b/src/TypeCache/Extensions/ExpressionExtensions.ArrayExpressionBuilder.cs similarity index 90% rename from src/TypeCache/Extensions/ArrayExpressionBuilder.cs rename to src/TypeCache/Extensions/ExpressionExtensions.ArrayExpressionBuilder.cs index 72556203..5474acbb 100644 --- a/src/TypeCache/Extensions/ArrayExpressionBuilder.cs +++ b/src/TypeCache/Extensions/ExpressionExtensions.ArrayExpressionBuilder.cs @@ -1,7 +1,6 @@ // Copyright (c) 2021 Samuel Abraham using System.Linq.Expressions; -using TypeCache.Extensions; namespace TypeCache.Extensions; @@ -18,34 +17,39 @@ public ArrayExpressionBuilder(Expression expression) /// /// => .ArrayAccess(._Expression, .Constant()); /// + [DebuggerHidden] public IndexExpression this[int index] => Expression.ArrayAccess(this._Expression, Expression.Constant(index)); /// /// - /// => .ArrayAccess(._Expression, .Select(index => ().Constant(index))); + /// => .ArrayAccess(._Expression, .Select(index => .Constant(index))); /// + [DebuggerHidden] public IndexExpression this[params int[] indexes] - => Expression.ArrayAccess(this._Expression, indexes.Select(index => (Expression)Expression.Constant(index))); + => Expression.ArrayAccess(this._Expression, indexes.Select(index => Expression.Constant(index))); /// /// /// => .ArrayAccess(._Expression, .Constant()); /// + [DebuggerHidden] public IndexExpression this[long index] => Expression.ArrayAccess(this._Expression, Expression.Constant(index)); /// /// - /// => .ArrayAccess(._Expression, .Select(index => ().Constant(index))); + /// => .ArrayAccess(._Expression, .Select(index => .Constant(index))); /// + [DebuggerHidden] public IndexExpression this[params long[] indexes] - => Expression.ArrayAccess(this._Expression, indexes.Select(index => (Expression)Expression.Constant(index))); + => Expression.ArrayAccess(this._Expression, indexes.Select(index => Expression.Constant(index))); /// /// /// => .ArrayAccess(._Expression, ); /// + [DebuggerHidden] public IndexExpression this[Expression index] => Expression.ArrayAccess(this._Expression, index); @@ -53,6 +57,7 @@ public IndexExpression this[Expression index] /// /// => .ArrayAccess(._Expression, ); /// + [DebuggerHidden] public IndexExpression this[IEnumerable indexes] => Expression.ArrayAccess(this._Expression, indexes); @@ -60,6 +65,7 @@ public IndexExpression this[IEnumerable indexes] /// /// => .ArrayAccess(._Expression, ); /// + [DebuggerHidden] public IndexExpression this[params Expression[] indexes] => Expression.ArrayAccess(this._Expression, indexes); @@ -67,5 +73,6 @@ public IndexExpression this[params Expression[] indexes] /// /// => .ArrayLength(._Expression); /// + [DebuggerHidden] public UnaryExpression Length => Expression.ArrayLength(this._Expression); } diff --git a/src/TypeCache/Extensions/BinaryOperator.cs b/src/TypeCache/Extensions/ExpressionExtensions.BinaryOperator.cs similarity index 100% rename from src/TypeCache/Extensions/BinaryOperator.cs rename to src/TypeCache/Extensions/ExpressionExtensions.BinaryOperator.cs diff --git a/src/TypeCache/Extensions/LabelTargetExtensions.cs b/src/TypeCache/Extensions/ExpressionExtensions.LabelTarget.cs similarity index 100% rename from src/TypeCache/Extensions/LabelTargetExtensions.cs rename to src/TypeCache/Extensions/ExpressionExtensions.LabelTarget.cs diff --git a/src/TypeCache/Extensions/UnaryOperator.cs b/src/TypeCache/Extensions/ExpressionExtensions.UnaryOperator.cs similarity index 100% rename from src/TypeCache/Extensions/UnaryOperator.cs rename to src/TypeCache/Extensions/ExpressionExtensions.UnaryOperator.cs diff --git a/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs index 99e08ed3..d110eb3e 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.ConstructorInfo.cs @@ -22,7 +22,7 @@ public static LambdaExpression ToLambdaExpression(this ConstructorInfo @this) { var parameters = @this.GetParameters() .OrderBy(parameterInfo => parameterInfo.Position) - .Select(parameterInfo => parameterInfo!.ToParameterExpression()) + .Select(parameterInfo => parameterInfo!.ToExpression()) .ToArray(); return @this.ToNewExpression(parameters).Lambda(parameters); diff --git a/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs index 59616e54..43827287 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.MemberInfo.cs @@ -22,12 +22,11 @@ public static bool HasCustomAttribute(this MemberInfo @this, bool inherit = t public static bool HasCustomAttribute(this MemberInfo @this, Type attributeType, bool inherit = true) => @this.GetCustomAttribute(attributeType, inherit) is not null; - /// - /// => @.GetCustomAttribute<>()?.Name
- /// ?? @.Name.Left(@.Name.IndexOf('`'));
- ///
[DebuggerHidden] public static string Name(this MemberInfo @this) - => @this.GetCustomAttribute()?.Name - ?? @this.Name.Left(@this.Name.IndexOf(GENERIC_TICKMARK)); + => @this.Name.IndexOf(GENERIC_TICKMARK) switch + { + var index when index > -1 => @this.Name.Left(index), + _ => @this.Name + }; } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs index 84cf84d4..de1467b4 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.MethodInfo.cs @@ -105,7 +105,7 @@ public static LambdaExpression ToLambdaExpression(this MethodInfo @this) ParameterExpression instance = nameof(instance).ToParameterExpression(@this.DeclaringType!); var parameters = @this.GetParameters() .OrderBy(parameterInfo => parameterInfo.Position) - .Select(parameterInfo => parameterInfo!.ToParameterExpression()) + .Select(parameterInfo => parameterInfo!.ToExpression()) .ToArray(); return !@this.IsStatic diff --git a/src/TypeCache/Extensions/ObjectType.cs b/src/TypeCache/Extensions/ReflectionExtensions.ObjectType.cs similarity index 100% rename from src/TypeCache/Extensions/ObjectType.cs rename to src/TypeCache/Extensions/ReflectionExtensions.ObjectType.cs diff --git a/src/TypeCache/Extensions/ReflectionExtensions.ParameterInfo.cs b/src/TypeCache/Extensions/ReflectionExtensions.ParameterInfo.cs index e5d08134..9d51f7af 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.ParameterInfo.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.ParameterInfo.cs @@ -24,21 +24,17 @@ public static bool HasCustomAttribute(this ParameterInfo @this, Type attributeTy => @this.GetCustomAttribute(attributeType, inherit) is not null; /// - /// => @.GetCustomAttribute<>()?.Name
- /// ?? @.Name.Left(@.Name.IndexOf('`'))
- /// ?? Invariant($"Parameter{@.Position}");
+ /// => @.Name.Left(@.Name.IndexOf('`')) ?? ; ///
- [DebuggerHidden] + [MethodImpl(AggressiveInlining), DebuggerHidden] public static string Name(this ParameterInfo @this) - => @this.GetCustomAttribute()?.Name - ?? @this.Name?.Left(@this.Name.IndexOf(GENERIC_TICKMARK)) - ?? Invariant($"Parameter{@this.Position}"); + => @this.Name?.Left(@this.Name.IndexOf(GENERIC_TICKMARK)) ?? string.Empty; /// /// /// => .Parameter(@); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static ParameterExpression ToParameterExpression(this ParameterInfo @this) + public static ParameterExpression ToExpression(this ParameterInfo @this) => Expression.Parameter(@this.ParameterType, @this.Name); } diff --git a/src/TypeCache/Extensions/ReflectionExtensions.RuntimeTypeHandle.cs b/src/TypeCache/Extensions/ReflectionExtensions.RuntimeTypeHandle.cs index c500fce7..2f292a02 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.RuntimeTypeHandle.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.RuntimeTypeHandle.cs @@ -14,34 +14,26 @@ public static bool Is(this RuntimeTypeHandle @this) /// /// => @.Equals(.TypeHandle) - /// || (.IsGenericTypeDefinition && @.ToGenericType() == ); + /// || (.IsGenericTypeDefinition && @.ToType().ToGenericType() == ); /// - [MethodImpl(AggressiveInlining), DebuggerHidden] + [DebuggerHidden] public static bool Is(this RuntimeTypeHandle @this, Type type) - => @this.Equals(type.TypeHandle) || type.IsGenericTypeDefinition && @this.ToGenericType() == type; + => @this.Equals(type.TypeHandle) || type.IsGenericTypeDefinition && @this.ToType().ToGenericTypeDefinition() == type; /// /// /// => @.ToType().MakeArrayType(); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static Type? ToArrayTypeOf(this RuntimeTypeHandle @this, int rank = 1) + public static Type ToArrayTypeOf(this RuntimeTypeHandle @this, int rank = 1) => @this.ToType().MakeArrayType(rank); - public static Type? ToGenericType(this RuntimeTypeHandle @this) - => @this.ToType() switch - { - null => null, - var type when type.IsGenericType => type.GetGenericTypeDefinition(), - _ => null - }; - /// /// /// => @.ToType().MakeGenericType(); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static Type? ToGenericTypeOf(this RuntimeTypeHandle @this, params Type[] typeArguments) + public static Type ToGenericTypeOf(this RuntimeTypeHandle @this, params Type[] typeArguments) => @this.ToType().MakeGenericType(typeArguments); /// @@ -50,6 +42,7 @@ public static bool Is(this RuntimeTypeHandle @this, Type type) ///
/// [MethodImpl(AggressiveInlining), DebuggerHidden] + [return: NotNull] public static Type ToType(this RuntimeTypeHandle @this) => Type.GetTypeFromHandle(@this) ?? throw new UnreachableException("Type.GetTypeFromHandle(...) returned null."); } diff --git a/src/TypeCache/Extensions/SystemType.cs b/src/TypeCache/Extensions/ReflectionExtensions.SystemType.cs similarity index 100% rename from src/TypeCache/Extensions/SystemType.cs rename to src/TypeCache/Extensions/ReflectionExtensions.SystemType.cs diff --git a/src/TypeCache/Extensions/ReflectionExtensions.Type.cs b/src/TypeCache/Extensions/ReflectionExtensions.Type.cs index 6110cb2e..6da8316e 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.Type.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.Type.cs @@ -1,5 +1,6 @@ // Copyright (c) 2021 Samuel Abraham +using System.Collections; using System.Linq.Expressions; using System.Reflection; using TypeCache.Extensions; @@ -130,7 +131,8 @@ public static SystemType GetSystemType(this Type @this) { { IsArray: true } => SystemType.Array, { IsEnum: true } => TypeStore.SystemTypes[@this.GetEnumUnderlyingType().TypeHandle], - _ when TypeStore.SystemTypes.TryGetValue(@this.ToGenericType()?.TypeHandle ?? @this.TypeHandle, out var systemType) => systemType, + { IsGenericType: true } when TypeStore.SystemTypes.TryGetValue(@this.ToGenericTypeDefinition()!.TypeHandle, out var systemType) => systemType, + _ when TypeStore.SystemTypes.TryGetValue(@this.TypeHandle, out var systemType) => systemType, _ => SystemType.None }; @@ -139,7 +141,7 @@ public static bool Implements(this Type @this, Type type) return type switch { { IsGenericTypeDefinition: false } => @this.IsAssignableTo(type), - { IsInterface: true } => @this.GetInterfaces().Any(_ => type.Is(_.ToGenericType())), + { IsInterface: true } => @this.GetInterfaces().Any(_ => type.Is(_.ToGenericTypeDefinition())), _ => isDescendantOf(@this.BaseType, type) }; @@ -244,7 +246,7 @@ public static bool Is(this Type? @this) public static bool Is(this Type? @this, Type? type) => (@this, type) switch { - ({ IsGenericType: true }, _) or (_, { IsGenericType: true }) => @this.ToGenericType() == type.ToGenericType(), + ({ IsGenericType: true }, _) or (_, { IsGenericType: true }) => @this.ToGenericTypeDefinition() == type.ToGenericTypeDefinition(), _ => @this == type }; @@ -331,118 +333,130 @@ public static bool IsAssignableTo(this Type @this) [DebuggerHidden] public static bool IsConvertible(this Type @this) => @this.IsAssignableTo() - || (@this.ToGenericType() == typeof(Nullable<>) && @this.GenericTypeArguments.First().IsAssignableTo()); + || (@this.ToGenericTypeDefinition() == typeof(Nullable<>) && @this.GenericTypeArguments.First().IsAssignableTo()); - public static bool IsConvertibleTo(this Type @this, Type targetType) - => @this switch + public static bool IsConvertibleTo(this Type @this, Type targetType) => @this?.GetSystemType() switch + { + null => false, + _ when @this == targetType => true, + _ when @this.IsConvertible() && targetType.IsConvertible() => true, + SystemType.DateOnly => targetType.GetSystemType() switch { - null => false, - _ when @this == targetType => true, - _ when targetType == typeof(string) => true, - _ when @this.IsConvertible() && targetType.IsConvertible() => true, - _ when @this == typeof(DateOnly) => targetType switch - { - _ when targetType == typeof(DateTime) => true, - _ when targetType == typeof(DateTimeOffset) => true, - _ when targetType == typeof(TimeSpan) => true, - _ when targetType == typeof(string) => true, - _ when targetType == typeof(int) => true, - _ => false - }, - _ when @this == typeof(DateTime) => targetType switch - { - _ when targetType == typeof(DateOnly) => true, - _ when targetType == typeof(DateTimeOffset) => true, - _ when targetType == typeof(TimeOnly) => true, - _ when targetType == typeof(string) => true, - _ when targetType == typeof(long) => true, - _ => false - }, - _ when @this == typeof(DateTimeOffset) => targetType switch - { - _ when targetType == typeof(DateOnly) => true, - _ when targetType == typeof(DateTime) => true, - _ when targetType == typeof(TimeOnly) => true, - _ when targetType == typeof(string) => true, - _ when targetType == typeof(long) => true, - _ => false - }, - _ when @this == typeof(Enum) => targetType switch - { - _ when targetType == typeof(string) => true, - _ when targetType.IsEnumUnderlyingType() => true, - _ => false - }, - _ when @this == typeof(Int128) => targetType switch - { - _ when targetType == typeof(int) => true, - _ when targetType == typeof(uint) => true, - _ when targetType == typeof(long) => true, - _ when targetType == typeof(ulong) => true, - _ when targetType == typeof(UInt128) => true, - _ when targetType == typeof(string) => true, - _ => false - }, - _ when @this == typeof(nint) => targetType switch - { - _ when targetType == typeof(nuint) => true, - _ when targetType == typeof(int) => true, - _ when targetType == typeof(long) => true, - _ when targetType == typeof(string) => true, - _ => false - }, - _ when @this == typeof(string) => targetType switch - { - _ when targetType == typeof(char) => true, - _ when targetType.IsEnum => true, - _ when targetType == typeof(Guid) => true, - _ when targetType == typeof(Uri) => true, - _ when targetType == typeof(DateOnly) => true, - _ when targetType == typeof(DateTime) => true, - _ when targetType == typeof(DateTimeOffset) => true, - _ when targetType == typeof(Int128) => true, - _ when targetType == typeof(UInt128) => true, - _ when targetType == typeof(nint) => true, - _ when targetType == typeof(nuint) => true, - _ when targetType == typeof(TimeOnly) => true, - _ when targetType == typeof(TimeSpan) => true, - _ when targetType.IsAssignableTo() => true, - _ => false - }, - _ when @this == typeof(TimeOnly) => targetType switch - { - _ when targetType == typeof(TimeSpan) => true, - _ when targetType == typeof(string) => true, - _ when targetType == typeof(long) => true, - _ => false - }, - _ when @this == typeof(TimeSpan) => targetType switch - { - _ when targetType == typeof(TimeOnly) => true, - _ when targetType == typeof(string) => true, - _ when targetType == typeof(long) => true, - _ => false - }, - _ when @this == typeof(UInt128) => targetType switch - { - _ when targetType == typeof(int) => true, - _ when targetType == typeof(uint) => true, - _ when targetType == typeof(long) => true, - _ when targetType == typeof(ulong) => true, - _ when targetType == typeof(Int128) => true, - _ when targetType == typeof(string) => true, - _ => false - }, - _ when @this == typeof(nuint) => targetType switch - { - _ when targetType == typeof(nint) => true, - _ when targetType == typeof(uint) => true, - _ when targetType == typeof(ulong) => true, - _ when targetType == typeof(string) => true, - _ => false - }, + SystemType.DateTime + or SystemType.DateTimeOffset + or SystemType.TimeSpan + or SystemType.String + or SystemType.Int32 + or SystemType.UInt32 + or SystemType.Int64 + or SystemType.UInt64 + or SystemType.Int128 + or SystemType.UInt128 => true, _ => false - }; + }, + SystemType.DateTime or SystemType.DateTimeOffset => targetType.GetSystemType() switch + { + SystemType.DateTime + or SystemType.DateTimeOffset + or SystemType.TimeOnly + or SystemType.String + or SystemType.Int64 + or SystemType.UInt64 + or SystemType.Int128 + or SystemType.UInt128 => true, + _ => false + }, + _ when @this.IsEnum => targetType.GetSystemType() switch + { + SystemType.String => true, + var targetSystemType when targetSystemType.IsEnumUnderlyingType() => true, + _ => false + }, + SystemType.Int128 => targetType.GetSystemType() switch + { + SystemType.Int32 + or SystemType.UInt32 + or SystemType.Int64 + or SystemType.UInt64 + or SystemType.UInt128 + or SystemType.String => true, + _ => false + }, + SystemType.IntPtr => targetType.GetSystemType() switch + { + SystemType.UIntPtr + or SystemType.Int32 + or SystemType.Int64 + or SystemType.Int128 + or SystemType.String => true, + _ => false + }, + SystemType.String => targetType.GetSystemType() switch + { + SystemType.Char + or SystemType.Guid + or SystemType.Uri + or SystemType.DateOnly + or SystemType.DateTime + or SystemType.DateTimeOffset + or SystemType.TimeOnly + or SystemType.TimeSpan + or SystemType.IntPtr + or SystemType.UIntPtr + or SystemType.Int128 + or SystemType.UInt128 => true, + _ when targetType.IsEnum => true, + _ when targetType.IsAssignableTo() => true, + _ => false + }, + SystemType.TimeOnly => targetType.GetSystemType() switch + { + SystemType.TimeSpan + or SystemType.String + or SystemType.Int64 + or SystemType.UInt64 + or SystemType.Int128 + or SystemType.UInt128 => true, + _ => false + }, + SystemType.TimeSpan => targetType.GetSystemType() switch + { + SystemType.TimeOnly + or SystemType.String + or SystemType.Int64 + or SystemType.UInt64 + or SystemType.Int128 + or SystemType.UInt128 => true, + _ => false + }, + SystemType.UInt128 => targetType.GetSystemType() switch + { + SystemType.Int32 + or SystemType.UInt32 + or SystemType.Int64 + or SystemType.UInt64 + or SystemType.Int128 + or SystemType.String => true, + _ => false + }, + SystemType.UIntPtr => targetType.GetSystemType() switch + { + SystemType.IntPtr + or SystemType.Int32 + or SystemType.Int64 + or SystemType.Int128 + or SystemType.String => true, + _ => false + }, + _ => false + }; + + /// + /// => @.IsAssignableTo<>(); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static bool IsEnumerable(this Type @this) + => @this.IsAssignableTo(); /// /// => @.IsAssignableTo<>(); @@ -470,7 +484,7 @@ internal static bool IsInvokable(this Type @this) /// [DebuggerHidden] public static bool IsNullable(this Type @this) - => @this.IsClass || @this.IsPointer || typeof(Nullable<>) == @this.ToGenericType(); + => @this.IsClass || @this.IsPointer || typeof(Nullable<>) == @this.ToGenericTypeDefinition(); /// /// => @.Is() || @.Implements(); @@ -508,7 +522,7 @@ public static DefaultExpression ToDefaultExpression(this Type @this) => Expression.Default(@this); [DebuggerHidden] - public static Type? ToGenericType(this Type? @this) + public static Type? ToGenericTypeDefinition(this Type? @this) => @this switch { { IsGenericTypeDefinition: true } => @this, diff --git a/src/TypeCache/Extensions/RulesBuilderExtensions.cs b/src/TypeCache/Extensions/RulesBuilderExtensions.cs index 6df10cd8..89e1f038 100644 --- a/src/TypeCache/Extensions/RulesBuilderExtensions.cs +++ b/src/TypeCache/Extensions/RulesBuilderExtensions.cs @@ -2,6 +2,7 @@ using System.Data; using System.Text.Json.Nodes; +using Microsoft.Extensions.DependencyInjection; using TypeCache.Data.Mediation; using TypeCache.Mediation; @@ -19,7 +20,7 @@ public static class RulesBuilderExtensions /// /// Requires calls to: /// - ///
+ ///
///
///
public static RulesBuilder AddSqlCommandRules(this RulesBuilder @this) diff --git a/src/TypeCache/Extensions/ServiceCollectionExtensions.cs b/src/TypeCache/Extensions/ServiceCollectionExtensions.cs index 817baaf1..ca877911 100644 --- a/src/TypeCache/Extensions/ServiceCollectionExtensions.cs +++ b/src/TypeCache/Extensions/ServiceCollectionExtensions.cs @@ -2,18 +2,14 @@ using System.Data; using System.Data.Common; -using System.Linq; using System.Reflection; -using System.Text.Json.Nodes; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using TypeCache.Attributes; using TypeCache.Data; -using TypeCache.Data.Mediation; using TypeCache.Mediation; using TypeCache.Net.Mediation; using TypeCache.Utilities; -using static System.FormattableString; namespace TypeCache.Extensions; diff --git a/src/TypeCache/Extensions/StringExtensions.cs b/src/TypeCache/Extensions/StringExtensions.cs index 2985844b..0c57c171 100644 --- a/src/TypeCache/Extensions/StringExtensions.cs +++ b/src/TypeCache/Extensions/StringExtensions.cs @@ -113,11 +113,11 @@ public static bool Has(this string @this, string value, StringComparison compari => @this.Contains(value, comparison); /// - /// => .ToStringComparer().Equals(@, ); + /// => .Equals(@, , ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool Is(this string? @this, string? value, StringComparison comparison = StringComparison.OrdinalIgnoreCase) - => comparison.ToStringComparer().Equals(@this, value); + => string.Equals(@this, value, comparison); /// /// diff --git a/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs b/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs index 21aa89a2..17538cff 100644 --- a/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs +++ b/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs @@ -8,9 +8,10 @@ namespace TypeCache.Net.Mediation; internal sealed class HttpClientValidationRule : IValidationRule { - public async Task ValidateAsync(HttpClientRequest request, CancellationToken token) - { - request?.Message?.RequestUri.AssertNotNull(); - request!.Message.RequestUri!.IsAbsoluteUri.AssertTrue(); - } + public Task ValidateAsync(HttpClientRequest request, CancellationToken token) + => Task.Run(() => + { + request?.Message?.RequestUri.AssertNotNull(); + request!.Message.RequestUri!.IsAbsoluteUri.AssertTrue(); + }, token); } diff --git a/src/TypeCache/TypeCache.csproj b/src/TypeCache/TypeCache.csproj index dbd8f901..dee2a7c2 100644 --- a/src/TypeCache/TypeCache.csproj +++ b/src/TypeCache/TypeCache.csproj @@ -6,7 +6,7 @@ TypeCache true TypeCache - 7.3.12 + 7.3.13 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache Reflection diff --git a/src/TypeCache/Utilities/EnumOf.cs b/src/TypeCache/Utilities/EnumOf.cs index 8e069136..061c12ce 100644 --- a/src/TypeCache/Utilities/EnumOf.cs +++ b/src/TypeCache/Utilities/EnumOf.cs @@ -2,7 +2,6 @@ using System.Collections.Immutable; using System.Reflection; -using TypeCache.Attributes; using TypeCache.Collections; using TypeCache.Extensions; @@ -18,7 +17,7 @@ static EnumOf() Attributes = type.GetCustomAttributes().ToImmutableArray(); Comparer = new EnumComparer(); Flags = Attributes.Any(); - Name = Attributes.FirstOrDefault()?.Name ?? type.Name; + Name = type.Name; Tokens = Enum.GetValues().ToImmutableDictionary(value => value, value => new Token(value), Comparer); } diff --git a/src/TypeCache/Utilities/TypeStore.cs b/src/TypeCache/Utilities/TypeStore.cs index b8d13636..de18329f 100644 --- a/src/TypeCache/Utilities/TypeStore.cs +++ b/src/TypeCache/Utilities/TypeStore.cs @@ -35,45 +35,12 @@ static TypeStore() { var type when type == typeof(object) => ObjectType.Object, var type when type == typeof(string) => ObjectType.String, - var type when type == typeof(StringBuilder) => ObjectType.StringBuilder, { IsPrimitive: true } => ObjectType.Primitive, { IsArray: true } => ObjectType.Array, { IsEnum: true } => ObjectType.Enum, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.Attribute, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.DataColumn, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.DataRow, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.DataRowView, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.DataSet, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.DataTable, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.Delegate, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.Exception, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.JsonNode, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.OrderedDictionary, - { IsClass: true } type when type.IsAssignableTo() => ObjectType.Stream, + { IsClass: true } type when tryGetClassObjectType(type, out var objectType) => objectType, var type when type.IsAssignableTo() => ObjectType.AsyncResult, - var type when type.IsGenericType || type.IsGenericTypeDefinition => type.ToGenericType()! switch - { - var genericType when genericType.IsOrImplements(typeof(IImmutableDictionary<,>)) => ObjectType.ImmutableDictionary, - var genericType when genericType.IsOrImplements(typeof(IImmutableSet<>)) => ObjectType.ImmutableSet, - var genericType when genericType.IsOrImplements(typeof(IImmutableList<>)) => ObjectType.ImmutableList, - var genericType when genericType.IsOrImplements(typeof(IImmutableQueue<>)) => ObjectType.ImmutableQueue, - var genericType when genericType.IsOrImplements(typeof(IImmutableStack<>)) => ObjectType.ImmutableStack, - var genericType when genericType.IsOrImplements(typeof(IReadOnlyDictionary<,>)) => ObjectType.ReadOnlyDictionary, - var genericType when genericType.IsOrImplements(typeof(IReadOnlySet<>)) => ObjectType.ReadOnlySet, - var genericType when genericType.IsOrImplements(typeof(IReadOnlyList<>)) => ObjectType.ReadOnlyList, - var genericType when genericType.IsOrImplements(typeof(IReadOnlyCollection<>)) => ObjectType.ReadOnlyCollection, - var genericType when genericType.IsOrImplements(typeof(IDictionary<,>)) => ObjectType.Dictionary, - var genericType when genericType.IsOrImplements(typeof(ISet<>)) => ObjectType.Set, - var genericType when genericType.IsOrImplements(typeof(IAsyncEnumerable<>)) => ObjectType.AsyncEnumerable, - var genericType when genericType.IsOrImplements(typeof(IAsyncEnumerator<>)) => ObjectType.AsyncEnumerator, - var genericType when genericType.IsOrImplements(typeof(IList<>)) => ObjectType.List, - var genericType when genericType.IsOrImplements(typeof(ICollection<>)) => ObjectType.Collection, - var genericType when genericType.IsOrImplements(typeof(IEnumerable<>)) => ObjectType.Enumerable, - var genericType when genericType.IsOrImplements(typeof(IEnumerator<>)) => ObjectType.Enumerator, - var genericType when genericType.IsOrImplements(typeof(IObservable<>)) => ObjectType.Observable, - var genericType when genericType.IsOrImplements(typeof(IObserver<>)) => ObjectType.Observer, - _ => ObjectType.Unknown - }, + { IsGenericType: true } type => getGenericObjectType(type.ToGenericTypeDefinition()!), _ => ObjectType.Unknown }); SystemTypes = new Dictionary(141) @@ -220,6 +187,52 @@ var genericType when genericType.IsOrImplements(typeof(IObserver<>)) => ObjectTy { typeof(ValueTask).TypeHandle, SystemType.ValueTask }, { typeof(ValueTask<>).TypeHandle, SystemType.ValueTask }, }.ToImmutableDictionary(); + + static bool tryGetClassObjectType(Type type, out ObjectType objectType) + { + objectType = type switch + { + _ when type.IsAssignableTo() => ObjectType.StringBuilder, + _ when type.IsAssignableTo() => ObjectType.Attribute, + _ when type.IsAssignableTo() => ObjectType.DataColumn, + _ when type.IsAssignableTo() => ObjectType.DataRow, + _ when type.IsAssignableTo() => ObjectType.DataRowView, + _ when type.IsAssignableTo() => ObjectType.DataSet, + _ when type.IsAssignableTo() => ObjectType.DataTable, + _ when type.IsAssignableTo() => ObjectType.Delegate, + _ when type.IsAssignableTo() => ObjectType.Exception, + _ when type.IsAssignableTo() => ObjectType.JsonNode, + _ when type.IsAssignableTo() => ObjectType.OrderedDictionary, + _ when type.IsAssignableTo() => ObjectType.Stream, + _ => ObjectType.Unknown + }; + return objectType == ObjectType.Unknown; + } + + static ObjectType getGenericObjectType(Type type) => type switch + { + null => ObjectType.Unknown, + _ when type.IsOrImplements(typeof(IImmutableDictionary<,>)) => ObjectType.ImmutableDictionary, + _ when type.IsOrImplements(typeof(IImmutableSet<>)) => ObjectType.ImmutableSet, + _ when type.IsOrImplements(typeof(IImmutableList<>)) => ObjectType.ImmutableList, + _ when type.IsOrImplements(typeof(IImmutableQueue<>)) => ObjectType.ImmutableQueue, + _ when type.IsOrImplements(typeof(IImmutableStack<>)) => ObjectType.ImmutableStack, + _ when type.IsOrImplements(typeof(IReadOnlyDictionary<,>)) => ObjectType.ReadOnlyDictionary, + _ when type.IsOrImplements(typeof(IReadOnlySet<>)) => ObjectType.ReadOnlySet, + _ when type.IsOrImplements(typeof(IReadOnlyList<>)) => ObjectType.ReadOnlyList, + _ when type.IsOrImplements(typeof(IReadOnlyCollection<>)) => ObjectType.ReadOnlyCollection, + _ when type.IsOrImplements(typeof(IDictionary<,>)) => ObjectType.Dictionary, + _ when type.IsOrImplements(typeof(ISet<>)) => ObjectType.Set, + _ when type.IsOrImplements(typeof(IAsyncEnumerable<>)) => ObjectType.AsyncEnumerable, + _ when type.IsOrImplements(typeof(IAsyncEnumerator<>)) => ObjectType.AsyncEnumerator, + _ when type.IsOrImplements(typeof(IList<>)) => ObjectType.List, + _ when type.IsOrImplements(typeof(ICollection<>)) => ObjectType.Collection, + _ when type.IsOrImplements(typeof(IEnumerable<>)) => ObjectType.Enumerable, + _ when type.IsOrImplements(typeof(IEnumerator<>)) => ObjectType.Enumerator, + _ when type.IsOrImplements(typeof(IObservable<>)) => ObjectType.Observable, + _ when type.IsOrImplements(typeof(IObserver<>)) => ObjectType.Observer, + _ => ObjectType.Unknown + }; } public static IReadOnlyDictionary> DefaultValueTypeConstructorInvokes { get; } diff --git a/tests/TypeCache.GraphQL.TestApp/Tables/Person.cs b/tests/TypeCache.GraphQL.TestApp/Tables/Person.cs index 01027e45..2b631585 100644 --- a/tests/TypeCache.GraphQL.TestApp/Tables/Person.cs +++ b/tests/TypeCache.GraphQL.TestApp/Tables/Person.cs @@ -4,7 +4,6 @@ using TypeCache.Attributes; using TypeCache.GraphQL.Attributes; using TypeCache.GraphQL.Types; -using TypeCache.Reflection; namespace TypeCache.GraphQL.TestApp.Tables; @@ -24,7 +23,7 @@ public class Person public string? AdditionalContactInfo { get; set; } public string? Demographics { get; set; } [GraphQLType()] - [Name("rowguid")] + [GraphQLName("rowguid")] public Guid Rowguid { get; set; } [GraphQLType>()] public DateTime ModifiedDate { get; set; } diff --git a/tests/TypeCache.Tests/Data/Extensions/SqlExtensions.cs b/tests/TypeCache.Tests/Data/Extensions/SqlExtensions.cs index d862d3b6..ba0a17a9 100644 --- a/tests/TypeCache.Tests/Data/Extensions/SqlExtensions.cs +++ b/tests/TypeCache.Tests/Data/Extensions/SqlExtensions.cs @@ -16,10 +16,8 @@ public class Person { public int ID { get; set; } - [Name("First Name")] public string FirstName { get; set; } - [Name("Last_Name")] public string LastName { get; set; } public int Age { get; set; } @@ -133,8 +131,8 @@ public void CreateBatchInsertSQL() var objectSchema = new ObjectSchema(dataSource, DatabaseObjectType.Table, table, "db", "dbo", "Test", new[] { new ColumnSchema("ID", false, true, true, true, typeof(int).TypeHandle), - new ColumnSchema("First Name", false, false, false, false, typeof(string).TypeHandle), - new ColumnSchema("Last Name", false, false, false, false, typeof(string).TypeHandle), + new ColumnSchema("FirstName", false, false, false, false, typeof(string).TypeHandle), + new ColumnSchema("LastName", false, false, false, false, typeof(string).TypeHandle), }); var data = new[] { @@ -144,13 +142,13 @@ public void CreateBatchInsertSQL() }; var expected = Invariant($@"INSERT INTO {table} -([First Name], [Last_Name]) -OUTPUT INSERTED.ID, INSERTED.[Last Name] +([FirstName], [LastName]) +OUTPUT INSERTED.ID, INSERTED.[LastName] VALUES (N'FirstName1', N'LastName1') , (N'FirstName2', N'LastName2') , (N'FirstName3', N'LastName3'); "); - var actual = objectSchema.CreateInsertSQL(new[] { "First Name", "Last_Name" }, data, "INSERTED.ID", "INSERTED.[Last Name]"); + var actual = objectSchema.CreateInsertSQL(new[] { "FirstName", "LastName" }, data, "INSERTED.ID", "INSERTED.[LastName]"); Assert.Equal(expected, actual); } @@ -197,26 +195,26 @@ public void CreateSelectSQL() var objectSchema = new ObjectSchema(dataSource, DatabaseObjectType.Table, new DatabaseObject("db.dbo.Test"), "db", "dbo", "Test", new[] { new ColumnSchema("ID", false, true, true, true, typeof(int).TypeHandle), - new ColumnSchema("First Name", false, false, false, false, typeof(string).TypeHandle), - new ColumnSchema("Last Name", false, false, false, false, typeof(string).TypeHandle), + new ColumnSchema("FirstName", false, false, false, false, typeof(string).TypeHandle), + new ColumnSchema("LastName", false, false, false, false, typeof(string).TypeHandle), }); var selectQuery = new SelectQuery { - Select = new[] { "ID", "TRIM([First Name]) AS [First Name]", "UPPER([LastName]) AS LastName", "40 Age", "Amount AS Amount" }, + Select = new[] { "ID", "TRIM([FirstName]) AS [FirstName]", "UPPER([LastName]) AS LastName", "40 Age", "Amount AS Amount" }, From = new("[dbo].[NonCustomers]"), Having = "MAX([Age]) > 40", - Where = "[First Name] = N'Sarah' AND [Last_Name] = N'Marshal'", - OrderBy = new[] { "[First Name] ASC", "Last_Name DESC" }, + Where = "[FirstName] = N'Sarah' AND [LastName] = N'Marshal'", + OrderBy = new[] { "[FirstName] ASC", "LastName DESC" }, Fetch = 100, Offset = 0, TableHints = "WITH(NOLOCK)" }; - var expected = Invariant($@"SELECT ID, TRIM([First Name]) AS [First Name], UPPER([LastName]) AS LastName, 40 Age, Amount AS Amount + var expected = Invariant($@"SELECT ID, TRIM([FirstName]) AS [FirstName], UPPER([LastName]) AS LastName, 40 Age, Amount AS Amount FROM [dbo].[NonCustomers] WITH(NOLOCK) -WHERE [First Name] = N'Sarah' AND [Last_Name] = N'Marshal' +WHERE [FirstName] = N'Sarah' AND [LastName] = N'Marshal' HAVING MAX([Age]) > 40 -ORDER BY [First Name] ASC, Last_Name DESC +ORDER BY [FirstName] ASC, LastName DESC FETCH NEXT 100 ROWS ONLY; "); var actual = objectSchema.CreateSelectSQL(selectQuery); @@ -232,8 +230,8 @@ public void CreateBatchUpdateSQL() var objectSchema = new ObjectSchema(dataSource, DatabaseObjectType.Table, table, "db", "dbo", "Test", new[] { new ColumnSchema("ID", false, true, true, true, typeof(int).TypeHandle), - new ColumnSchema("First Name", false, false, false, false, typeof(string).TypeHandle), - new ColumnSchema("Last Name", false, false, false, false, typeof(string).TypeHandle), + new ColumnSchema("FirstName", false, false, false, false, typeof(string).TypeHandle), + new ColumnSchema("LastName", false, false, false, false, typeof(string).TypeHandle), }); var data = new[] { @@ -243,19 +241,19 @@ public void CreateBatchUpdateSQL() }; var expected = Invariant($@"UPDATE {table} WITH(UPDLOCK) -SET [First Name] = data.[First Name], [Last_Name] = data.[Last_Name] -OUTPUT INSERTED.[First Name] AS [First Name], DELETED.Last_Name AS Last_Name, INSERTED.[ID] AS [ID] +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 ( VALUES (N'FirstName1', N'LastName1') , (N'FirstName2', N'LastName2') , (N'FirstName3', N'LastName3') -) AS data ([First Name], [Last_Name]) +) AS data ([FirstName], [LastName]) ON data.[ID] = _.[ID]; "); - var actual = objectSchema.CreateUpdateSQL(new[] { "First Name", "Last_Name" }, data - , "INSERTED.[First Name] AS [First Name]", "DELETED.Last_Name AS Last_Name", "INSERTED.[ID] AS [ID]"); + var actual = objectSchema.CreateUpdateSQL(new[] { "FirstName", "LastName" }, data + , "INSERTED.[FirstName] AS [FirstName]", "DELETED.LastName AS LastName", "INSERTED.[ID] AS [ID]"); Assert.Equal(expected, actual); } diff --git a/tests/TypeCache.Tests/Extensions/AssertExtensions.cs b/tests/TypeCache.Tests/Extensions/AssertExtensions.cs index 3078d4b6..37f49cd2 100644 --- a/tests/TypeCache.Tests/Extensions/AssertExtensions.cs +++ b/tests/TypeCache.Tests/Extensions/AssertExtensions.cs @@ -20,7 +20,7 @@ public void Assert() Xunit.Assert.Throws(() => "AAA".AssertEquals("bbb")); Xunit.Assert.Throws(() => (null as string).AssertEquals("bbb")); Xunit.Assert.Throws(() => "AAA".AssertEquals("bbb", StringComparer.Ordinal)); - Xunit.Assert.Throws(() => "AAA".AssertEquals(null, null)); + Xunit.Assert.Throws(() => "AAA".AssertEquals(null, null)); } [Fact]