├── docs ├── basics │ ├── README.md │ ├── executing_a_query.md │ └── build_a_schema.md ├── development │ └── README.md ├── queries_and_mutations │ ├── README.md │ ├── aliases.md │ ├── arguments.md │ ├── fields.md │ ├── fragments.md │ ├── mutations.md │ └── inline-fragments.md └── introduction │ └── README.md ├── GraphQL.Parser.Test ├── Program.fs ├── packages.config ├── App.config ├── AssemblyInfo.fs └── GraphQL.Parser.Test.fsproj ├── GraphQL.Net ├── ResolutionType.cs ├── InteropHelpers.cs ├── StringExtensions.cs ├── SchemaAdapters │ ├── InfoExtensions.cs │ ├── Info.cs │ ├── SchemaRootType.cs │ ├── SchemaType.cs │ ├── Schema.cs │ └── SchemaField.cs ├── App.config ├── ExpressionOptions.cs ├── GraphQLFieldBuilder.cs ├── GraphQLType.cs ├── ParameterReplacer.cs ├── Properties │ └── AssemblyInfo.cs ├── VariableTypes.cs ├── GraphQL.cs ├── DynamicTypeBuilder.cs ├── GraphQL.Net.csproj └── GraphQLField.cs ├── Tests ├── packages.config ├── SchemaSetupTests.cs ├── App.config ├── Test.cs ├── Properties │ └── AssemblyInfo.cs ├── IntrospectionTests.cs └── InMemoryExecutionTests.cs ├── GraphQL.Parser ├── packages.config ├── app.config ├── README.md ├── SourceTypes.fs ├── AssemblyInfo.fs ├── Integration │ ├── GraphQLDocument.fs │ └── CS.fs └── Utilities.fs ├── examples ├── 09-cosmos-db │ ├── WebApi.Tests │ │ ├── Models │ │ │ ├── GraphQLResponse.cs │ │ │ └── UsersResponse.cs │ │ ├── WebApi.Tests.csproj │ │ ├── MutationTests.cs │ │ ├── BaseTests.cs │ │ └── QueryTests.cs │ ├── WebApi │ │ ├── Models │ │ │ ├── Profile.cs │ │ │ ├── Account.cs │ │ │ ├── GraphQLError.cs │ │ │ ├── User.cs │ │ │ ├── GraphQLRequest.cs │ │ │ ├── GraphQLLocation.cs │ │ │ └── GraphQLResponse.cs │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Program.cs │ │ ├── Data │ │ │ └── GraphQLContext.cs │ │ ├── WebApi.csproj │ │ ├── Services │ │ │ ├── GraphQL.cs │ │ │ ├── MyDocumentClientInitializer.cs │ │ │ └── MyDocumentClient.cs │ │ ├── Startup.cs │ │ ├── Ms │ │ │ └── Extensions │ │ │ │ └── DependencyInjection │ │ │ │ └── WebApiServiceCollectionExtensions.cs │ │ └── Controllers │ │ │ └── GraphQLController.cs │ ├── README.md │ └── 09-cosmos-db.sln ├── 03-aliases │ ├── 03-aliases │ │ ├── app.config │ │ ├── packages.config │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ └── AliasesExample.cs │ ├── 03-aliases.sln │ └── .gitignore ├── 04-fragments │ ├── 04-fragments │ │ ├── app.config │ │ ├── packages.config │ │ └── Properties │ │ │ └── AssemblyInfo.cs │ ├── 04-fragments.sln │ └── .gitignore ├── 07-mutations │ ├── 07-mutations │ │ ├── app.config │ │ ├── packages.config │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ └── MutationsExample.cs │ ├── 07-mutations.sln │ └── .gitignore ├── 01-simple-query │ ├── 01-simple-query │ │ ├── app.config │ │ ├── packages.config │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ └── SimpleQueryExample.cs │ ├── 01-simple-query.sln │ └── .gitignore ├── 02-field-arguments │ ├── 02-field-arguments │ │ ├── app.config │ │ ├── packages.config │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ └── FieldArgumentsExample.cs │ ├── 02-field-arguments.sln │ └── .gitignore └── 08-inline-fragments │ ├── 08-inline-fragments │ ├── app.config │ ├── packages.config │ ├── Properties │ │ └── AssemblyInfo.cs │ └── InlineFragmentsExample.cs │ ├── 08-inline-fragments.sln │ └── .gitignore ├── Settings.FSharpLint ├── .travis.yml ├── GraphQL.Net.sln.DotSettings ├── Tests.EF ├── packages.config ├── Properties │ └── AssemblyInfo.cs └── App.config ├── SUMMARY.md ├── GraphQL.Net.nuspec ├── LICENSE.txt ├── GraphQL.Net.sln ├── .gitignore └── README.md /docs/basics/README.md: -------------------------------------------------------------------------------- 1 | # Basics 2 | -------------------------------------------------------------------------------- /GraphQL.Parser.Test/Program.fs: -------------------------------------------------------------------------------- 1 | [] 2 | let main argv = 3 | printfn "%A" argv 4 | 0 5 | -------------------------------------------------------------------------------- /docs/development/README.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | - Visual Studio 2015 (Visual C#, Visual F#) 4 | - NUnit 3 Test Adapter (Visual Studio Extension) -------------------------------------------------------------------------------- /GraphQL.Parser.Test/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /GraphQL.Net/ResolutionType.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.Net 2 | { 3 | internal enum ResolutionType 4 | { 5 | Unmodified, 6 | ToList, 7 | FirstOrDefault, 8 | First 9 | } 10 | } -------------------------------------------------------------------------------- /Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GraphQL.Parser/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/queries_and_mutations/README.md: -------------------------------------------------------------------------------- 1 | # Queries and Mutations 2 | 3 | This section is oriented towards the official GraphQL documentation on [Queries and Mutations](http://graphql.org/learn/queries/). 4 | 5 | In the following sections we implement the GraphQL examples using GraphQL.net. -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi.Tests/Models/GraphQLResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebApi.Tests.Models 4 | { 5 | public class GraphQLResponse 6 | { 7 | [JsonProperty("data")] 8 | public TData Data { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Settings.FSharpLint: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /GraphQL.Net/InteropHelpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.FSharp.Core; 2 | 3 | namespace GraphQL.Net 4 | { 5 | public static class InteropHelpers 6 | { 7 | public static T OrDefault(this FSharpOption option) 8 | => option == null ? default(T) : option.Value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi.Tests/Models/UsersResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using WebApi.Models; 3 | 4 | namespace WebApi.Tests.Models 5 | { 6 | public class UsersResponse 7 | { 8 | [JsonProperty("users")] 9 | public User[] Users { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Models/Profile.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebApi.Models 4 | { 5 | public class Profile 6 | { 7 | [JsonProperty(PropertyName = "age")] 8 | public int Age { get; set; } 9 | 10 | [JsonProperty(PropertyName = "gender")] 11 | public string Gender { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /GraphQL.Net/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.Net 2 | { 3 | public static class StringExtensions 4 | { 5 | public static string ToCamelCase(this string val) 6 | { 7 | if (string.IsNullOrEmpty(val)) 8 | return ""; 9 | return char.ToLower(val[0]) + val.Substring(1); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /GraphQL.Net/SchemaAdapters/InfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Parser; 2 | 3 | namespace GraphQL.Net.SchemaAdapters 4 | { 5 | static class InfoExtensions 6 | { 7 | public static GraphQLField Field(this ISchemaInfo schemaInfo) => schemaInfo.Info?.Field; 8 | public static GraphQLType Type(this ISchemaInfo schemaInfo) => schemaInfo.Info?.Type; 9 | } 10 | } -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Models/Account.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebApi.Models 4 | { 5 | public class Account 6 | { 7 | [JsonProperty(PropertyName = "id")] 8 | public string Id { get; set; } 9 | 10 | [JsonProperty(PropertyName = "name")] 11 | public string Name { get; set; } 12 | 13 | public bool Paid { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Models/GraphQLError.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace WebApi.Models 5 | { 6 | public class GraphQLError 7 | { 8 | [JsonProperty("message")] 9 | public string Message { get; set; } 10 | 11 | [JsonProperty("locations")] 12 | public IEnumerable Locations { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /GraphQL.Net/SchemaAdapters/Info.cs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.Net.SchemaAdapters 2 | { 3 | class Info 4 | { 5 | public Info(GraphQLField field) 6 | { 7 | Field = field; 8 | } 9 | 10 | public Info(GraphQLType type) 11 | { 12 | Type = type; 13 | } 14 | 15 | public GraphQLField Field { get; } 16 | public GraphQLType Type { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /examples/03-aliases/03-aliases/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/04-fragments/04-fragments/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/07-mutations/07-mutations/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | solution: GraphQL.Net.sln 3 | install: 4 | - nuget restore GraphQL.Net.sln 5 | - nuget install NUnit.Runners -Version 3.2.0 -OutputDirectory testrunner 6 | script: 7 | - xbuild /p:Configuration=Debug GraphQL.Net.sln 8 | - mono ./testrunner/NUnit.ConsoleRunner.3.2.0/tools/nunit3-console.exe ./Tests/bin/Debug/Tests.dll 9 | - mono ./testrunner/NUnit.ConsoleRunner.3.2.0/tools/nunit3-console.exe ./GraphQL.Parser.Test/bin/Debug/GraphQL.Parser.Test.exe 10 | -------------------------------------------------------------------------------- /examples/01-simple-query/01-simple-query/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "CosmosDb": { 11 | "EndpointUrl": "https://localhost:8081", 12 | "DatabaseId": "graphql-cosmos-db", 13 | "AuthorizationKey": "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/02-field-arguments/02-field-arguments/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/08-inline-fragments/08-inline-fragments/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Tests/SchemaSetupTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Net; 3 | using NUnit.Framework; 4 | 5 | namespace Tests 6 | { 7 | [TestFixture] 8 | public class SchemaSetupTests 9 | { 10 | [Test] 11 | public void IncompleteSchemaCantBeQueried() 12 | { 13 | var schema = new GraphQLSchema(() => null); 14 | Assert.Throws(() => new GraphQL(schema).ExecuteQuery("query users { id }")); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/03-aliases/03-aliases/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GraphQL.Net/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/04-fragments/04-fragments/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/07-mutations/07-mutations/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "Debug": { 5 | "LogLevel": { 6 | "Default": "Warning" 7 | } 8 | }, 9 | "Console": { 10 | "LogLevel": { 11 | "Default": "Warning" 12 | } 13 | } 14 | }, 15 | "CosmosDb": { 16 | "EndpointUrl": "https://localhost:8081", 17 | "DatabaseId": "graphql-cosmos-db", 18 | "AuthorizationKey": "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/01-simple-query/01-simple-query/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/02-field-arguments/02-field-arguments/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Models/User.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebApi.Models 4 | { 5 | public class User 6 | { 7 | [JsonProperty(PropertyName = "id")] 8 | public string Id { get; set; } 9 | 10 | [JsonProperty(PropertyName = "profile")] 11 | public Profile Profile { get; set; } 12 | 13 | [JsonProperty(PropertyName = "accountId")] 14 | public string AccountId { get; set; } 15 | [JsonProperty(PropertyName = "account")] 16 | public Account Account { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/08-inline-fragments/08-inline-fragments/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GraphQL.Parser/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /GraphQL.Parser.Test/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Models/GraphQLRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace WebApi.Models 8 | { 9 | public class GraphQLRequest 10 | { 11 | [JsonProperty("query")] 12 | public string Query { get; set; } 13 | 14 | [JsonProperty("operationName")] 15 | public string OperationName { get; set; } 16 | 17 | [JsonProperty("variables")] 18 | public Dictionary Variables { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /GraphQL.Net.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | CLR 3 | GQL 4 | QL -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Models/GraphQLLocation.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebApi.Models 4 | { 5 | /// 6 | /// Describes the location of a GraphQL error. 7 | /// 8 | public class GraphQLLocation 9 | { 10 | /// 11 | /// 12 | /// TODO: Must be above 1 13 | /// 14 | [JsonProperty("line")] 15 | public int Line { get; set; } 16 | 17 | /// 18 | /// 19 | /// TODO: Must be above 1 20 | /// 21 | [JsonProperty("column")] 22 | public int Column { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests.EF/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace WebApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GraphQL.Net/SchemaAdapters/SchemaRootType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using GraphQL.Parser; 4 | using GraphQL.Parser.CS; 5 | 6 | namespace GraphQL.Net.SchemaAdapters 7 | { 8 | class SchemaRootType : SchemaQueryTypeCS 9 | { 10 | public SchemaRootType(Schema schema, GraphQLType baseQueryType) 11 | { 12 | Fields = baseQueryType.OwnFields 13 | .Select(f => new SchemaField(this, f, schema)) 14 | .ToDictionary(f => f.FieldName, f => f as ISchemaField); 15 | } 16 | 17 | public override IReadOnlyDictionary> Fields { get; } 18 | public override string TypeName => "SchemaRoot"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Read Me](README.md) 4 | * [Introduction](docs/introduction/README.md) 5 | * [Basics](docs/basics/README.md) 6 | * [Build a Schema](docs/basics/build_a_schema.md) 7 | * [Executing a Query](docs/basics/executing_a_query.md) 8 | * [Queries and Mutations](docs/queries_and_mutations/README.md) 9 | * [Fields](docs/queries_and_mutations/fields.md) 10 | * [Arguments](docs/queries_and_mutations/arguments.md) 11 | * [Aliases](docs/queries_and_mutations/aliases.md) 12 | * [Fragments](docs/queries_and_mutations/fragments.md) 13 | * [Mutations](docs/queries_and_mutations/mutations.md) 14 | * [Inline Fragments](docs/queries_and_mutations/inline-fragments.md) 15 | * [Development](docs/development/README.md) 16 | 17 | -------------------------------------------------------------------------------- /docs/basics/executing_a_query.md: -------------------------------------------------------------------------------- 1 | # Executing a Query 2 | ```csharp 3 | var query = @"{ 4 | user(id:1) { 5 | userId : id 6 | userName : name 7 | account { 8 | id 9 | paid 10 | } 11 | totalUsers 12 | }"; 13 | 14 | var gql = new GraphQL(schema); 15 | var dict = gql.ExecuteQuery(query); 16 | Console.WriteLine(JsonConvert.SerializeObject(dict, Formatting.Indented)); 17 | 18 | // { 19 | // "user": { 20 | // "userId": 1, 21 | // "userName": "Joe User", 22 | // "account": { 23 | // "id": 1, 24 | // "paid": true 25 | // }, 26 | // "totalUsers": 2 27 | // } 28 | // } 29 | ``` 30 | 31 | The results from executing the query are returned as a nested Dictionary which can easily be converted to JSON and returned to the user. -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Data/GraphQLContext.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using WebApi.Services; 3 | 4 | namespace WebApi.Data 5 | { 6 | public class GraphQLContext 7 | { 8 | private readonly IMyDocumentClient documentClient; 9 | 10 | public GraphQLContext(IMyDocumentClient documentClient) 11 | { 12 | this.documentClient = documentClient; 13 | } 14 | 15 | public IQueryable Users 16 | { 17 | get 18 | { 19 | return documentClient.GetUsers(); 20 | } 21 | } 22 | 23 | public IQueryable Accounts 24 | { 25 | get 26 | { 27 | return documentClient.GetAccounts(); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi.Tests/WebApi.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net471 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi.Tests/MutationTests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Linq; 3 | using WebApi.Models; 4 | using WebApi.Services; 5 | using WebApi.Tests.Models; 6 | using Xunit; 7 | using FluentAssertions; 8 | using Newtonsoft.Json.Linq; 9 | using Seeder = WebApi.Services.MyDocumentClientInitializer; 10 | 11 | namespace WebApi.Tests 12 | { 13 | public class MutationTests : BaseTests 14 | { 15 | //[Fact] 16 | //public async void Test1() 17 | //{ 18 | // // Act 19 | // var resp = await Get(@"{ users { id } }"); 20 | 21 | // // Assert 22 | // var actual = JObject.Parse(resp)["data"]["users"].Children().Select(j => j.ToObject()); 23 | 24 | // actual.Should().BeEquivalentTo(Seeder.Users, options => options.Including(u => u.Id)); 25 | //} 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Converters; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace Tests 8 | { 9 | public static class Test 10 | { 11 | private static readonly JsonSerializer Serializer = new JsonSerializer 12 | { 13 | Converters = { new StringEnumConverter() }, 14 | }; 15 | public static void DeepEquals(IDictionary results, string json) 16 | { 17 | var expected = JObject.Parse(json); 18 | var actual = JObject.FromObject(results, Serializer); 19 | if (expected.ToString() == actual.ToString()) 20 | return; 21 | 22 | throw new Exception($"Results do not match expectation:\n\nExpected:\n{expected}\n\nActual:\n{actual}"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /GraphQL.Net/ExpressionOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GraphQL.Net 4 | { 5 | internal class ExpressionOptions 6 | { 7 | public ExpressionOptions(Func validForQueryType, bool castAssignment = false, 8 | bool nullCheckLists = false, bool typeCheckInheritance = false, bool useBaseType = false) 9 | { 10 | ValidForQueryType = validForQueryType; 11 | CastAssignment = castAssignment; 12 | NullCheckLists = nullCheckLists; 13 | UseBaseType = useBaseType; 14 | TypeCheckInheritance = typeCheckInheritance; 15 | } 16 | 17 | public Func ValidForQueryType { get; } 18 | public bool CastAssignment { get; } 19 | public bool NullCheckLists { get; } 20 | public bool UseBaseType { get; } 21 | public bool TypeCheckInheritance { get; } 22 | } 23 | } -------------------------------------------------------------------------------- /examples/09-cosmos-db/README.md: -------------------------------------------------------------------------------- 1 | # GraphQL.NET with Azure Cosmos DB example 2 | 3 | This is an example of a real-world setup with Cosmos DB as the persistance layer. 4 | 5 | ## Run it yourself 6 | 7 | * Clone this repo locally. 8 | * Install the [Cosmos DB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator) or spin up your own Cosmos DB instance in Azure (at a cost). 9 | * If you're not using the emulator, replace the CosmosDb settings in "appsettings.Development.json" with your own. 10 | * Build and run the application. It will create in your instance a db, "users" collection (400RUs) and seed some users automatically for you. 11 | 12 | ## Backlog 13 | 14 | * Seed process: make user documents more complex for better scenarios. 15 | * Write a bunch of queries at API endpoints to test, or maybe just a generic endpoint that accepts the query as a string and runs it (e.g. for Postman usage) -------------------------------------------------------------------------------- /examples/03-aliases/03-aliases.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "03-aliases", "03-aliases\03-aliases.csproj", "{C59412AF-5E2F-4470-843C-1B6A305334A8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C59412AF-5E2F-4470-843C-1B6A305334A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C59412AF-5E2F-4470-843C-1B6A305334A8}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C59412AF-5E2F-4470-843C-1B6A305334A8}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C59412AF-5E2F-4470-843C-1B6A305334A8}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /examples/04-fragments/04-fragments.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "04-fragments", "04-fragments\04-fragments.csproj", "{5725C8BD-3397-4FE7-A860-C484D71B11C9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {5725C8BD-3397-4FE7-A860-C484D71B11C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {5725C8BD-3397-4FE7-A860-C484D71B11C9}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {5725C8BD-3397-4FE7-A860-C484D71B11C9}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {5725C8BD-3397-4FE7-A860-C484D71B11C9}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /examples/07-mutations/07-mutations.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "07-mutations", "07-mutations\07-mutations.csproj", "{4D86F2BD-476F-4E5E-A268-50E65076F773}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4D86F2BD-476F-4E5E-A268-50E65076F773}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {4D86F2BD-476F-4E5E-A268-50E65076F773}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {4D86F2BD-476F-4E5E-A268-50E65076F773}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {4D86F2BD-476F-4E5E-A268-50E65076F773}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /GraphQL.Net/GraphQLFieldBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Parser; 3 | 4 | namespace GraphQL.Net 5 | { 6 | public class GraphQLFieldBuilder 7 | { 8 | private readonly GraphQLField _field; 9 | 10 | internal GraphQLFieldBuilder(GraphQLField field) 11 | { 12 | _field = field; 13 | } 14 | 15 | public GraphQLFieldBuilder WithDescription(string description) 16 | { 17 | _field.Description = description; 18 | return this; 19 | } 20 | 21 | public GraphQLFieldBuilder WithComplexity(long min, long max) 22 | { 23 | _field.Complexity = Complexity.NewRange(Tuple.Create(min, max)); 24 | return this; 25 | } 26 | 27 | // TODO: This should be removed once we figure out a better way to do it 28 | internal GraphQLFieldBuilder WithResolutionType(ResolutionType type) 29 | { 30 | _field.ResolutionType = type; 31 | return this; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /examples/01-simple-query/01-simple-query.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "01-simple-query", "01-simple-query\01-simple-query.csproj", "{F8A6BD38-A5C7-4D5A-9841-BA012E7C3B83}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F8A6BD38-A5C7-4D5A-9841-BA012E7C3B83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F8A6BD38-A5C7-4D5A-9841-BA012E7C3B83}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F8A6BD38-A5C7-4D5A-9841-BA012E7C3B83}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F8A6BD38-A5C7-4D5A-9841-BA012E7C3B83}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /docs/introduction/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Many of the .NET GraphQL implementations that have come out so far only seem to work in memory. 4 | For me, this isn't terribly useful since most of my data is stored in a database (and I assume that's the case for many others). 5 | This library is an implementation of the GraphQL spec that converts GraphQL queries to IQueryable. 6 | That IQueryable can then be executed using the ORM of your choice. 7 | 8 | Here's a descriptive example, using an example from [the GraphQL spec](http://facebook.github.io/graphql/#sec-Language.Query-Document.Arguments): 9 | 10 | ``` 11 | { 12 | user(id: 4) { 13 | id 14 | name 15 | profilePic(size: 100) 16 | } 17 | } 18 | ``` 19 | 20 | The above GraphQL query could be translated to: 21 | 22 | ```csharp 23 | db.Users 24 | .Where(u => u.Id == 4) 25 | .Select(u => new 26 | { 27 | id = u.Id, 28 | name = u.Name, 29 | profilePic = db.ProfilePics 30 | .FirstOrDefault(p => p.UserId == u.Id && p.Size == 100) 31 | .Url 32 | }) 33 | .FirstOrDefault(); 34 | ``` -------------------------------------------------------------------------------- /GraphQL.Net.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GraphQL.Net 5 | 0.3.5 6 | Chad Kimes 7 | Chad Kimes 8 | https://github.com/ckimes89/graphql-net/blob/master/LICENSE.txt 9 | https://github.com/ckimes89/graphql-net 10 | false 11 | An implementation of GraphQL for .NET and IQueryable 12 | 13 | Copyright 2016 14 | graphql graph ql iqueryable 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/02-field-arguments/02-field-arguments.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "02-field-arguments", "02-field-arguments\02-field-arguments.csproj", "{0E9D6B42-9851-4B6D-B909-856DB42BFD06}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {0E9D6B42-9851-4B6D-B909-856DB42BFD06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {0E9D6B42-9851-4B6D-B909-856DB42BFD06}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {0E9D6B42-9851-4B6D-B909-856DB42BFD06}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {0E9D6B42-9851-4B6D-B909-856DB42BFD06}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /examples/08-inline-fragments/08-inline-fragments.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "08-inline-fragments", "08-inline-fragments\08-inline-fragments.csproj", "{5F24B9CA-7E1E-4A83-B176-E60548F84367}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {5F24B9CA-7E1E-4A83-B176-E60548F84367}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {5F24B9CA-7E1E-4A83-B176-E60548F84367}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {5F24B9CA-7E1E-4A83-B176-E60548F84367}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {5F24B9CA-7E1E-4A83-B176-E60548F84367}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Chad Kimes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/WebApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net471 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /GraphQL.Parser/README.md: -------------------------------------------------------------------------------- 1 | ## Integration 2 | 3 | The files in the "Integration" folder are intended to make it easier to use this library from C# programs. 4 | 5 | ## Parsing 6 | 7 | The files in the "Parsing" folder define the language grammar for GraphQL documents. 8 | 9 | The goal here is to get an abstract syntax tree representation of the language, without concern for whether 10 | it is valid in the context of a schema. 11 | 12 | ## Schema 13 | 14 | The files in the "Schema" folder define the interfaces and types we use to represent schemas, 15 | schema-validated syntax trees, and the generic schema validation logic. 16 | 17 | We represents schemas with an interface `ISchema<'s>`, where `'s` can be any type the schema wants to 18 | extend the AST with. The same type is used to extend fields, arguments, directives, etc. so it is 19 | not completely type-safe; a schema using this extension capability will likely have to implicitly 20 | follow rules about which subtypes of 's it puts on each AST element. 21 | 22 | ## SchemaTools 23 | 24 | The files in the "SchemaTools" folder define code that, while technically not necessary for implementing a schema, 25 | is expected to be useful. This includes things like mapping CLR types to GraphQL scalar types or converting an AST to 26 | a uniform tree of selections. 27 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Models/GraphQLResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace WebApi.Models 8 | { 9 | /// 10 | /// GraphQL Response object as defined in specification. 11 | /// Specification: http://facebook.github.io/graphql/October2016/#sec-Response-Format 12 | /// 13 | public class GraphQLResponse 14 | { 15 | /// 16 | /// Data property. 17 | /// TODO: Only show if execution started. 18 | /// 19 | [JsonProperty("data")] 20 | public IDictionary Data { get; set; } 21 | 22 | /// 23 | /// Errors property. 24 | /// TODO: Must be present whenever errors were encountered during execution. 25 | /// "If the data entry in the response is null or not present, the errors entry in the response must not be empty. 26 | /// It must contain at least one error. The errors it contains should indicate why no data was able to be returned." 27 | /// 28 | [JsonProperty("errors", NullValueHandling = NullValueHandling.Ignore)] 29 | public IEnumerable Errors { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GraphQL.Parser/SourceTypes.fs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.Parser 2 | open System 3 | 4 | /// The position in the source file that a syntactic element appeared. 5 | type SourcePosition = 6 | { 7 | Index : int64 8 | Line : int64 9 | Column : int64 10 | } 11 | 12 | type ParsingException(msg, pos : SourcePosition) = 13 | inherit Exception(msg) 14 | member this.Position = pos 15 | 16 | /// The span of (start, end) positions in the source file 17 | /// that a syntactic element occupies. 18 | type SourceInfo = 19 | { 20 | StartPosition : SourcePosition 21 | EndPosition : SourcePosition 22 | } 23 | member this.ShowInSource(source : string) = 24 | // TODO: nicely format, point at the location with ^^^ or something 25 | source.Substring 26 | ( int this.StartPosition.Index 27 | , int (this.EndPosition.Index - this.StartPosition.Index) 28 | ) 29 | 30 | /// `'a` with the positions in source that it spanned. 31 | type WithSource<'a> = 32 | { 33 | /// The position in source of the syntactic element 34 | Source : SourceInfo 35 | /// The syntactic element 36 | Value : 'a 37 | } 38 | 39 | type SourceException(msg : string, pos : SourceInfo) = 40 | inherit Exception(msg) 41 | member this.SourceInfo = pos 42 | 43 | type ValidationException(msg : string) = 44 | inherit Exception(msg) 45 | -------------------------------------------------------------------------------- /GraphQL.Net/GraphQLType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace GraphQL.Net 6 | { 7 | internal class GraphQLType 8 | { 9 | public GraphQLType(Type type) 10 | { 11 | CLRType = type; 12 | Name = type.Name; 13 | OwnFields = new List(); 14 | IncludedTypes = new List(); 15 | } 16 | 17 | public string Name { get; set; } 18 | public string Description { get; set; } 19 | public List OwnFields { get; set; } 20 | public GraphQLType BaseType { get; set; } 21 | public List IncludedTypes { get; set; } 22 | public Type CLRType { get; set; } 23 | public Type QueryType { get; set; } 24 | public bool IsScalar { get; set; } // TODO: TypeKind? 25 | 26 | // Returns own fields and the fields of all included types. 27 | public IEnumerable GetQueryFields() 28 | { 29 | return OwnFields.Concat(IncludedTypes.SelectMany(t => t.GetQueryFields()).Where(f => f.Name != "__typename")); 30 | } 31 | 32 | // Returns own fields and the fields of all included types. 33 | public IEnumerable GetAllFieldIncludeBaseType() 34 | { 35 | return BaseType != null ? OwnFields.Concat(BaseType.GetAllFieldIncludeBaseType()) : OwnFields; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /GraphQL.Net/SchemaAdapters/SchemaType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GraphQL.Parser; 5 | using GraphQL.Parser.CS; 6 | 7 | namespace GraphQL.Net.SchemaAdapters 8 | { 9 | class SchemaType : SchemaQueryTypeCS 10 | { 11 | private readonly GraphQLType _type; 12 | private readonly Lazy>> _fields; 13 | 14 | internal SchemaType(GraphQLType type, Schema schema) 15 | { 16 | _type = type; 17 | _fields = new Lazy>>(() => type.GetAllFieldIncludeBaseType() 18 | .Select(f => new SchemaField(this, f, schema)) 19 | // There might be duplicates (i.e. '__typename' on types with a base type) - ignore them. 20 | .Aggregate( 21 | new Dictionary>(), 22 | (dict, field) => 23 | { 24 | dict[field.FieldName] = field; 25 | return dict; 26 | } 27 | )); 28 | } 29 | 30 | public override IReadOnlyDictionary> Fields => _fields.Value; 31 | public override string TypeName => _type.Name; 32 | public override string Description => _type.Description; 33 | public override Info Info => new Info(_type); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /GraphQL.Net/ParameterReplacer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace GraphQL.Net 4 | { 5 | public static class ParameterReplacer 6 | { 7 | /// 8 | /// Returns an expression with all occurrences of 9 | /// replaced by . 10 | /// 11 | /// Base expression 12 | /// Parameter to be replaced 13 | /// Expression to replace the parameter with 14 | /// 15 | public static Expression Replace (Expression expression, ParameterExpression toReplace, Expression replaceWith) 16 | => new ParameterReplacerVisitor(toReplace, replaceWith).Visit(expression); 17 | 18 | private class ParameterReplacerVisitor : ExpressionVisitor 19 | { 20 | private readonly ParameterExpression _source; 21 | private readonly Expression _target; 22 | 23 | public ParameterReplacerVisitor (ParameterExpression source, Expression target) 24 | { 25 | _source = source; 26 | _target = target; 27 | } 28 | 29 | protected override Expression VisitParameter(ParameterExpression node) 30 | { 31 | return node == _source ? _target : base.VisitParameter(node); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /GraphQL.Net/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("GraphQL.Net")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("GraphQL.Net")] 12 | [assembly: AssemblyCopyright("Copyright © 2015")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("530742a1-10d8-4255-837d-43d57b00ee50")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /docs/queries_and_mutations/aliases.md: -------------------------------------------------------------------------------- 1 | # Aliases 2 | 3 | Result objects can be renamed using aliases: 4 | 5 | ```csharp 6 | { 7 | empireHero: hero(episode: EMPIRE) { 8 | name 9 | } 10 | jediHero: hero(episode: JEDI) { 11 | name 12 | } 13 | } 14 | ``` 15 | Result: 16 | ```json 17 | { 18 | "data": { 19 | "empireHero": { 20 | "name": "Luke Skywalker" 21 | }, 22 | "jediHero": { 23 | "name": "R2-D2" 24 | } 25 | } 26 | } 27 | ``` 28 | 29 | This can be implemented like this: 30 | 31 | ```csharp 32 | class Context 33 | { 34 | public IList Heros { get; set; } 35 | } 36 | 37 | class Hero 38 | { 39 | public string Id { get; set; } 40 | public string Name { get; set; } 41 | public string Episode { get; set; } 42 | } 43 | 44 | ... 45 | 46 | var schema = GraphQL.CreateDefaultSchema(() => 47 | new Context 48 | { 49 | Heros = new List { 50 | new Hero { Id = "1000", Name = "Luke Skywalker", Episode = "EMPIRE" }, 51 | new Hero { Id = "1001", Name = "R2-D2", Episode = "JEDI" } 52 | } 53 | }); 54 | schema.AddType().AddAllFields(); 55 | schema.AddField( 56 | "hero", 57 | new { episode = "EMPIRE" }, 58 | (c, args) => c.Heros.SingleOrDefault(h => h.Episode == args.episode)); 59 | 60 | schema.Complete(); 61 | 62 | var gql = new GraphQL(schema); 63 | var queryResult = gql.ExecuteQuery("{empireHero: hero(episode: \"EMPIRE\") {name}, jediHero: hero(episode: \"JEDI\") {name}}"); 64 | ``` 65 | 66 | See `examples/03-aliases`. -------------------------------------------------------------------------------- /Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5d1249e3-cb58-4323-9d1b-9bb2abefd6e6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Services/GraphQL.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Net; 2 | using System.Linq; 3 | using WebApi.Data; 4 | using WebApi.Models; 5 | 6 | namespace WebApi.Services 7 | { 8 | public interface IGraphQL 9 | { 10 | GraphQL Current { get; } 11 | } 12 | 13 | public class GraphQL : IGraphQL 14 | { 15 | private readonly IMyDocumentClient documentClient; 16 | 17 | public GraphQL Current { get; private set; } 18 | 19 | public GraphQL(IMyDocumentClient documentClient) 20 | { 21 | this.documentClient = documentClient; 22 | 23 | // Build schema 24 | var schema = GraphQL.CreateDefaultSchema(() => new GraphQLContext(documentClient)); 25 | 26 | // schema.AddType().AddAllFields(); 27 | schema.AddType().AddAllFields(); 28 | schema.AddListField("users", db => db.Users); 29 | schema.AddField("user", new { id = "" }, (db, args) => db.Users.Where(u => u.Id == args.id).ToArray().FirstOrDefault()); 30 | 31 | schema.AddType().AddAllFields(); 32 | schema.AddListField("accounts", db => db.Accounts); 33 | schema.AddField("account", new { id = "" }, (db, args) => db.Accounts.Where(a => a.Id == args.id).ToArray().FirstOrDefault()); 34 | 35 | schema.Complete(); 36 | 37 | // Initialise singleton GraphQL instance 38 | this.Current = new GraphQL(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests.EF/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Tests.EF")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Tests.EF")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c7f0c31c-202e-4185-9083-4d0485cea861")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/03-aliases/03-aliases/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("03-aliases")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("03-aliases")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c59412af-5e2f-4470-843c-1b6a305334a8")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/04-fragments/04-fragments/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("04-fragments")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("04-fragments")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5725c8bd-3397-4fe7-a860-c484d71b11c9")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/07-mutations/07-mutations/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("07-mutations")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("07-mutations")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4d86f2bd-476f-4e5e-a268-50e65076f773")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/01-simple-query/01-simple-query/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("01-simple-query")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("01-simple-query")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f8a6bd38-a5c7-4d5a-9841-ba012e7c3b83")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/02-field-arguments/02-field-arguments/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("02-field-arguments")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("02-field-arguments")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0e9d6b42-9851-4b6d-b909-856db42bfd06")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/08-inline-fragments/08-inline-fragments/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("08-inline-fragments")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("08-inline-fragments")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5f24b9ca-7e1e-4a83-b176-e60548f84367")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /GraphQL.Parser.Test/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace GraphQL.Parser.Test.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | [] 17 | [] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | [] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [] 37 | [] 38 | [] 39 | 40 | do 41 | () -------------------------------------------------------------------------------- /examples/09-cosmos-db/09-cosmos-db.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2024 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi", "WebApi\WebApi.csproj", "{D47F8041-1C58-4788-9315-CE64E02402AA}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi.Tests", "WebApi.Tests\WebApi.Tests.csproj", "{E03A7C2A-5AD3-44E1-BE92-5C899E552E0A}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {D47F8041-1C58-4788-9315-CE64E02402AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {D47F8041-1C58-4788-9315-CE64E02402AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {D47F8041-1C58-4788-9315-CE64E02402AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {D47F8041-1C58-4788-9315-CE64E02402AA}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {E03A7C2A-5AD3-44E1-BE92-5C899E552E0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E03A7C2A-5AD3-44E1-BE92-5C899E552E0A}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E03A7C2A-5AD3-44E1-BE92-5C899E552E0A}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E03A7C2A-5AD3-44E1-BE92-5C899E552E0A}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {05005872-6FDB-4164-959F-E69CB6E51281} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /GraphQL.Parser/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Parser.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | [] 17 | [] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | [] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [] 37 | [] 38 | [] 39 | #if !DEBUG 40 | [] 41 | #endif 42 | 43 | do 44 | () -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | using Microsoft.Extensions.Options; 11 | using WebApi.Services; 12 | 13 | namespace WebApi 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | services.AddSingleton(Configuration); 28 | 29 | services.AddWebApi(); 30 | 31 | services.AddSingleton(); 32 | services.AddSingleton(); 33 | 34 | services.AddSingleton(); 35 | } 36 | 37 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 38 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMyDocumentClientInitializer docClientIniter) 39 | { 40 | if (env.IsDevelopment()) 41 | { 42 | app.UseDeveloperExceptionPage(); 43 | Task.WaitAll(docClientIniter.Reset()); 44 | } 45 | 46 | app.UseMvc(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /GraphQL.Net/SchemaAdapters/Schema.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using GraphQL.Parser; 6 | using GraphQL.Parser.CS; 7 | 8 | namespace GraphQL.Net.SchemaAdapters 9 | { 10 | abstract class Schema : SchemaCS 11 | { 12 | internal readonly GraphQLSchema GraphQLSchema; 13 | protected Schema(GraphQLSchema schema) 14 | { 15 | GraphQLSchema = schema; 16 | } 17 | 18 | private readonly Dictionary _typeMap = new Dictionary(); 19 | 20 | public SchemaType OfType(GraphQLType type) 21 | { 22 | SchemaType existing; 23 | if (_typeMap.TryGetValue(type, out existing)) return existing; 24 | return _typeMap[type] = new SchemaType(type, this); 25 | } 26 | } 27 | class Schema : Schema 28 | { 29 | private readonly GraphQLSchema _schema; 30 | private readonly Dictionary> _queryTypes; 31 | 32 | public Schema(GraphQLSchema schema) : base(schema) 33 | { 34 | RootType = new SchemaRootType(this, schema.GetGQLType(typeof(TContext))); 35 | _schema = schema; 36 | _queryTypes = schema.Types 37 | .Where(t => !t.IsScalar) 38 | .Select(OfType) 39 | .ToDictionary(t => t.TypeName, t => t as ISchemaQueryType); 40 | } 41 | 42 | public override IReadOnlyDictionary> QueryTypes 43 | => _queryTypes; 44 | 45 | public override IReadOnlyDictionary VariableTypes 46 | => _schema.VariableTypes.TypeDictionary; 47 | 48 | public override ISchemaQueryType RootType { get; } 49 | 50 | public override EnumValue ResolveEnumValue(string name) 51 | { 52 | return _schema.VariableTypes.ResolveEnumValue(name); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Tests.EF/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi.Tests/BaseTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.AspNetCore.TestHost; 3 | using Microsoft.Extensions.Configuration; 4 | using Newtonsoft.Json.Linq; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net.Http; 9 | using System.Threading.Tasks; 10 | using WebApi.Models; 11 | 12 | namespace WebApi.Tests 13 | { 14 | public class BaseTests 15 | { 16 | private readonly TestServer _server; 17 | private readonly HttpClient _client; 18 | 19 | public BaseTests() 20 | { 21 | // Arrange 22 | var configuration = new ConfigurationBuilder() 23 | .SetBasePath(Path.GetFullPath(@"../../../../WebApi/")) 24 | .AddJsonFile("appsettings.Development.json", optional: false) 25 | .Build(); 26 | 27 | _server = new TestServer(new WebHostBuilder() 28 | .UseStartup() 29 | .UseConfiguration(configuration) 30 | ); 31 | 32 | _client = _server.CreateClient(); 33 | } 34 | 35 | protected async Task Get(string query) 36 | { 37 | var request = "/graphql"; 38 | if (!string.IsNullOrEmpty(query)) 39 | { 40 | request += "?query=" + query; 41 | } 42 | var response = await _client.GetAsync(request); 43 | response.EnsureSuccessStatusCode(); 44 | 45 | return await response.Content.ReadAsStringAsync(); 46 | } 47 | 48 | protected async Task> GetUsers(string query) 49 | { 50 | var resp = await Get(query); 51 | var users = JObject.Parse(resp)["data"]["users"].Children().Select(j => j.ToObject()); 52 | return users; 53 | } 54 | 55 | protected async Task GetUser(string query) 56 | { 57 | var resp = await Get(query); 58 | var user = JObject.Parse(resp)["data"]["user"].ToObject(); 59 | return user; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /GraphQL.Parser/Integration/GraphQLDocument.fs: -------------------------------------------------------------------------------- 1 | //MIT License 2 | // 3 | //Copyright (c) 2016 Robert Peele 4 | // 5 | //Permission is hereby granted, free of charge, to any person obtaining a copy 6 | //of this software and associated documentation files (the "Software"), to deal 7 | //in the Software without restriction, including without limitation the rights 8 | //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | //copies of the Software, and to permit persons to whom the Software is 10 | //furnished to do so, subject to the following conditions: 11 | // 12 | //The above copyright notice and this permission notice shall be included in all 13 | //copies or substantial portions of the Software. 14 | // 15 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | //SOFTWARE. 22 | 23 | namespace GraphQL.Parser 24 | open GraphQL.Parser 25 | open GraphQL.Parser.SchemaResolver 26 | open System.Collections.Generic 27 | 28 | type GraphQLParserDocument(source : string, document : ParserAST.Document) = 29 | member __.Source = source 30 | member __.AST = document 31 | static member Parse(source) = 32 | let document = Parser.parseDocument source 33 | new GraphQLParserDocument(source, document) 34 | 35 | type GraphQLDocument<'s>(schema : ISchema<'s>, source : string, operations : Operation<'s> ListWithSource) = 36 | member __.Schema = schema 37 | member __.Source = source 38 | member __.Operations = operations 39 | static member Parse(schema, source) = 40 | let doc = GraphQLParserDocument.Parse(source) 41 | let context = new DocumentContext<'s>(schema, doc.AST) 42 | let ops = context.ResolveOperations() 43 | new GraphQLDocument<'s>(schema, source, ops) 44 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi.Tests/QueryTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Xunit; 3 | using Seeder = WebApi.Services.MyDocumentClientInitializer; 4 | 5 | namespace WebApi.Tests 6 | { 7 | public class QueryTests : BaseTests 8 | { 9 | [Fact] 10 | public async void Users_With_IdAndProfile() 11 | { 12 | // Act 13 | var actual = await GetUsers(@"{ users { id, profile } }"); 14 | 15 | // Assert 16 | actual.Should().BeEquivalentTo(Seeder.Users, options => options 17 | .Including(u => u.Id) 18 | .Including(u => u.Profile) 19 | ); 20 | } 21 | 22 | [Fact] 23 | public async void Users_With_Id() 24 | { 25 | // Act 26 | var actual = await GetUsers(@"{ users { id } }"); 27 | 28 | // Assert 29 | actual.Should().BeEquivalentTo(Seeder.Users, options => options.Including(u => u.Id)); 30 | } 31 | 32 | [Fact] 33 | public async void Users_With_Profile() 34 | { 35 | // Act 36 | var users = await GetUsers(@"{ users { profile } }"); 37 | 38 | // Assert 39 | users.Should().BeEquivalentTo(Seeder.Users, options => options.Including(u => u.Profile)); 40 | } 41 | 42 | [Fact] 43 | public async void Users_With_IdAndProfileAge() 44 | { 45 | // Act 46 | var users = await GetUsers(@"{ users { id, profile { age } } }"); 47 | 48 | // Assert 49 | users.Should().BeEquivalentTo(Seeder.Users, options => options 50 | .Including(u => u.Id) 51 | .Including(u => u.Profile.Age) 52 | ); 53 | } 54 | 55 | [Fact] 56 | public async void User_With_Id() 57 | { 58 | // Act 59 | var user = await GetUser("{ user(id: \"" + Seeder.User1.Id + "\") { id } }"); 60 | 61 | // Assert 62 | user.Should().BeEquivalentTo(Seeder.User1, options => options 63 | .Including(u => u.Id) 64 | ); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /GraphQL.Net/SchemaAdapters/SchemaField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GraphQL.Parser; 5 | using GraphQL.Parser.CS; 6 | 7 | namespace GraphQL.Net.SchemaAdapters 8 | { 9 | class SchemaField : SchemaFieldCS 10 | { 11 | private readonly Schema _schema; 12 | private readonly GraphQLField _field; 13 | 14 | public SchemaField(ISchemaQueryType declaringType, GraphQLField field, Schema schema) 15 | { 16 | DeclaringType = declaringType; 17 | _field = field; 18 | _schema = schema; 19 | if (_field.Type.IsScalar) 20 | { 21 | var varType = _schema.GraphQLSchema.VariableTypes.VariableTypeOf(_field.Type.CLRType); 22 | FieldType = SchemaFieldType.NewValueField(varType); 23 | } 24 | else 25 | { 26 | FieldType = SchemaFieldType.NewQueryField(_schema.OfType(_field.Type));; 27 | } 28 | Arguments = _field.Arguments.ToDictionary(a => a.ArgumentName); 29 | } 30 | 31 | public override ISchemaQueryType DeclaringType { get; } 32 | public override SchemaFieldType FieldType { get; } 33 | 34 | public override string FieldName => _field.Name; 35 | public override string Description => _field.Description; 36 | public override Info Info => new Info(_field); 37 | public override IReadOnlyDictionary> Arguments { get; } 38 | public override Complexity EstimateComplexity(IEnumerable> args) 39 | { 40 | if (_field.Type.IsScalar) return Complexity.Zero; // scalars are practically free to select 41 | if (!_field.IsList) return Complexity.One; 42 | return _field.Complexity ?? (args.Any(a => a.ArgumentName.Equals("id", StringComparison.OrdinalIgnoreCase)) 43 | ? Complexity.One 44 | // wild guess: we can have 0 to 200 related entities 45 | // can we let the underlying schema provide more accurate info here? 46 | : Complexity.Of(0, 200)); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /examples/03-aliases/03-aliases/AliasesExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Net; 3 | using NUnit.Framework; 4 | using System.Collections.Generic; 5 | using Newtonsoft.Json.Linq; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Converters; 8 | using System.Linq; 9 | 10 | namespace _03_aliases 11 | { 12 | [TestFixture] 13 | public class AliasesExample 14 | { 15 | class Context 16 | { 17 | public IList Heros { get; set; } 18 | } 19 | 20 | class Hero 21 | { 22 | public string Id { get; set; } 23 | public string Name { get; set; } 24 | public string Episode { get; set; } 25 | } 26 | 27 | [Test] 28 | public void RunExample() 29 | { 30 | var schema = GraphQL.CreateDefaultSchema(() => 31 | new Context 32 | { 33 | Heros = new List { 34 | new Hero { Id = "1000", Name = "Luke Skywalker", Episode = "EMPIRE" }, 35 | new Hero { Id = "1001", Name = "R2-D2", Episode = "JEDI" } 36 | } 37 | }); 38 | schema.AddType().AddAllFields(); 39 | schema.AddField( 40 | "hero", 41 | new { episode = "EMPIRE" }, 42 | (c, args) => c.Heros.SingleOrDefault(h => h.Episode == args.episode)); 43 | 44 | schema.Complete(); 45 | 46 | var gql = new GraphQL(schema); 47 | var queryResult = gql.ExecuteQuery("{empireHero: hero(episode: \"EMPIRE\") {name}, jediHero: hero(episode: \"JEDI\") {name}}"); 48 | DeepEquals(queryResult, "{empireHero: {name: 'Luke Skywalker'}, jediHero: {name: 'R2-D2'}}"); 49 | } 50 | 51 | private static readonly JsonSerializer Serializer = new JsonSerializer 52 | { 53 | Converters = { new StringEnumConverter() }, 54 | }; 55 | 56 | private static void DeepEquals(IDictionary results, string json) 57 | { 58 | var expected = JObject.Parse(json); 59 | var actual = JObject.FromObject(results, Serializer); 60 | if (expected.ToString() == actual.ToString()) 61 | return; 62 | 63 | throw new Exception($"Results do not match expectation:\n\nExpected:\n{expected}\n\nActual:\n{actual}"); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Ms/Extensions/DependencyInjection/WebApiServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.Internal; 4 | 5 | // ReSharper disable once CheckNamespace 6 | namespace Microsoft.Extensions.DependencyInjection 7 | { 8 | public static class WebApiServiceCollectionExtensions 9 | { 10 | /// 11 | /// Adds MVC services to the specified for Web API. 12 | /// This is a slimmed down version of 13 | /// 14 | /// The to add services to. 15 | /// An that can be used to further configure the MVC services. 16 | public static IMvcBuilder AddWebApi(this IServiceCollection services) 17 | { 18 | if (services == null) throw new ArgumentNullException(nameof(services)); 19 | 20 | var builder = services.AddMvcCore(); 21 | 22 | builder.AddApiExplorer(); 23 | builder.AddAuthorization(); 24 | builder.AddFormatterMappings(); 25 | // builder.AddDataAnnotations(); 26 | builder.AddJsonFormatters(); 27 | builder.AddCors(); 28 | return new MvcBuilder(builder.Services, builder.PartManager); 29 | } 30 | 31 | /// 32 | /// Adds MVC services to the specified for Web API. 33 | /// This is a slimmed down version of 34 | /// 35 | /// The to add services to. 36 | /// An to configure the provided . 37 | /// An that can be used to further configure the MVC services. 38 | public static IMvcBuilder AddWebApi(this IServiceCollection services, Action setupAction) 39 | { 40 | if (services == null) throw new ArgumentNullException(nameof(services)); 41 | if (setupAction == null) throw new ArgumentNullException(nameof(setupAction)); 42 | 43 | var builder = services.AddWebApi(); 44 | builder.Services.Configure(setupAction); 45 | 46 | return builder; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/basics/build_a_schema.md: -------------------------------------------------------------------------------- 1 | # Build a Schema 2 | 3 | Let's assume we have an Entity Framework DbContext that looks like this: 4 | 5 | ```csharp 6 | public class TestContext : DbContext 7 | { 8 | public IDbSet Users { get; set; } 9 | public IDbSet Accounts { get; set; } 10 | } 11 | 12 | public class User 13 | { 14 | public int Id { get; set; } 15 | public string Name { get; set; } 16 | 17 | public int AccountId { get; set; } 18 | public Account Account { get; set; } 19 | } 20 | 21 | public class Account 22 | { 23 | public int Id { get; set; } 24 | public string Name { get; set; } 25 | public bool Paid { get; set; } 26 | } 27 | ``` 28 | 29 | First, we create and set the default schema by providing a function that creates our context: 30 | 31 | ```csharp 32 | var schema = GraphQL.CreateDefaultSchema(() => new TestContext()); 33 | ``` 34 | 35 | The default schema is required to use the helper method`GraphQL.Execute(query)`, but you can execute queries against the schema without it. Next, we'll define a type in the schema and fields on that type. 36 | 37 | ```csharp 38 | var user = schema.AddType(); 39 | user.AddField(u => u.Id); 40 | user.AddField(u => u.Name); 41 | user.AddField(u => u.Account); 42 | user.AddField("totalUsers", (db, u) => db.Users.Count()); 43 | user.AddField("accountPaid", (db, u) => u.Account.Paid); 44 | ``` 45 | 46 | Fields can be defined using only a property expression, or you can specify your own fields and provide a custom resolving expression. Let's do the same for account: 47 | 48 | ```csharp 49 | schema.AddType().AddAllFields(); 50 | ``` 51 | 52 | If we just want to expose all fields, we can use the `AddAllFields` helper method. 53 | 54 | The last thing we want to do is create some queries, as fields on the schema itself. Let's add some to find users: 55 | 56 | ```csharp 57 | schema.AddListField("users", db => db.Users); 58 | schema.AddField("user", new { id = 0 }, (db, args) => db.Users.Where(u => u.Id == args.id).FirstOrDefault()); 59 | ``` 60 | 61 | In our first query, we want to see all users so we can just return the entire list. However, notice how in the second query we define the shape of an anonymous type `new { id = 0 }`. This is what is expected to be passed in from the GraphQL query. Since we've defined the shape, we can now use that in the `Where` clause to build our IQueryable. We use `FirstOrDefault` to signify that this query will return a single result. 62 | 63 | ```csharp 64 | schema.Complete(); 65 | ``` 66 | 67 | Finally, we complete the schema when we've finished setting up. Now we're ready to execute a query. -------------------------------------------------------------------------------- /docs/queries_and_mutations/arguments.md: -------------------------------------------------------------------------------- 1 | # Arguments 2 | 3 | Grapqhl-net supports field arguments. 4 | 5 | The query: 6 | ```csharp 7 | { 8 | human(id: "1000") { 9 | name 10 | height 11 | } 12 | } 13 | ``` 14 | Should return: 15 | ```json 16 | { 17 | "data": { 18 | "human": { 19 | "name": "Luke Skywalker", 20 | "height": 1.72 21 | } 22 | } 23 | } 24 | ``` 25 | 26 | This can be implemented like this: 27 | 28 | ```csharp 29 | class Context 30 | { 31 | public IList Humans { get; set; } 32 | } 33 | 34 | class Human 35 | { 36 | public string Id { get; set; } 37 | public string Name { get; set; } 38 | public double Height { get; set; } 39 | } 40 | 41 | ... 42 | 43 | var schema = GraphQL.CreateDefaultSchema(() => 44 | new Context 45 | { 46 | Humans = new List { 47 | new Human { Id = "1000", Name = "Luke Skywalker", Height = 1.72 } 48 | } 49 | }); 50 | schema.AddType().AddAllFields(); 51 | schema.AddField( 52 | "human", 53 | new { id = "-1" }, 54 | (c, args) => c.Humans.SingleOrDefault(h => h.Id == args.id)); 55 | 56 | schema.Complete(); 57 | 58 | var gql = new GraphQL(schema); 59 | var queryResult = gql.ExecuteQuery("{human(id: \"1000\") {name, height}}"); 60 | ``` 61 | Arguments can be specified on scalar fields as well: 62 | ```csharp 63 | { 64 | human(id: "1000") { 65 | name 66 | height(unit: FOOT) 67 | } 68 | } 69 | ``` 70 | The result should look like this: 71 | ```json 72 | { 73 | "data": { 74 | "human": { 75 | "name": "Luke Skywalker", 76 | "height": 5.6430448 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | This can be implemented like this: 83 | 84 | ```csharp 85 | var schema = GraphQL.CreateDefaultSchema(() => 86 | new Context 87 | { 88 | Humans = new List { 89 | new Human { Id = "1000", Name = "Luke Skywalker", Height = 1.72 } 90 | } 91 | }); 92 | var humanSchema = schema.AddType(); 93 | humanSchema.AddField(h => h.Id); 94 | humanSchema.AddField(h => h.Name); 95 | humanSchema.AddField( 96 | "height", 97 | new { unit = "METER"}, 98 | (c, args, h) => args.unit == "FOOT" ? h.Height * 3.28084 : h.Height); 99 | 100 | schema.AddField( 101 | "human", 102 | new { id = "-1" }, 103 | (c, args) => c.Humans.SingleOrDefault(h => h.Id == args.id)); 104 | 105 | schema.Complete(); 106 | 107 | var gql = new GraphQL(schema); 108 | var queryResult = gql.ExecuteQuery("{human(id: \"1000\") {name, height(unit: \"FOOT\")}}"); 109 | ``` 110 | 111 | See `examples/02-field-argumens`. -------------------------------------------------------------------------------- /GraphQL.Net/VariableTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GraphQL.Parser; 5 | 6 | namespace GraphQL.Net 7 | { 8 | public class VariableTypes 9 | { 10 | private readonly List> _customHandlers = 11 | new List>(); 12 | private RootTypeHandler _rootTypeHandler; 13 | 14 | private ITypeHandler TypeHandler => _rootTypeHandler; 15 | 16 | public void AddType(Func customHandler) 17 | { 18 | if (_rootTypeHandler != null) throw new Exception("Can't add types after completing."); 19 | _customHandlers.Add(customHandler); 20 | } 21 | 22 | private class MetaTypeHandler : IMetaTypeHandler 23 | { 24 | private readonly VariableTypes _variableTypes; 25 | 26 | public MetaTypeHandler(VariableTypes variableTypes) 27 | { 28 | _variableTypes = variableTypes; 29 | } 30 | 31 | 32 | public IEnumerable Handlers(ITypeHandler rootHandler) 33 | => _variableTypes._customHandlers.Select(h => h(rootHandler)); 34 | } 35 | 36 | public void Complete() 37 | { 38 | if (_rootTypeHandler != null) throw new Exception("Variable types already complete."); 39 | _rootTypeHandler = new RootTypeHandler(new MetaTypeHandler(this)); 40 | } 41 | 42 | public IReadOnlyDictionary TypeDictionary => _rootTypeHandler.TypeDictionary; 43 | 44 | public EnumValue ResolveEnumValue(string name) 45 | { 46 | return _rootTypeHandler.ResolveEnumValueByName(name).Value; 47 | } 48 | 49 | /// 50 | /// Return the schema variable type used to represent values of type . 51 | /// 52 | /// 53 | /// 54 | public VariableType VariableTypeOf(Type clrType) 55 | => TypeHandler.GetMapping(clrType)?.Value.VariableType; 56 | 57 | /// 58 | /// Get a CLR object of type from the value . 59 | /// 60 | /// 61 | /// 62 | /// 63 | public object TranslateValue(Value graphQLValue, Type desiredCLRType) 64 | => TypeHandler.GetMapping(desiredCLRType)?.Value.Translate.Invoke(graphQLValue); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /docs/queries_and_mutations/fields.md: -------------------------------------------------------------------------------- 1 | # Fields 2 | Let's look at the following query: 3 | 4 | ``` 5 | { 6 | hero { 7 | name 8 | # Queries can have comments! 9 | friends { 10 | name 11 | } 12 | } 13 | } 14 | ``` 15 | The expected result looks as follows: 16 | ```json 17 | { 18 | "data": { 19 | "hero": { 20 | "name": "R2-D2", 21 | "friends": [ 22 | { 23 | "name": "Luke Skywalker" 24 | }, 25 | { 26 | "name": "Han Solo" 27 | }, 28 | { 29 | "name": "Leia Organa" 30 | } 31 | ] 32 | } 33 | } 34 | } 35 | ``` 36 | 37 | Assuming we have the following data models: 38 | ```csharp 39 | class Context 40 | { 41 | public Character Hero { get; set; } 42 | } 43 | 44 | class Character 45 | { 46 | public string Name { get; set; } 47 | public IEnumerable Friends { get; set; } 48 | } 49 | ``` 50 | ... and the following data: 51 | ```csharp 52 | var context = new Context 53 | { 54 | Hero = new Character 55 | { 56 | Name = "R2-D2", 57 | Friends = new List 58 | { 59 | new Character { 60 | Name = "Luke Skywalker" 61 | }, 62 | new Character { 63 | Name = "Han Solo" 64 | }, 65 | new Character { 66 | Name = "Leia Organa" 67 | } 68 | } 69 | } 70 | }; 71 | ``` 72 | 73 | The GraphQL.Net schema definition could look like this: 74 | 75 | ```csharp 76 | var schema = GraphQL.CreateDefaultSchema(() => context); 77 | schema.AddType().AddAllFields(); 78 | schema.AddField("hero", c => c.Hero); 79 | 80 | schema.Complete(); 81 | 82 | var gql = new GraphQL(schema); 83 | ``` 84 | 85 | Let's run the query: 86 | ```csharp 87 | var queryResult = gql.ExecuteQuery( 88 | @"{ 89 | hero { 90 | name, 91 | friends { 92 | name 93 | } 94 | } 95 | }" 96 | ); 97 | ``` 98 | 99 | See `examples/01-simple-query/` for the code. 100 | 101 | 102 | > **Note on list fields and `AddAllFields`** 103 | > 104 | > `AddAllFields` does not support list fields of type array, they have to be of type `IEnumerable`. 105 | > If we define the property `Character.Friends` as: 106 | > ```csharp 107 | > public Character[] Friends { get; set; } 108 | > ``` 109 | > We will get the following Exception on execution `gql.ExecuteQuery`: 110 | > ``` 111 | > GraphQL.Parser.ValidationException : Unsupported CLR type ``Character'' 112 | > ``` -------------------------------------------------------------------------------- /GraphQL.Net/GraphQL.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GraphQL.Net.SchemaAdapters; 5 | using GraphQL.Parser; 6 | using GraphQL.Parser.Execution; 7 | using Microsoft.FSharp.Core; 8 | 9 | namespace GraphQL.Net 10 | { 11 | public class GraphQL 12 | { 13 | public static GraphQLSchema Schema; 14 | 15 | private readonly GraphQLSchema _schema; 16 | public GraphQL(GraphQLSchema schema = null) 17 | { 18 | _schema = schema ?? Schema; 19 | } 20 | 21 | public static GraphQLSchema CreateDefaultSchema(Func creationFunc) 22 | { 23 | return Schema = new GraphQLSchema(creationFunc); 24 | } 25 | 26 | public static IDictionary Execute(string query) 27 | { 28 | var gql = new GraphQL(); 29 | return gql.ExecuteQuery(query); 30 | } 31 | 32 | public IDictionary ExecuteQuery(string queryStr) 33 | { 34 | if (_schema.ContextCreator == null) 35 | throw new InvalidOperationException("No context creator specified. Either pass a context " + 36 | "creator to the schema's constroctur or call overloaded method 'Execut(string query, TContext context)' " + 37 | "and pass a context."); 38 | var context = _schema.ContextCreator(); 39 | var result = ExecuteQuery(queryStr, context); 40 | (context as IDisposable)?.Dispose(); 41 | return result; 42 | } 43 | 44 | public IDictionary ExecuteQuery(string queryStr, TContext queryContext) 45 | { 46 | if (!_schema.Completed) 47 | throw new InvalidOperationException("Schema must be Completed before executing a query. Try calling the schema's Complete method."); 48 | 49 | if (queryContext == null) 50 | throw new ArgumentException("Context must not be null."); 51 | 52 | var document = GraphQLDocument.Parse(_schema.Adapter, queryStr); 53 | var context = DefaultExecContext.Instance; // TODO use a real IExecContext to support passing variables 54 | var operation = document.Operations.Single(); // TODO support multiple operations per document, look up by name 55 | var execSelections = context.ToExecSelections(operation.Value); 56 | 57 | var outputs = new Dictionary(); 58 | foreach (var execSelection in execSelections.Select(s => s.Value)) 59 | { 60 | var field = execSelection.SchemaField.Field(); 61 | outputs[execSelection.Name] = Executor.Execute(_schema, queryContext, field, execSelection); 62 | } 63 | return outputs; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /GraphQL.Net.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphQL.Net", "GraphQL.Net\GraphQL.Net.csproj", "{530742A1-10D8-4255-837D-43D57B00EE50}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GraphQL.Parser", "GraphQL.Parser\GraphQL.Parser.fsproj", "{D5F65A61-8C03-46F1-86AA-7B6DA4BE25F5}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{201D521E-FAE4-4EC9-A5FE-EAA4C6F53CE1}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{5D1249E3-CB58-4323-9D1B-9BB2ABEFD6E6}" 13 | EndProject 14 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GraphQL.Parser.Test", "GraphQL.Parser.Test\GraphQL.Parser.Test.fsproj", "{75CDF08F-DBCF-4C68-B713-377776337BBF}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.EF", "Tests.EF\Tests.EF.csproj", "{C7F0C31C-202E-4185-9083-4D0485CEA861}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {530742A1-10D8-4255-837D-43D57B00EE50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {530742A1-10D8-4255-837D-43D57B00EE50}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {530742A1-10D8-4255-837D-43D57B00EE50}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {530742A1-10D8-4255-837D-43D57B00EE50}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {D5F65A61-8C03-46F1-86AA-7B6DA4BE25F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {D5F65A61-8C03-46F1-86AA-7B6DA4BE25F5}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {D5F65A61-8C03-46F1-86AA-7B6DA4BE25F5}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {D5F65A61-8C03-46F1-86AA-7B6DA4BE25F5}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {5D1249E3-CB58-4323-9D1B-9BB2ABEFD6E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {5D1249E3-CB58-4323-9D1B-9BB2ABEFD6E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {5D1249E3-CB58-4323-9D1B-9BB2ABEFD6E6}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {5D1249E3-CB58-4323-9D1B-9BB2ABEFD6E6}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {75CDF08F-DBCF-4C68-B713-377776337BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {75CDF08F-DBCF-4C68-B713-377776337BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {75CDF08F-DBCF-4C68-B713-377776337BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {75CDF08F-DBCF-4C68-B713-377776337BBF}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {C7F0C31C-202E-4185-9083-4D0485CEA861}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {C7F0C31C-202E-4185-9083-4D0485CEA861}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {C7F0C31C-202E-4185-9083-4D0485CEA861}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {C7F0C31C-202E-4185-9083-4D0485CEA861}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /GraphQL.Net/DynamicTypeBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | 6 | namespace GraphQL.Net 7 | { 8 | // Currently unused - keeping it around since we'll at some point need to dynamically create types that have more than 20 fields 9 | internal static class DynamicTypeBuilder 10 | { 11 | private static readonly ModuleBuilder ModuleBuilder; 12 | const string AssemblyName = "GraphQL.DynamicObjects"; 13 | 14 | static DynamicTypeBuilder() 15 | { 16 | var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(AssemblyName), AssemblyBuilderAccess.RunAndSave); 17 | ModuleBuilder = assemblyBuilder.DefineDynamicModule(AssemblyName + ".dll"); 18 | } 19 | 20 | public static Type CreateDynamicType(string name, Dictionary properties) 21 | { 22 | var typeBuilder = ModuleBuilder.DefineType(AssemblyName + "." + name, 23 | TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Serializable | TypeAttributes.BeforeFieldInit); 24 | foreach (var prop in properties) 25 | CreateProperty(typeBuilder, prop.Key, prop.Value); 26 | return typeBuilder.CreateType(); 27 | } 28 | 29 | private static void CreateProperty(TypeBuilder typeBuilder, string name, Type type) 30 | { 31 | var fieldBuilder = typeBuilder.DefineField("_" + name.ToLower(), type, FieldAttributes.Private); 32 | var propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.HasDefault, type, null); 33 | 34 | propertyBuilder.SetGetMethod(CreateGetMethod(typeBuilder, fieldBuilder, name, type)); 35 | propertyBuilder.SetSetMethod(CreateSetMethod(typeBuilder, fieldBuilder, name, type)); 36 | } 37 | 38 | const MethodAttributes MethodAttrs = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; 39 | private static MethodBuilder CreateGetMethod(TypeBuilder typeBuilder, FieldInfo fieldBuilder, string name, Type type) 40 | { 41 | var methodBuilder = typeBuilder.DefineMethod("get_" + name, MethodAttrs, type, Type.EmptyTypes); 42 | var generator = methodBuilder.GetILGenerator(); 43 | generator.Emit(OpCodes.Ldarg_0); 44 | generator.Emit(OpCodes.Ldfld, fieldBuilder); 45 | generator.Emit(OpCodes.Ret); 46 | 47 | return methodBuilder; 48 | } 49 | 50 | private static MethodBuilder CreateSetMethod(TypeBuilder typeBuilder, FieldInfo fieldBuilder, string name, Type type) 51 | { 52 | var methodBuilder = typeBuilder.DefineMethod("set" + name, MethodAttrs, null, new[] { type }); 53 | var generator = methodBuilder.GetILGenerator(); 54 | generator.Emit(OpCodes.Ldarg_0); 55 | generator.Emit(OpCodes.Ldarg_1); 56 | generator.Emit(OpCodes.Stfld, fieldBuilder); 57 | generator.Emit(OpCodes.Ret); 58 | 59 | return methodBuilder; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Controllers/GraphQLController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System.IO; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using WebApi.Models; 6 | using WebApi.Services; 7 | 8 | namespace WebApi.Controllers 9 | { 10 | /// 11 | /// GraphQL api endpoint as per specification. 12 | /// Specification: http://graphql.org/learn/serving-over-http/ 13 | /// 14 | [Route("[controller]")] 15 | [Produces("application/json")] 16 | public class GraphQLController : ControllerBase 17 | { 18 | private readonly IGraphQL gql; 19 | 20 | public GraphQLController(IGraphQL gql) 21 | { 22 | this.gql = gql; 23 | } 24 | 25 | /// 26 | /// Support for a GET request with a "query" param passed in the query string. 27 | /// 28 | /// The GraphQL query to execute. 29 | /// The execution result. 30 | [HttpGet] 31 | public GraphQLResponse Get(string query) 32 | { 33 | // TODO: Try catch and handle errors by adding to response 34 | var data = gql.Current.ExecuteQuery(query); 35 | 36 | return new GraphQLResponse() 37 | { 38 | Data = data 39 | }; 40 | } 41 | 42 | /// 43 | /// Support for a POST request with a Content-Type header of "application/graphql" 44 | /// with the HTTP POST body contents as the query to execute. 45 | /// 46 | /// The execution result. 47 | [HttpPost] 48 | [Consumes("application/graphql")] 49 | public async Task PostWithContentType() 50 | { 51 | using (var reader = new StreamReader(Request.Body, Encoding.UTF8)) 52 | { 53 | return Get(await reader.ReadToEndAsync()); 54 | } 55 | } 56 | 57 | /// 58 | /// Support for a POST request with JSON-encoded HTTP POST contents in form: 59 | /// { 60 | /// "query": "...", 61 | /// "operationName": "...", 62 | /// "variables": { "myVariable": "someValue", ... } 63 | /// } 64 | /// 65 | /// The execution result. 66 | [HttpPost] 67 | [Consumes("application/json")] 68 | public GraphQLResponse Post([FromBody]GraphQLRequest request) 69 | { 70 | // TODO: Handle other properties in the object?? 71 | // TODO: Try catch and handle errors by adding to response 72 | var data = gql.Current.ExecuteQuery(request.Query); 73 | 74 | return new GraphQLResponse() 75 | { 76 | Data = data 77 | }; 78 | } 79 | 80 | /// 81 | /// Support for a POST request with a "query" param passed in the query string. 82 | /// 83 | /// The GraphQL query to execute. 84 | /// The execution result. 85 | [HttpPost] 86 | public GraphQLResponse PostWithQueryString([FromQuery]string query) 87 | { 88 | return Get(query); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /examples/02-field-arguments/02-field-arguments/FieldArgumentsExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Net; 3 | using NUnit.Framework; 4 | using System.Collections.Generic; 5 | using Newtonsoft.Json.Linq; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Converters; 8 | using System.Linq; 9 | 10 | namespace _02_field_arguments 11 | { 12 | [TestFixture] 13 | public class FieldArgumentsExample 14 | { 15 | class Context 16 | { 17 | public IList Humans { get; set; } 18 | } 19 | 20 | class Human 21 | { 22 | public string Id { get; set; } 23 | public string Name { get; set; } 24 | public double Height { get; set; } 25 | } 26 | 27 | [Test] 28 | public void RunExample() 29 | { 30 | var schema = GraphQL.CreateDefaultSchema(() => 31 | new Context 32 | { 33 | Humans = new List { 34 | new Human { Id = "1000", Name = "Luke Skywalker", Height = 1.72 } 35 | } 36 | }); 37 | schema.AddType().AddAllFields(); 38 | schema.AddField( 39 | "human", 40 | new { id = "-1" }, 41 | (c, args) => c.Humans.SingleOrDefault(h => h.Id == args.id)); 42 | 43 | schema.Complete(); 44 | 45 | var gql = new GraphQL(schema); 46 | var queryResult = gql.ExecuteQuery("{human(id: \"1000\") {name, height}}"); 47 | DeepEquals(queryResult, "{human: {name: 'Luke Skywalker', height: 1.72}}"); 48 | } 49 | 50 | [Test] 51 | public void RunExample2() 52 | { 53 | var schema = GraphQL.CreateDefaultSchema(() => 54 | new Context 55 | { 56 | Humans = new List { 57 | new Human { Id = "1000", Name = "Luke Skywalker", Height = 1.72 } 58 | } 59 | }); 60 | var humanSchema = schema.AddType(); 61 | humanSchema.AddField(h => h.Id); 62 | humanSchema.AddField(h => h.Name); 63 | humanSchema.AddField( 64 | "height", 65 | new { unit = "METER"}, 66 | (c, args, h) => args.unit == "FOOT" ? h.Height * 3.28084 : h.Height); 67 | 68 | schema.AddField( 69 | "human", 70 | new { id = "-1" }, 71 | (c, args) => c.Humans.SingleOrDefault(h => h.Id == args.id)); 72 | 73 | schema.Complete(); 74 | 75 | var gql = new GraphQL(schema); 76 | var queryResult = gql.ExecuteQuery("{human(id: \"1000\") {name, height(unit: \"FOOT\")}}"); 77 | DeepEquals(queryResult, "{human: {name: 'Luke Skywalker', height: 5.6430448}}"); 78 | } 79 | 80 | private static readonly JsonSerializer Serializer = new JsonSerializer 81 | { 82 | Converters = { new StringEnumConverter() }, 83 | }; 84 | 85 | private static void DeepEquals(IDictionary results, string json) 86 | { 87 | var expected = JObject.Parse(json); 88 | var actual = JObject.FromObject(results, Serializer); 89 | if (expected.ToString() == actual.ToString()) 90 | return; 91 | 92 | throw new Exception($"Results do not match expectation:\n\nExpected:\n{expected}\n\nActual:\n{actual}"); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /GraphQL.Parser/Utilities.fs: -------------------------------------------------------------------------------- 1 | //MIT License 2 | // 3 | //Copyright (c) 2016 Robert Peele 4 | // 5 | //Permission is hereby granted, free of charge, to any person obtaining a copy 6 | //of this software and associated documentation files (the "Software"), to deal 7 | //in the Software without restriction, including without limitation the rights 8 | //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | //copies of the Software, and to permit persons to whom the Software is 10 | //furnished to do so, subject to the following conditions: 11 | // 12 | //The above copyright notice and this permission notice shall be included in all 13 | //copies or substantial portions of the Software. 14 | // 15 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | //SOFTWARE. 22 | 23 | [] 24 | module GraphQL.Parser.Utilities 25 | open GraphQL.Parser 26 | open System.Collections.Generic 27 | 28 | /// Make a dictionary from a list of key/value pair tuples. 29 | /// This is like the built-in F# function dict, except it returns a System.Collections.Dictionary 30 | /// instead of an IDictionary, and it does *not* allow duplicate keys to implicitly overwrite each 31 | /// other. 32 | let dictionary (pairs : ('k * 'v) seq) = 33 | let d = new Dictionary<_, _>() 34 | for key, value in pairs do 35 | d.Add(key, value) 36 | d 37 | 38 | type IReadOnlyDictionary<'k, 'v> with 39 | /// Return `Some value` if the key is present in the dictionary, otherwise `None`. 40 | member this.TryFind(key : 'k) = 41 | let mutable output = Unchecked.defaultof<'v> 42 | if this.TryGetValue(key, &output) then Some output 43 | else None 44 | 45 | let invalid msg = 46 | raise (new ValidationException(msg)) 47 | 48 | let failAt pos msg = 49 | raise (new SourceException(msg, pos)) 50 | 51 | let chooseWithSource transform inputs = 52 | seq { 53 | for { Source = pos; Value = v } in inputs do 54 | match transform v with 55 | | None -> () 56 | | Some tr -> 57 | yield { Source = pos; Value = tr } 58 | } 59 | 60 | let mapWithSource transform inputs = 61 | seq { 62 | for { Source = pos; Value = v } in inputs do 63 | yield { Source = pos; Value = transform v } 64 | } 65 | 66 | let collectWithSource transform inputs = 67 | inputs 68 | |> mapWithSource transform 69 | |> Seq.collect 70 | (function { Source = pos; Value = vs } -> vs |> Seq.map (fun v -> { Source = pos; Value = v })) 71 | 72 | let mapDictionaryWithSource transform inputs = 73 | seq { 74 | for KeyValue(name, { Source = pos; Value = v }) in inputs do 75 | yield name, { Source = pos; Value = transform v } 76 | } |> dictionary :> IReadOnlyDictionary<_, _> 77 | 78 | let toReadOnlyList xs = xs |> Seq.toArray :> IReadOnlyList<_> 79 | 80 | let appendReadOnlyList xs ys = Seq.append xs ys |> toReadOnlyList 81 | 82 | let obj2option x = 83 | if obj.ReferenceEquals(x, null) then None 84 | else Some x 85 | 86 | [] 87 | let emptyDictionary<'k, 'v when 'k : equality> : IReadOnlyDictionary<'k, 'v> = 88 | [||] |> dictionary :> IReadOnlyDictionary<'k, 'v> -------------------------------------------------------------------------------- /examples/01-simple-query/01-simple-query/SimpleQueryExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Net; 3 | using NUnit.Framework; 4 | using System.Collections.Generic; 5 | using Newtonsoft.Json.Linq; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Converters; 8 | 9 | namespace _01_simple_query 10 | { 11 | [TestFixture] 12 | public class SimpleQueryExample 13 | { 14 | class Context 15 | { 16 | public Character Hero { get; set; } 17 | } 18 | 19 | class Character 20 | { 21 | public string Name { get; set; } 22 | public IEnumerable Friends { get; set; } 23 | } 24 | 25 | private Context CreateDefaultContext() 26 | { 27 | return new Context 28 | { 29 | Hero = new Character 30 | { 31 | Name = "R2-D2", 32 | Friends = new List 33 | { 34 | new Character { 35 | Name = "Luke Skywalker" 36 | }, 37 | new Character { 38 | Name = "Han Solo" 39 | }, 40 | new Character { 41 | Name = "Leia Organa" 42 | } 43 | } 44 | } 45 | }; 46 | } 47 | 48 | [Test] 49 | public void RunExample() 50 | { 51 | var schema = GraphQL.CreateDefaultSchema(CreateDefaultContext); 52 | schema.AddType().AddAllFields(); 53 | schema.AddField("hero", c => c.Hero); 54 | 55 | schema.Complete(); 56 | 57 | var gql = new GraphQL(schema); 58 | var queryResult = gql.ExecuteQuery( 59 | @"{ 60 | hero { 61 | name, 62 | friends { 63 | name 64 | } 65 | } 66 | }" 67 | ); 68 | DeepEquals( 69 | queryResult, 70 | @"{ 71 | hero: { 72 | name: 'R2-D2', 73 | friends: [ 74 | { 75 | name: 'Luke Skywalker' 76 | }, 77 | { 78 | name: 'Han Solo' 79 | }, 80 | { 81 | name: 'Leia Organa' 82 | } 83 | ] 84 | } 85 | }" 86 | ); 87 | } 88 | 89 | private static readonly JsonSerializer Serializer = new JsonSerializer 90 | { 91 | Converters = { new StringEnumConverter() }, 92 | }; 93 | 94 | private static void DeepEquals(IDictionary results, string json) 95 | { 96 | var expected = JObject.Parse(json); 97 | var actual = JObject.FromObject(results, Serializer); 98 | if (expected.ToString() == actual.ToString()) 99 | return; 100 | 101 | throw new Exception($"Results do not match expectation:\n\nExpected:\n{expected}\n\nActual:\n{actual}"); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | *.nupkg 19 | 20 | # Roslyn cache directories 21 | *.ide/ 22 | .vs/ 23 | .idea/ 24 | 25 | # MSTest test Results 26 | [Tt]est[Rr]esult*/ 27 | [Bb]uild[Ll]og.* 28 | 29 | #NUNIT 30 | *.VisualState.xml 31 | TestResult.xml 32 | 33 | # Build Results of an ATL Project 34 | [Dd]ebugPS/ 35 | [Rr]eleasePS/ 36 | dlldata.c 37 | 38 | *_i.c 39 | *_p.c 40 | *_i.h 41 | *.ilk 42 | *.meta 43 | *.obj 44 | *.pch 45 | *.pdb 46 | *.pgc 47 | *.pgd 48 | *.rsp 49 | *.sbr 50 | *.tlb 51 | *.tli 52 | *.tlh 53 | *.tmp 54 | *.tmp_proj 55 | *.log 56 | *.vspscc 57 | *.vssscc 58 | .builds 59 | *.pidb 60 | *.svclog 61 | *.scc 62 | 63 | # Chutzpah Test files 64 | _Chutzpah* 65 | 66 | # Visual C++ cache files 67 | ipch/ 68 | *.aps 69 | *.ncb 70 | *.opensdf 71 | *.sdf 72 | *.cachefile 73 | 74 | # Visual Studio profiler 75 | *.psess 76 | *.vsp 77 | *.vspx 78 | 79 | # TFS 2012 Local Workspace 80 | $tf/ 81 | 82 | # Guidance Automation Toolkit 83 | *.gpState 84 | 85 | # ReSharper is a .NET coding add-in 86 | _ReSharper*/ 87 | *.[Rr]e[Ss]harper 88 | *.DotSettings.user 89 | 90 | # JustCode is a .NET coding addin-in 91 | .JustCode 92 | 93 | # TeamCity is a build add-in 94 | _TeamCity* 95 | 96 | # DotCover is a Code Coverage Tool 97 | *.dotCover 98 | 99 | # NCrunch 100 | _NCrunch_* 101 | .*crunch*.local.xml 102 | 103 | # MightyMoose 104 | *.mm.* 105 | AutoTest.Net/ 106 | 107 | # Web workbench (sass) 108 | .sass-cache/ 109 | 110 | # Installshield output folder 111 | [Ee]xpress/ 112 | 113 | # DocProject is a documentation generator add-in 114 | DocProject/buildhelp/ 115 | DocProject/Help/*.HxT 116 | DocProject/Help/*.HxC 117 | DocProject/Help/*.hhc 118 | DocProject/Help/*.hhk 119 | DocProject/Help/*.hhp 120 | DocProject/Help/Html2 121 | DocProject/Help/html 122 | 123 | # Click-Once directory 124 | publish/ 125 | 126 | # Publish Web Output 127 | *.[Pp]ublish.xml 128 | *.azurePubxml 129 | ## TODO: Comment the next line if you want to checkin your 130 | ## web deploy settings but do note that will include unencrypted 131 | ## passwords 132 | #*.pubxml 133 | 134 | # NuGet Packages Directory 135 | packages/* 136 | ## TODO: If the tool you use requires repositories.config 137 | ## uncomment the next line 138 | #!packages/repositories.config 139 | 140 | # Enable "build/" folder in the NuGet Packages folder since 141 | # NuGet packages use it for MSBuild targets. 142 | # This line needs to be after the ignore of the build folder 143 | # (and the packages folder if the line above has been uncommented) 144 | !packages/build/ 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | sql/ 155 | *.Cache 156 | ClientBin/ 157 | [Ss]tyle[Cc]op.* 158 | ~$* 159 | *~ 160 | *.dbmdl 161 | *.dbproj.schemaview 162 | *.pfx 163 | *.snk 164 | *.publishsettings 165 | node_modules/ 166 | bower_components/ 167 | 168 | # RIA/Silverlight projects 169 | Generated_Code/ 170 | 171 | # Backup & report files from converting an old project file 172 | # to a newer Visual Studio version. Backup files are not needed, 173 | # because we have git ;-) 174 | _UpgradeReport_Files/ 175 | Backup*/ 176 | UpgradeLog*.XML 177 | UpgradeLog*.htm 178 | 179 | # SQL Server files 180 | *.mdf 181 | *.ldf 182 | 183 | # Business Intelligence projects 184 | *.rdl.data 185 | *.bim.layout 186 | *.bim_*.settings 187 | 188 | # Microsoft Fakes 189 | FakesAssemblies/ 190 | 191 | # LightSwitch generated files 192 | GeneratedArtifacts/ 193 | _Pvt_Extensions/ 194 | ModelManifest.xml 195 | -------------------------------------------------------------------------------- /examples/03-aliases/.gitignore: -------------------------------------------------------------------------------- 1 | # Download this file using PowerShell v3 under Windows with the following comand: 2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 3 | # or wget: 4 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # NuGet Packages 21 | *.nupkg 22 | # The packages folder can be ignored because of Package Restore 23 | **/packages/* 24 | # except build/, which is used as an MSBuild target. 25 | !**/packages/build/ 26 | # Uncomment if necessary however generally it will be regenerated when needed 27 | #!**/packages/repositories.config 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | *_i.c 34 | *_p.c 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.log 55 | *.scc 56 | 57 | # OS generated files # 58 | .DS_Store* 59 | Icon? 60 | 61 | # Visual C++ cache files 62 | ipch/ 63 | *.aps 64 | *.ncb 65 | *.opensdf 66 | *.sdf 67 | *.cachefile 68 | 69 | # Visual Studio profiler 70 | *.psess 71 | *.vsp 72 | *.vspx 73 | 74 | # Guidance Automation Toolkit 75 | *.gpState 76 | 77 | # ReSharper is a .NET coding add-in 78 | _ReSharper*/ 79 | *.[Rr]e[Ss]harper 80 | 81 | # TeamCity is a build add-in 82 | _TeamCity* 83 | 84 | # DotCover is a Code Coverage Tool 85 | *.dotCover 86 | 87 | # NCrunch 88 | *.ncrunch* 89 | .*crunch*.local.xml 90 | 91 | # Installshield output folder 92 | [Ee]xpress/ 93 | 94 | # DocProject is a documentation generator add-in 95 | DocProject/buildhelp/ 96 | DocProject/Help/*.HxT 97 | DocProject/Help/*.HxC 98 | DocProject/Help/*.hhc 99 | DocProject/Help/*.hhk 100 | DocProject/Help/*.hhp 101 | DocProject/Help/Html2 102 | DocProject/Help/html 103 | 104 | # Click-Once directory 105 | publish/ 106 | 107 | # Publish Web Output 108 | *.Publish.xml 109 | 110 | # Windows Azure Build Output 111 | csx 112 | *.build.csdef 113 | 114 | # Windows Store app package directory 115 | AppPackages/ 116 | 117 | # Others 118 | *.Cache 119 | ClientBin/ 120 | [Ss]tyle[Cc]op.* 121 | ~$* 122 | *~ 123 | *.dbmdl 124 | *.[Pp]ublish.xml 125 | *.pfx 126 | *.publishsettings 127 | modulesbin/ 128 | tempbin/ 129 | 130 | # EPiServer Site file (VPP) 131 | AppData/ 132 | 133 | # RIA/Silverlight projects 134 | Generated_Code/ 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | UpgradeLog*.htm 142 | 143 | # vim 144 | *.txt~ 145 | *.swp 146 | *.swo 147 | 148 | # svn 149 | .svn 150 | 151 | # Remainings from resolvings conflicts in Source Control 152 | *.orig 153 | 154 | # SQL Server files 155 | **/App_Data/*.mdf 156 | **/App_Data/*.ldf 157 | **/App_Data/*.sdf 158 | 159 | 160 | #LightSwitch generated files 161 | GeneratedArtifacts/ 162 | _Pvt_Extensions/ 163 | ModelManifest.xml 164 | 165 | # ========================= 166 | # Windows detritus 167 | # ========================= 168 | 169 | # Windows image file caches 170 | Thumbs.db 171 | ehthumbs.db 172 | 173 | # Folder config file 174 | Desktop.ini 175 | 176 | # Recycle Bin used on file shares 177 | $RECYCLE.BIN/ 178 | 179 | # Mac desktop service store files 180 | .DS_Store 181 | 182 | # SASS Compiler cache 183 | .sass-cache 184 | 185 | # Visual Studio 2014 CTP 186 | **/*.sln.ide 187 | 188 | # Visual Studio temp something 189 | .vs/ 190 | 191 | ##### 192 | # End of core ignore list, below put you custom 'per project' settings (patterns or path) 193 | ##### -------------------------------------------------------------------------------- /examples/04-fragments/.gitignore: -------------------------------------------------------------------------------- 1 | # Download this file using PowerShell v3 under Windows with the following comand: 2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 3 | # or wget: 4 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # NuGet Packages 21 | *.nupkg 22 | # The packages folder can be ignored because of Package Restore 23 | **/packages/* 24 | # except build/, which is used as an MSBuild target. 25 | !**/packages/build/ 26 | # Uncomment if necessary however generally it will be regenerated when needed 27 | #!**/packages/repositories.config 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | *_i.c 34 | *_p.c 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.log 55 | *.scc 56 | 57 | # OS generated files # 58 | .DS_Store* 59 | Icon? 60 | 61 | # Visual C++ cache files 62 | ipch/ 63 | *.aps 64 | *.ncb 65 | *.opensdf 66 | *.sdf 67 | *.cachefile 68 | 69 | # Visual Studio profiler 70 | *.psess 71 | *.vsp 72 | *.vspx 73 | 74 | # Guidance Automation Toolkit 75 | *.gpState 76 | 77 | # ReSharper is a .NET coding add-in 78 | _ReSharper*/ 79 | *.[Rr]e[Ss]harper 80 | 81 | # TeamCity is a build add-in 82 | _TeamCity* 83 | 84 | # DotCover is a Code Coverage Tool 85 | *.dotCover 86 | 87 | # NCrunch 88 | *.ncrunch* 89 | .*crunch*.local.xml 90 | 91 | # Installshield output folder 92 | [Ee]xpress/ 93 | 94 | # DocProject is a documentation generator add-in 95 | DocProject/buildhelp/ 96 | DocProject/Help/*.HxT 97 | DocProject/Help/*.HxC 98 | DocProject/Help/*.hhc 99 | DocProject/Help/*.hhk 100 | DocProject/Help/*.hhp 101 | DocProject/Help/Html2 102 | DocProject/Help/html 103 | 104 | # Click-Once directory 105 | publish/ 106 | 107 | # Publish Web Output 108 | *.Publish.xml 109 | 110 | # Windows Azure Build Output 111 | csx 112 | *.build.csdef 113 | 114 | # Windows Store app package directory 115 | AppPackages/ 116 | 117 | # Others 118 | *.Cache 119 | ClientBin/ 120 | [Ss]tyle[Cc]op.* 121 | ~$* 122 | *~ 123 | *.dbmdl 124 | *.[Pp]ublish.xml 125 | *.pfx 126 | *.publishsettings 127 | modulesbin/ 128 | tempbin/ 129 | 130 | # EPiServer Site file (VPP) 131 | AppData/ 132 | 133 | # RIA/Silverlight projects 134 | Generated_Code/ 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | UpgradeLog*.htm 142 | 143 | # vim 144 | *.txt~ 145 | *.swp 146 | *.swo 147 | 148 | # svn 149 | .svn 150 | 151 | # Remainings from resolvings conflicts in Source Control 152 | *.orig 153 | 154 | # SQL Server files 155 | **/App_Data/*.mdf 156 | **/App_Data/*.ldf 157 | **/App_Data/*.sdf 158 | 159 | 160 | #LightSwitch generated files 161 | GeneratedArtifacts/ 162 | _Pvt_Extensions/ 163 | ModelManifest.xml 164 | 165 | # ========================= 166 | # Windows detritus 167 | # ========================= 168 | 169 | # Windows image file caches 170 | Thumbs.db 171 | ehthumbs.db 172 | 173 | # Folder config file 174 | Desktop.ini 175 | 176 | # Recycle Bin used on file shares 177 | $RECYCLE.BIN/ 178 | 179 | # Mac desktop service store files 180 | .DS_Store 181 | 182 | # SASS Compiler cache 183 | .sass-cache 184 | 185 | # Visual Studio 2014 CTP 186 | **/*.sln.ide 187 | 188 | # Visual Studio temp something 189 | .vs/ 190 | 191 | ##### 192 | # End of core ignore list, below put you custom 'per project' settings (patterns or path) 193 | ##### -------------------------------------------------------------------------------- /examples/07-mutations/.gitignore: -------------------------------------------------------------------------------- 1 | # Download this file using PowerShell v3 under Windows with the following comand: 2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 3 | # or wget: 4 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # NuGet Packages 21 | *.nupkg 22 | # The packages folder can be ignored because of Package Restore 23 | **/packages/* 24 | # except build/, which is used as an MSBuild target. 25 | !**/packages/build/ 26 | # Uncomment if necessary however generally it will be regenerated when needed 27 | #!**/packages/repositories.config 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | *_i.c 34 | *_p.c 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.log 55 | *.scc 56 | 57 | # OS generated files # 58 | .DS_Store* 59 | Icon? 60 | 61 | # Visual C++ cache files 62 | ipch/ 63 | *.aps 64 | *.ncb 65 | *.opensdf 66 | *.sdf 67 | *.cachefile 68 | 69 | # Visual Studio profiler 70 | *.psess 71 | *.vsp 72 | *.vspx 73 | 74 | # Guidance Automation Toolkit 75 | *.gpState 76 | 77 | # ReSharper is a .NET coding add-in 78 | _ReSharper*/ 79 | *.[Rr]e[Ss]harper 80 | 81 | # TeamCity is a build add-in 82 | _TeamCity* 83 | 84 | # DotCover is a Code Coverage Tool 85 | *.dotCover 86 | 87 | # NCrunch 88 | *.ncrunch* 89 | .*crunch*.local.xml 90 | 91 | # Installshield output folder 92 | [Ee]xpress/ 93 | 94 | # DocProject is a documentation generator add-in 95 | DocProject/buildhelp/ 96 | DocProject/Help/*.HxT 97 | DocProject/Help/*.HxC 98 | DocProject/Help/*.hhc 99 | DocProject/Help/*.hhk 100 | DocProject/Help/*.hhp 101 | DocProject/Help/Html2 102 | DocProject/Help/html 103 | 104 | # Click-Once directory 105 | publish/ 106 | 107 | # Publish Web Output 108 | *.Publish.xml 109 | 110 | # Windows Azure Build Output 111 | csx 112 | *.build.csdef 113 | 114 | # Windows Store app package directory 115 | AppPackages/ 116 | 117 | # Others 118 | *.Cache 119 | ClientBin/ 120 | [Ss]tyle[Cc]op.* 121 | ~$* 122 | *~ 123 | *.dbmdl 124 | *.[Pp]ublish.xml 125 | *.pfx 126 | *.publishsettings 127 | modulesbin/ 128 | tempbin/ 129 | 130 | # EPiServer Site file (VPP) 131 | AppData/ 132 | 133 | # RIA/Silverlight projects 134 | Generated_Code/ 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | UpgradeLog*.htm 142 | 143 | # vim 144 | *.txt~ 145 | *.swp 146 | *.swo 147 | 148 | # svn 149 | .svn 150 | 151 | # Remainings from resolvings conflicts in Source Control 152 | *.orig 153 | 154 | # SQL Server files 155 | **/App_Data/*.mdf 156 | **/App_Data/*.ldf 157 | **/App_Data/*.sdf 158 | 159 | 160 | #LightSwitch generated files 161 | GeneratedArtifacts/ 162 | _Pvt_Extensions/ 163 | ModelManifest.xml 164 | 165 | # ========================= 166 | # Windows detritus 167 | # ========================= 168 | 169 | # Windows image file caches 170 | Thumbs.db 171 | ehthumbs.db 172 | 173 | # Folder config file 174 | Desktop.ini 175 | 176 | # Recycle Bin used on file shares 177 | $RECYCLE.BIN/ 178 | 179 | # Mac desktop service store files 180 | .DS_Store 181 | 182 | # SASS Compiler cache 183 | .sass-cache 184 | 185 | # Visual Studio 2014 CTP 186 | **/*.sln.ide 187 | 188 | # Visual Studio temp something 189 | .vs/ 190 | 191 | ##### 192 | # End of core ignore list, below put you custom 'per project' settings (patterns or path) 193 | ##### -------------------------------------------------------------------------------- /examples/01-simple-query/.gitignore: -------------------------------------------------------------------------------- 1 | # Download this file using PowerShell v3 under Windows with the following comand: 2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 3 | # or wget: 4 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # NuGet Packages 21 | *.nupkg 22 | # The packages folder can be ignored because of Package Restore 23 | **/packages/* 24 | # except build/, which is used as an MSBuild target. 25 | !**/packages/build/ 26 | # Uncomment if necessary however generally it will be regenerated when needed 27 | #!**/packages/repositories.config 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | *_i.c 34 | *_p.c 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.log 55 | *.scc 56 | 57 | # OS generated files # 58 | .DS_Store* 59 | Icon? 60 | 61 | # Visual C++ cache files 62 | ipch/ 63 | *.aps 64 | *.ncb 65 | *.opensdf 66 | *.sdf 67 | *.cachefile 68 | 69 | # Visual Studio profiler 70 | *.psess 71 | *.vsp 72 | *.vspx 73 | 74 | # Guidance Automation Toolkit 75 | *.gpState 76 | 77 | # ReSharper is a .NET coding add-in 78 | _ReSharper*/ 79 | *.[Rr]e[Ss]harper 80 | 81 | # TeamCity is a build add-in 82 | _TeamCity* 83 | 84 | # DotCover is a Code Coverage Tool 85 | *.dotCover 86 | 87 | # NCrunch 88 | *.ncrunch* 89 | .*crunch*.local.xml 90 | 91 | # Installshield output folder 92 | [Ee]xpress/ 93 | 94 | # DocProject is a documentation generator add-in 95 | DocProject/buildhelp/ 96 | DocProject/Help/*.HxT 97 | DocProject/Help/*.HxC 98 | DocProject/Help/*.hhc 99 | DocProject/Help/*.hhk 100 | DocProject/Help/*.hhp 101 | DocProject/Help/Html2 102 | DocProject/Help/html 103 | 104 | # Click-Once directory 105 | publish/ 106 | 107 | # Publish Web Output 108 | *.Publish.xml 109 | 110 | # Windows Azure Build Output 111 | csx 112 | *.build.csdef 113 | 114 | # Windows Store app package directory 115 | AppPackages/ 116 | 117 | # Others 118 | *.Cache 119 | ClientBin/ 120 | [Ss]tyle[Cc]op.* 121 | ~$* 122 | *~ 123 | *.dbmdl 124 | *.[Pp]ublish.xml 125 | *.pfx 126 | *.publishsettings 127 | modulesbin/ 128 | tempbin/ 129 | 130 | # EPiServer Site file (VPP) 131 | AppData/ 132 | 133 | # RIA/Silverlight projects 134 | Generated_Code/ 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | UpgradeLog*.htm 142 | 143 | # vim 144 | *.txt~ 145 | *.swp 146 | *.swo 147 | 148 | # svn 149 | .svn 150 | 151 | # Remainings from resolvings conflicts in Source Control 152 | *.orig 153 | 154 | # SQL Server files 155 | **/App_Data/*.mdf 156 | **/App_Data/*.ldf 157 | **/App_Data/*.sdf 158 | 159 | 160 | #LightSwitch generated files 161 | GeneratedArtifacts/ 162 | _Pvt_Extensions/ 163 | ModelManifest.xml 164 | 165 | # ========================= 166 | # Windows detritus 167 | # ========================= 168 | 169 | # Windows image file caches 170 | Thumbs.db 171 | ehthumbs.db 172 | 173 | # Folder config file 174 | Desktop.ini 175 | 176 | # Recycle Bin used on file shares 177 | $RECYCLE.BIN/ 178 | 179 | # Mac desktop service store files 180 | .DS_Store 181 | 182 | # SASS Compiler cache 183 | .sass-cache 184 | 185 | # Visual Studio 2014 CTP 186 | **/*.sln.ide 187 | 188 | # Visual Studio temp something 189 | .vs/ 190 | 191 | ##### 192 | # End of core ignore list, below put you custom 'per project' settings (patterns or path) 193 | ##### -------------------------------------------------------------------------------- /examples/02-field-arguments/.gitignore: -------------------------------------------------------------------------------- 1 | # Download this file using PowerShell v3 under Windows with the following comand: 2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 3 | # or wget: 4 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # NuGet Packages 21 | *.nupkg 22 | # The packages folder can be ignored because of Package Restore 23 | **/packages/* 24 | # except build/, which is used as an MSBuild target. 25 | !**/packages/build/ 26 | # Uncomment if necessary however generally it will be regenerated when needed 27 | #!**/packages/repositories.config 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | *_i.c 34 | *_p.c 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.log 55 | *.scc 56 | 57 | # OS generated files # 58 | .DS_Store* 59 | Icon? 60 | 61 | # Visual C++ cache files 62 | ipch/ 63 | *.aps 64 | *.ncb 65 | *.opensdf 66 | *.sdf 67 | *.cachefile 68 | 69 | # Visual Studio profiler 70 | *.psess 71 | *.vsp 72 | *.vspx 73 | 74 | # Guidance Automation Toolkit 75 | *.gpState 76 | 77 | # ReSharper is a .NET coding add-in 78 | _ReSharper*/ 79 | *.[Rr]e[Ss]harper 80 | 81 | # TeamCity is a build add-in 82 | _TeamCity* 83 | 84 | # DotCover is a Code Coverage Tool 85 | *.dotCover 86 | 87 | # NCrunch 88 | *.ncrunch* 89 | .*crunch*.local.xml 90 | 91 | # Installshield output folder 92 | [Ee]xpress/ 93 | 94 | # DocProject is a documentation generator add-in 95 | DocProject/buildhelp/ 96 | DocProject/Help/*.HxT 97 | DocProject/Help/*.HxC 98 | DocProject/Help/*.hhc 99 | DocProject/Help/*.hhk 100 | DocProject/Help/*.hhp 101 | DocProject/Help/Html2 102 | DocProject/Help/html 103 | 104 | # Click-Once directory 105 | publish/ 106 | 107 | # Publish Web Output 108 | *.Publish.xml 109 | 110 | # Windows Azure Build Output 111 | csx 112 | *.build.csdef 113 | 114 | # Windows Store app package directory 115 | AppPackages/ 116 | 117 | # Others 118 | *.Cache 119 | ClientBin/ 120 | [Ss]tyle[Cc]op.* 121 | ~$* 122 | *~ 123 | *.dbmdl 124 | *.[Pp]ublish.xml 125 | *.pfx 126 | *.publishsettings 127 | modulesbin/ 128 | tempbin/ 129 | 130 | # EPiServer Site file (VPP) 131 | AppData/ 132 | 133 | # RIA/Silverlight projects 134 | Generated_Code/ 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | UpgradeLog*.htm 142 | 143 | # vim 144 | *.txt~ 145 | *.swp 146 | *.swo 147 | 148 | # svn 149 | .svn 150 | 151 | # Remainings from resolvings conflicts in Source Control 152 | *.orig 153 | 154 | # SQL Server files 155 | **/App_Data/*.mdf 156 | **/App_Data/*.ldf 157 | **/App_Data/*.sdf 158 | 159 | 160 | #LightSwitch generated files 161 | GeneratedArtifacts/ 162 | _Pvt_Extensions/ 163 | ModelManifest.xml 164 | 165 | # ========================= 166 | # Windows detritus 167 | # ========================= 168 | 169 | # Windows image file caches 170 | Thumbs.db 171 | ehthumbs.db 172 | 173 | # Folder config file 174 | Desktop.ini 175 | 176 | # Recycle Bin used on file shares 177 | $RECYCLE.BIN/ 178 | 179 | # Mac desktop service store files 180 | .DS_Store 181 | 182 | # SASS Compiler cache 183 | .sass-cache 184 | 185 | # Visual Studio 2014 CTP 186 | **/*.sln.ide 187 | 188 | # Visual Studio temp something 189 | .vs/ 190 | 191 | ##### 192 | # End of core ignore list, below put you custom 'per project' settings (patterns or path) 193 | ##### -------------------------------------------------------------------------------- /examples/08-inline-fragments/.gitignore: -------------------------------------------------------------------------------- 1 | # Download this file using PowerShell v3 under Windows with the following comand: 2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 3 | # or wget: 4 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # NuGet Packages 21 | *.nupkg 22 | # The packages folder can be ignored because of Package Restore 23 | **/packages/* 24 | # except build/, which is used as an MSBuild target. 25 | !**/packages/build/ 26 | # Uncomment if necessary however generally it will be regenerated when needed 27 | #!**/packages/repositories.config 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | *_i.c 34 | *_p.c 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.log 55 | *.scc 56 | 57 | # OS generated files # 58 | .DS_Store* 59 | Icon? 60 | 61 | # Visual C++ cache files 62 | ipch/ 63 | *.aps 64 | *.ncb 65 | *.opensdf 66 | *.sdf 67 | *.cachefile 68 | 69 | # Visual Studio profiler 70 | *.psess 71 | *.vsp 72 | *.vspx 73 | 74 | # Guidance Automation Toolkit 75 | *.gpState 76 | 77 | # ReSharper is a .NET coding add-in 78 | _ReSharper*/ 79 | *.[Rr]e[Ss]harper 80 | 81 | # TeamCity is a build add-in 82 | _TeamCity* 83 | 84 | # DotCover is a Code Coverage Tool 85 | *.dotCover 86 | 87 | # NCrunch 88 | *.ncrunch* 89 | .*crunch*.local.xml 90 | 91 | # Installshield output folder 92 | [Ee]xpress/ 93 | 94 | # DocProject is a documentation generator add-in 95 | DocProject/buildhelp/ 96 | DocProject/Help/*.HxT 97 | DocProject/Help/*.HxC 98 | DocProject/Help/*.hhc 99 | DocProject/Help/*.hhk 100 | DocProject/Help/*.hhp 101 | DocProject/Help/Html2 102 | DocProject/Help/html 103 | 104 | # Click-Once directory 105 | publish/ 106 | 107 | # Publish Web Output 108 | *.Publish.xml 109 | 110 | # Windows Azure Build Output 111 | csx 112 | *.build.csdef 113 | 114 | # Windows Store app package directory 115 | AppPackages/ 116 | 117 | # Others 118 | *.Cache 119 | ClientBin/ 120 | [Ss]tyle[Cc]op.* 121 | ~$* 122 | *~ 123 | *.dbmdl 124 | *.[Pp]ublish.xml 125 | *.pfx 126 | *.publishsettings 127 | modulesbin/ 128 | tempbin/ 129 | 130 | # EPiServer Site file (VPP) 131 | AppData/ 132 | 133 | # RIA/Silverlight projects 134 | Generated_Code/ 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | UpgradeLog*.htm 142 | 143 | # vim 144 | *.txt~ 145 | *.swp 146 | *.swo 147 | 148 | # svn 149 | .svn 150 | 151 | # Remainings from resolvings conflicts in Source Control 152 | *.orig 153 | 154 | # SQL Server files 155 | **/App_Data/*.mdf 156 | **/App_Data/*.ldf 157 | **/App_Data/*.sdf 158 | 159 | 160 | #LightSwitch generated files 161 | GeneratedArtifacts/ 162 | _Pvt_Extensions/ 163 | ModelManifest.xml 164 | 165 | # ========================= 166 | # Windows detritus 167 | # ========================= 168 | 169 | # Windows image file caches 170 | Thumbs.db 171 | ehthumbs.db 172 | 173 | # Folder config file 174 | Desktop.ini 175 | 176 | # Recycle Bin used on file shares 177 | $RECYCLE.BIN/ 178 | 179 | # Mac desktop service store files 180 | .DS_Store 181 | 182 | # SASS Compiler cache 183 | .sass-cache 184 | 185 | # Visual Studio 2014 CTP 186 | **/*.sln.ide 187 | 188 | # Visual Studio temp something 189 | .vs/ 190 | 191 | ##### 192 | # End of core ignore list, below put you custom 'per project' settings (patterns or path) 193 | ##### -------------------------------------------------------------------------------- /Tests/IntrospectionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | 6 | namespace Tests 7 | { 8 | [TestFixture] 9 | public class IntrospectionTests 10 | { 11 | [Test] 12 | public void TypeDirectFields() 13 | { 14 | var gql = MemContext.CreateDefaultContext(); 15 | var results = gql.ExecuteQuery("{ __type(name: \"User\") { name, description, kind } }"); 16 | Test.DeepEquals(results, "{ __type: { name: 'User', description: '', kind: 'OBJECT' } }"); 17 | } 18 | 19 | [Test] 20 | public void TypeWithChildFields() 21 | { 22 | var gql = MemContext.CreateDefaultContext(); 23 | var results = gql.ExecuteQuery("{ __type(name: \"User\") { fields { name } } }"); 24 | Test.DeepEquals(results, 25 | @"{ 26 | __type: { 27 | fields: [ 28 | { name: 'id' }, 29 | { name: 'name' }, 30 | { name: 'account' }, 31 | { name: 'nullRef' }, 32 | { name: 'total' }, 33 | { name: 'accountPaid' }, 34 | { name: 'abc' }, 35 | { name: 'sub' }, 36 | { name: '__typename' } 37 | ] 38 | } 39 | }"); 40 | } 41 | 42 | [Test] 43 | public void ChildFieldType() 44 | { 45 | var gql = MemContext.CreateDefaultContext(); 46 | var results = gql.ExecuteQuery("{ __type(name: \"User\") { fields { name, type { name, kind, ofType { name, kind } } } } }"); 47 | Test.DeepEquals(results, 48 | @"{ 49 | __type: { 50 | fields: [ 51 | { name: 'id', type: { name: null, kind: 'NON_NULL', ofType: { name: 'Int', kind: 'SCALAR' } } }, 52 | { name: 'name', type: { name: 'String', kind: 'SCALAR', ofType: null } }, 53 | { name: 'account', type: { name: 'Account', kind: 'OBJECT', ofType: null } }, 54 | { name: 'nullRef', type: { name: 'NullRef', kind: 'OBJECT', ofType: null } }, 55 | { name: 'total', type: { name: null, kind: 'NON_NULL', ofType: { name: 'Int', kind: 'SCALAR' } } }, 56 | { name: 'accountPaid', type: { name: null, kind: 'NON_NULL', ofType: { name: 'Boolean', kind: 'SCALAR' } } }, 57 | { name: 'abc', type: { name: 'String', kind: 'SCALAR', ofType: null } }, 58 | { name: 'sub', type: { name: 'Sub', kind: 'OBJECT', ofType: null } }, 59 | { name: '__typename', type: { name: 'String', kind: 'SCALAR', ofType: null } } 60 | ] 61 | } 62 | }"); 63 | } 64 | 65 | [Test] 66 | public void SchemaTypes() 67 | { 68 | // TODO: Use Test.DeepEquals once we get all the primitive type noise sorted out 69 | 70 | var gql = MemContext.CreateDefaultContext(); 71 | var schema = (IDictionary) gql.ExecuteQuery("{ __schema { types { name, kind, interfaces { name } } } }")["__schema"]; 72 | var types = (List>) schema["types"]; 73 | 74 | var intType = types.First(t => (string) t["name"] == "Int"); 75 | Assert.AreEqual(intType["name"], "Int"); 76 | Assert.AreEqual(intType["kind"].ToString(), "SCALAR"); 77 | Assert.IsNull(intType["interfaces"]); 78 | 79 | var userType = types.First(t => (string) t["name"] == "User"); 80 | Assert.AreEqual(userType["name"], "User"); 81 | Assert.AreEqual(userType["kind"].ToString(), "OBJECT"); 82 | Assert.AreEqual(((List>)userType["interfaces"]).Count, 0); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /docs/queries_and_mutations/fragments.md: -------------------------------------------------------------------------------- 1 | # Fragments 2 | 3 | With _fragments_ GraphQL provides a concept of reusable units constructing a set of fields and include them in queries where you need them. 4 | 5 | Here is an example using a fragment to avoid repetition: 6 | 7 | ```graphql 8 | { 9 | leftComparison: hero(episode: EMPIRE) { 10 | ...comparisonFields 11 | } 12 | rightComparison: hero(episode: JEDI) { 13 | ...comparisonFields 14 | } 15 | } 16 | 17 | fragment comparisonFields on Character { 18 | name 19 | appearsIn 20 | friends { 21 | name 22 | } 23 | } 24 | ``` 25 | 26 | The result could look like this: 27 | ```json 28 | { 29 | "data": { 30 | "leftComparison": { 31 | "name": "Luke Skywalker", 32 | "appearsIn": [ 33 | "NEWHOPE", 34 | "EMPIRE", 35 | "JEDI" 36 | ], 37 | "friends": [ 38 | { 39 | "name": "Han Solo" 40 | }, 41 | { 42 | "name": "Leia Organa" 43 | }, 44 | { 45 | "name": "C-3PO" 46 | }, 47 | { 48 | "name": "R2-D2" 49 | } 50 | ] 51 | }, 52 | "rightComparison": { 53 | "name": "R2-D2", 54 | "appearsIn": [ 55 | "NEWHOPE", 56 | "EMPIRE", 57 | "JEDI" 58 | ], 59 | "friends": [ 60 | { 61 | "name": "Luke Skywalker" 62 | }, 63 | { 64 | "name": "Han Solo" 65 | }, 66 | { 67 | "name": "Leia Organa" 68 | } 69 | ] 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | The data models could like this: 76 | 77 | ```csharp 78 | class Context 79 | { 80 | public IList Heros { get; set; } 81 | } 82 | 83 | public class Character 84 | { 85 | public string Id { get; set; } 86 | public string Name { get; set; } 87 | public string Episode { get; set; } 88 | public string[] AppearsIn { get; set; } 89 | public IEnumerable Friends { get; set; } 90 | } 91 | ``` 92 | 93 | The schema and context could look like this: 94 | ```csharp 95 | var schema = GraphQL.CreateDefaultSchema(() => 96 | new Context 97 | { 98 | Heros = new List { 99 | new Character { 100 | Id = "1000", 101 | Name = "Luke Skywalker", 102 | Episode = "EMPIRE", 103 | AppearsIn = new string[] { "NEWHOPE", "EMPIRE", "JEDI"}, 104 | Friends = new List { 105 | new Character { Name = "Han Solo"}, 106 | new Character { Name = "Leia Organa"}, 107 | new Character { Name = "C-3PO"}, 108 | new Character { Name = "R2-D2"} 109 | } 110 | }, 111 | new Character { 112 | Id = "1001", 113 | Name = "R2-D2", 114 | Episode = "JEDI", 115 | AppearsIn = new string[] {"NEWHOPE", "EMPIRE", "JEDI" }, 116 | Friends = new List { 117 | new Character { Name = "Luke Skywalker"}, 118 | new Character { Name = "Han Solo"}, 119 | new Character { Name = "Leia Organa"} 120 | } 121 | } 122 | } 123 | }); 124 | schema.AddType().AddAllFields(); 125 | schema.AddField( 126 | "hero", 127 | new { episode = "EMPIRE" }, 128 | (c, args) => c.Heros.SingleOrDefault(h => h.Episode == args.episode)); 129 | 130 | schema.Complete(); 131 | ``` 132 | 133 | Now we run the query: 134 | ```csharp 135 | var gql = new GraphQL(schema); 136 | var queryResult = gql.ExecuteQuery( 137 | @"{ 138 | leftComparison: hero(episode: ""EMPIRE"") { 139 | ...comparisonFields 140 | } 141 | rightComparison: hero(episode: ""JEDI"") { 142 | ...comparisonFields 143 | } 144 | } 145 | fragment comparisonFields on Character { 146 | name 147 | appearsIn 148 | friends { 149 | name 150 | } 151 | }" 152 | ); 153 | ``` 154 | The result is as expected, see `examples/04-fragments` for a running example. -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Services/MyDocumentClientInitializer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using WebApi.Models; 3 | 4 | namespace WebApi.Services 5 | { 6 | public interface IMyDocumentClientInitializer 7 | { 8 | Task Reset(); 9 | } 10 | 11 | public class MyDocumentClientInitializer : IMyDocumentClientInitializer 12 | { 13 | private readonly IMyDocumentClient documentClient; 14 | 15 | public MyDocumentClientInitializer(IMyDocumentClient documentClient) 16 | { 17 | this.documentClient = documentClient; 18 | } 19 | 20 | public async Task Reset() 21 | { 22 | await DeleteDatabase(); 23 | await CreateDatabase(); 24 | await CreateUsers(); 25 | await CreateAccounts(); 26 | } 27 | 28 | private async Task DeleteDatabase() 29 | { 30 | await documentClient.Current.DeleteDatabaseAsync(documentClient.DatabaseSelfLink); 31 | } 32 | 33 | private async Task CreateDatabase() 34 | { 35 | await documentClient.GetOrCreateDatabaseAsync(); 36 | } 37 | 38 | private async Task CreateUsers() 39 | { 40 | // Create users collection 41 | await documentClient.GetOrCreateUsersCollectionAsync(); 42 | 43 | // Insert users 44 | foreach (var userToInsert in Users) 45 | { 46 | var user = await documentClient.Current.CreateDocumentAsync( 47 | documentClient.UsersCollectionSelfLink, 48 | userToInsert 49 | ); 50 | } 51 | } 52 | 53 | private async Task CreateAccounts() 54 | { 55 | // Create accounts collection 56 | await documentClient.GetOrCreateAccountsCollectionAsync(); 57 | 58 | // Insert accounts 59 | foreach (var accountToInsert in Accounts) 60 | { 61 | var user = await documentClient.Current.CreateDocumentAsync( 62 | documentClient.AccountsCollectionSelfLink, 63 | accountToInsert 64 | ); 65 | } 66 | } 67 | 68 | public static User User1 = new User() 69 | { 70 | Id = "56745d3f-9812-42b2-b52a-c97e720f10ac", 71 | Profile = new Profile() 72 | { 73 | Age = 10, 74 | Gender = "female" 75 | }, 76 | AccountId = "1" 77 | }; 78 | 79 | public static User User2 = new User() 80 | { 81 | Id = "9c0b2900-6566-4632-a910-030ed9edced1", 82 | Profile = new Profile() 83 | { 84 | Age = 20, 85 | Gender = "male" 86 | }, 87 | AccountId = "2" 88 | }; 89 | 90 | public static User User3 = new User() 91 | { 92 | Id = "a03aa638-f93f-4547-9428-c00333d3262e", 93 | Profile = new Profile() 94 | { 95 | Age = 30, 96 | Gender = "female" 97 | }, 98 | AccountId = "3" 99 | }; 100 | 101 | public static User[] Users = new [] 102 | { 103 | User1, 104 | User2, 105 | User3 106 | }; 107 | 108 | public static Account Account1 = new Account() 109 | { 110 | Id = "1", 111 | Name = "Account 1", 112 | Paid = false 113 | }; 114 | 115 | public static Account Account2 = new Account() 116 | { 117 | Id = "2", 118 | Name = "Account 2", 119 | Paid = true 120 | }; 121 | 122 | public static Account Account3 = new Account() 123 | { 124 | Id = "3", 125 | Name = "Account 3", 126 | Paid = false 127 | }; 128 | 129 | public static Account[] Accounts = new[] 130 | { 131 | Account1, 132 | Account2, 133 | Account3 134 | }; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /examples/07-mutations/07-mutations/MutationsExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Net; 3 | using NUnit.Framework; 4 | using System.Collections.Generic; 5 | using Newtonsoft.Json.Linq; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Converters; 8 | using System.Linq; 9 | 10 | namespace _07_mutations 11 | { 12 | [TestFixture] 13 | public class MutationsExample 14 | { 15 | class Context 16 | { 17 | public IList Reviews { get; set; } 18 | } 19 | 20 | public class Review 21 | { 22 | public string Episode { get; set; } 23 | public string Commentary { get; set; } 24 | public int Stars { get; set; } 25 | public int Id { get; internal set; } 26 | } 27 | 28 | public class ReviewInput 29 | { 30 | public string Commentary { get; set; } 31 | public int Stars { get; set; } 32 | } 33 | 34 | [Test] 35 | public void RunExample() 36 | { 37 | var defaultContext = new Context 38 | { 39 | Reviews = new List { 40 | new Review { 41 | Stars = 5, 42 | Episode = "EMPIRE", 43 | Commentary = "Great movie" 44 | } 45 | } 46 | }; 47 | 48 | var schema = GraphQL.CreateDefaultSchema(() => defaultContext); 49 | schema.AddType().AddAllFields(); 50 | schema.AddScalar( 51 | new 52 | { 53 | stars = default(int), 54 | commentary = default(string) 55 | }, 56 | i => new ReviewInput { Stars = i.stars, Commentary = i.commentary }, 57 | "ReviewInput" 58 | ); 59 | schema.AddMutation( 60 | "createReview", 61 | new { episode = "EMPIRE", review = default(ReviewInput) }, 62 | (db, args) => 63 | { 64 | var newId = db.Reviews.Select(r => r.Id).Max() + 1; 65 | var review = new Review 66 | { 67 | Id = newId, 68 | Episode = args.episode, 69 | Commentary = args.review.Commentary, 70 | Stars = args.review.Stars 71 | }; 72 | db.Reviews.Add(review); 73 | return newId; 74 | }, 75 | (db, args, rId) => db.Reviews.AsQueryable().SingleOrDefault(r => r.Id == rId) 76 | ); 77 | schema.Complete(); 78 | 79 | var gql = new GraphQL(schema); 80 | var queryResult = gql.ExecuteQuery( 81 | @"mutation CreateReviewForEpisode($ep: String!, $review: ReviewInput!) { 82 | createReview(episode: ""JEDI"", review: {commentary: ""This is a great movie!"", stars: 5}) { 83 | stars 84 | commentary 85 | } 86 | }" 87 | ); 88 | DeepEquals( 89 | queryResult, 90 | @"{ 91 | ""createReview"": { 92 | ""stars"": 5, 93 | ""commentary"": ""This is a great movie!"" 94 | } 95 | } 96 | "); 97 | } 98 | 99 | private static readonly JsonSerializer Serializer = new JsonSerializer 100 | { 101 | Converters = { new StringEnumConverter() }, 102 | }; 103 | 104 | private static void DeepEquals(IDictionary results, string json) 105 | { 106 | var expected = JObject.Parse(json); 107 | var actual = JObject.FromObject(results, Serializer); 108 | if (expected.ToString() == actual.ToString()) 109 | return; 110 | 111 | throw new Exception($"Results do not match expectation:\n\nExpected:\n{expected}\n\nActual:\n{actual}"); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /docs/queries_and_mutations/mutations.md: -------------------------------------------------------------------------------- 1 | # Mutations 2 | 3 | GraphQL introduces mutations to perform write-operations. 4 | 5 | >Just like queries, mutations return an object type you can ask for nested fields. 6 | 7 | The following mutation is meant to _create a review_: 8 | 9 | ```graphql 10 | mutation CreateReviewForEpisode($ep: String!, $review: ReviewInput!) { 11 | createReview(episode: $ep, review: $review) { 12 | stars 13 | commentary 14 | } 15 | } 16 | ``` 17 | 18 | With the following parameters: 19 | ```json 20 | { 21 | "ep": "JEDI", 22 | "review": { 23 | "stars": 5, 24 | "commentary": "This is a great movie!" 25 | } 26 | } 27 | ``` 28 | 29 | > **Note** 30 | > 31 | > You need GraphQL.Net version >= 3.0.1 32 | 33 | 34 | We can define the data model and context like this: 35 | 36 | ```csharp 37 | public class Review 38 | { 39 | public string Episode { get; set; } 40 | public string Commentary { get; set; } 41 | public int Stars { get; set; } 42 | public int Id { get; internal set; } 43 | } 44 | 45 | public class ReviewInput 46 | { 47 | public string Commentary { get; set; } 48 | public int Stars { get; set; } 49 | } 50 | 51 | class Context 52 | { 53 | public IList Reviews { get; set; } 54 | } 55 | ``` 56 | 57 | We create a default context with some data: 58 | ```csharp 59 | var defaultContext = new Context 60 | { 61 | Reviews = new List { 62 | new Review { 63 | Stars = 5, 64 | Episode = "EMPIRE", 65 | Commentary = "Great movie" 66 | } 67 | } 68 | }; 69 | ``` 70 | 71 | We create the schema and add `Review` as a type: 72 | 73 | ```csharp 74 | var schema = GraphQL.CreateDefaultSchema(() => defaultContext); 75 | schema.AddType().AddAllFields(); 76 | ``` 77 | 78 | Now we add the type `ReviewInput` as a scalar to the schema so that we can use it as an input type in the mutation: 79 | 80 | ```csharp 81 | schema.AddScalar( 82 | new { 83 | stars = default(int), 84 | commentary = default(string) 85 | }, 86 | i => new ReviewInput { Stars = i.stars, Commentary = i.commentary }, 87 | "ReviewInput" 88 | ); 89 | ``` 90 | 91 | The signature of the method `AddScalar` looks like this: 92 | ```csharp 93 | void AddScalar(TRepr shape, Func translate, string name = null); 94 | ``` 95 | Now we can define the mutation: 96 | ```csharp 97 | schema.AddMutation( 98 | "createReview", 99 | new { episode = "EMPIRE", review = default(ReviewInput) }, 100 | (db, args) => 101 | { 102 | var newId = db.Reviews.Select(r => r.Id).Max() + 1; 103 | var review = new Review 104 | { 105 | Id = newId, 106 | Episode = args.episode, 107 | Commentary = args.review.Commentary, 108 | Stars = args.review.Stars 109 | }; 110 | db.Reviews.Add(review); 111 | return newId; 112 | }, 113 | (db, args, rId) => db.Reviews.AsQueryable().SingleOrDefault(r => r.Id == rId) 114 | ); 115 | ``` 116 | 117 | The signature of `AddMutation` looks like this: 118 | ```csharp 119 | GraphQLFieldBuilder AddMutation(string name, TArgs argObj, Func mutation, Expression> queryableGetter); 120 | ``` 121 | 122 | Finally, we complete the schema: 123 | ```csharp 124 | schema.Complete(); 125 | ``` 126 | 127 | Now we can run the mutation: 128 | ```csharp 129 | var gql = new GraphQL(schema); 130 | var queryResult = gql.ExecuteQuery( 131 | @"mutation CreateReviewForEpisode($ep: String!, $review: ReviewInput!) { 132 | createReview(episode: ""JEDI"", review: {commentary: ""This is a great movie!"", stars: 5}) { 133 | stars 134 | commentary 135 | } 136 | }" 137 | ); 138 | ``` 139 | 140 | > **Note** 141 | > 142 | > Currently, GraphQL.Net does not support passing a separate object containing the input variable values into `ExecuteQuery`, the variables have to be put into the query string. 143 | 144 | The result is as expected, see `examples/07-mutations` for a running example. 145 | -------------------------------------------------------------------------------- /docs/queries_and_mutations/inline-fragments.md: -------------------------------------------------------------------------------- 1 | #Inline Fragments 2 | 3 | [GraphQL docs](http://graphql.org/learn/queries/#inline-fragments): 4 | >Like many other type systems, GraphQL schemas include the ability to define interfaces and union types. Learn about them in the schema guide. 5 | 6 | >If you are querying a field that returns an interface or a union type, you will need to use inline fragments to access data on the underlying concrete type. 7 | 8 | Let's look at an example query with inline fragments: 9 | 10 | ```graphql 11 | query Heros { 12 | heros { 13 | name 14 | ... on Droid { 15 | primaryFunction 16 | } 17 | ... on Stormtrooper { 18 | specialization 19 | } 20 | ... on Human { 21 | height 22 | } 23 | } 24 | }" 25 | ``` 26 | 27 | The expected result looks like this: 28 | ```json 29 | { 30 | "heros": [ 31 | { 32 | "name": "Han Solo", 33 | "height": 5.6430448 34 | }, 35 | { 36 | "name": "FN-2187", 37 | "specialization": "Imperial Snowtrooper", 38 | "height": 4.9 39 | }, 40 | { 41 | "name": "R2-D2", 42 | "primaryFunction": "Astromech" 43 | } 44 | ] 45 | } 46 | ``` 47 | 48 | The date model can be implemented as follows: 49 | ```csharp 50 | class Character 51 | { 52 | public int Id { get; set; } 53 | public string Name { get; set; } 54 | } 55 | 56 | class Human : Character 57 | { 58 | public double Height { get; set; } 59 | } 60 | class Stormtrooper : Human 61 | { 62 | public string Specialization { get; set; } 63 | } 64 | 65 | class Droid : Character 66 | { 67 | public string PrimaryFunction { get; set; } 68 | } 69 | ``` 70 | 71 | With the following context and data: 72 | ```csharp 73 | class Context 74 | { 75 | public IList Heros { get; set; } 76 | } 77 | 78 | ... 79 | 80 | var defaultContext = new Context 81 | { 82 | Heros = new List { 83 | new Human 84 | { 85 | Id = 1, 86 | Name = "Han Solo", 87 | Height = 5.6430448 88 | }, 89 | new Stormtrooper 90 | { 91 | Id = 2, 92 | Name = "FN-2187", 93 | Height = 4.9, 94 | Specialization = "Imperial Snowtrooper" 95 | }, 96 | new Droid 97 | { 98 | Id = 3, 99 | Name = "R2-D2", 100 | PrimaryFunction = "Astromech" 101 | } 102 | } 103 | }; 104 | ``` 105 | 106 | The schema can be defined as follows: 107 | ```csharp 108 | var schema = GraphQL.CreateDefaultSchema(() => defaultContext); 109 | schema.AddType().AddAllFields(); 110 | schema.AddType().AddAllFields(); 111 | schema.AddType().AddAllFields(); 112 | schema.AddType().AddAllFields(); 113 | schema.AddListField( 114 | "heros", 115 | db => db.Heros.AsQueryable() 116 | ); 117 | schema.Complete(); 118 | ``` 119 | 120 | Now we can run the query: 121 | ```csharp 122 | var gql = new GraphQL(schema); 123 | var queryResult = gql.ExecuteQuery( 124 | @"query Heros { 125 | heros { 126 | name 127 | ... on Droid { 128 | primaryFunction 129 | } 130 | ... on Stormtrooper { 131 | specialization 132 | } 133 | ... on Human { 134 | height 135 | } 136 | } 137 | }" 138 | ); 139 | ``` 140 | 141 | The result is as expected, see examples/08-inline-fragments for a running example. 142 | 143 | > **Note:** 144 | > 145 | > If two types with a common base type (which may be `object`) both have a property with the same name but with different types, the schema builder will raise an error: 146 | > ``` 147 | >at GraphQL.Net.GraphQLSchema`1.CompleteType(GraphQLType type) 148 | at GraphQL.Net.GraphQLSchema`1.CompleteTypes(IEnumerable`1 types) 149 | at GraphQL.Net.GraphQLSchema`1.Complete() 150 | at _08_inline_fragments.InlineFragmentsExample.RunExample() 151 | Result Message: System.ArgumentException : The type 'Character' has multiple fields named 'test' with different types. 152 | >``` 153 | > This may be supported in the future. 154 | > As a workaround the properties can be added to the grapqhl types using different field names. 155 | -------------------------------------------------------------------------------- /GraphQL.Net/GraphQL.Net.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {530742A1-10D8-4255-837D-43D57B00EE50} 8 | Library 9 | Properties 10 | GraphQL.Net 11 | GraphQL.Net 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | true 35 | 36 | 37 | graphql.net.snk 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | {d5f65a61-8c03-46f1-86aa-7b6da4be25f5} 83 | GraphQL.Parser 84 | 85 | 86 | 87 | 94 | -------------------------------------------------------------------------------- /examples/09-cosmos-db/WebApi/Services/MyDocumentClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Documents; 2 | using Microsoft.Azure.Documents.Client; 3 | using Microsoft.Extensions.Configuration; 4 | using System; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace WebApi.Services 9 | { 10 | public interface IMyDocumentClient 11 | { 12 | IDocumentClient Current { get; } 13 | 14 | string DatabaseSelfLink { get; } 15 | Task GetOrCreateDatabaseAsync(); 16 | 17 | string UsersCollectionSelfLink { get; } 18 | Task GetOrCreateUsersCollectionAsync(); 19 | IQueryable GetUsers(); 20 | 21 | string AccountsCollectionSelfLink { get; } 22 | Task GetOrCreateAccountsCollectionAsync(); 23 | IQueryable GetAccounts(); 24 | 25 | // etc 26 | } 27 | 28 | public class MyDocumentClient : IMyDocumentClient 29 | { 30 | private readonly string endpointUrl; 31 | private readonly string authKey; 32 | 33 | private readonly string databaseId; 34 | public string DatabaseSelfLink { get; private set; } 35 | 36 | private readonly string usersCollectionId = "users"; 37 | public string UsersCollectionSelfLink { get; private set; } 38 | 39 | private readonly string accountsCollectionId = "accounts"; 40 | public string AccountsCollectionSelfLink { get; private set; } 41 | 42 | // etc 43 | 44 | private static readonly ConnectionPolicy connPolicy = new ConnectionPolicy(); 45 | //{ 46 | // ConnectionMode = ConnectionMode.Direct, 47 | // ConnectionProtocol = Protocol.Tcp 48 | //}; 49 | 50 | public IDocumentClient Current { get; private set; } 51 | 52 | public MyDocumentClient(IConfiguration config) 53 | { 54 | endpointUrl = config["CosmosDb:EndpointUrl"]; 55 | authKey = config["CosmosDb:AuthorizationKey"]; 56 | databaseId = config["CosmosDb:DatabaseId"]; 57 | 58 | // Allow Fiddler interception 59 | connPolicy.EnableEndpointDiscovery = false; 60 | 61 | Current = new DocumentClient(new Uri(endpointUrl), authKey, connectionPolicy: connPolicy); 62 | 63 | // Setup 64 | Task.WaitAll(GetOrCreateDatabaseAsync()); 65 | Task.WaitAll(new [] { 66 | GetOrCreateUsersCollectionAsync(), 67 | GetOrCreateAccountsCollectionAsync() 68 | // etc 69 | }); 70 | } 71 | 72 | public async Task GetOrCreateDatabaseAsync() 73 | { 74 | var db = Current.CreateDatabaseQuery().Where(d => d.Id == databaseId).ToArray().FirstOrDefault() 75 | ?? await Current.CreateDatabaseAsync(new Database { Id = databaseId }); 76 | DatabaseSelfLink = db.SelfLink; 77 | return db; 78 | } 79 | 80 | public async Task GetOrCreateUsersCollectionAsync() 81 | { 82 | var usersColl = Current.CreateDocumentCollectionQuery(DatabaseSelfLink).Where(c => c.Id == usersCollectionId).ToArray().FirstOrDefault() 83 | ?? await Current.CreateDocumentCollectionAsync(DatabaseSelfLink, new DocumentCollection() { Id = usersCollectionId }, new RequestOptions() { OfferThroughput = 400 }); 84 | UsersCollectionSelfLink = usersColl.SelfLink; 85 | return usersColl; 86 | } 87 | 88 | public IQueryable GetUsers() 89 | { 90 | return Current.CreateDocumentQuery(UsersCollectionSelfLink); 91 | } 92 | 93 | public async Task GetOrCreateAccountsCollectionAsync() 94 | { 95 | var accountsColl = Current.CreateDocumentCollectionQuery(DatabaseSelfLink).Where(c => c.Id == accountsCollectionId).ToArray().FirstOrDefault() 96 | ?? await Current.CreateDocumentCollectionAsync(DatabaseSelfLink, new DocumentCollection() { Id = accountsCollectionId }, new RequestOptions() { OfferThroughput = 400 }); 97 | AccountsCollectionSelfLink = accountsColl.SelfLink; 98 | return accountsColl; 99 | } 100 | 101 | public IQueryable GetAccounts() 102 | { 103 | return Current.CreateDocumentQuery(AccountsCollectionSelfLink); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /examples/08-inline-fragments/08-inline-fragments/InlineFragmentsExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GraphQL.Net; 3 | using NUnit.Framework; 4 | using System.Collections.Generic; 5 | using Newtonsoft.Json.Linq; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Converters; 8 | using System.Linq; 9 | 10 | namespace _08_inline_fragments 11 | { 12 | [TestFixture] 13 | public class InlineFragmentsExample 14 | { 15 | class Context 16 | { 17 | public IList Heros { get; set; } 18 | } 19 | 20 | class Character 21 | { 22 | public int Id { get; set; } 23 | public string Name { get; set; } 24 | } 25 | 26 | class Human : Character 27 | { 28 | public double Height { get; set; } 29 | } 30 | class Stormtrooper : Human 31 | { 32 | public string Specialization { get; set; } 33 | } 34 | 35 | class Droid : Character 36 | { 37 | public string PrimaryFunction { get; set; } 38 | } 39 | 40 | [Test] 41 | public void RunExample() 42 | { 43 | var defaultContext = new Context 44 | { 45 | Heros = new List { 46 | new Human 47 | { 48 | Id = 1, 49 | Name = "Han Solo", 50 | Height = 5.6430448 51 | }, 52 | new Stormtrooper 53 | { 54 | Id = 2, 55 | Name = "FN-2187", 56 | Height = 4.9, 57 | Specialization = "Imperial Snowtrooper" 58 | }, 59 | new Droid 60 | { 61 | Id = 3, 62 | Name = "R2-D2", 63 | PrimaryFunction = "Astromech" 64 | } 65 | } 66 | }; 67 | 68 | var schema = GraphQL.CreateDefaultSchema(() => defaultContext); 69 | schema.AddType().AddAllFields(); 70 | schema.AddType().AddAllFields(); 71 | schema.AddType().AddAllFields(); 72 | schema.AddType().AddAllFields(); 73 | schema.AddListField( 74 | "heros", 75 | db => db.Heros.AsQueryable() 76 | ); 77 | schema.Complete(); 78 | 79 | var gql = new GraphQL(schema); 80 | var queryResult = gql.ExecuteQuery( 81 | @"query Heros { 82 | heros { 83 | name 84 | ... on Droid { 85 | primaryFunction 86 | } 87 | ... on Stormtrooper { 88 | specialization 89 | } 90 | ... on Human { 91 | height 92 | } 93 | } 94 | }" 95 | ); 96 | DeepEquals( 97 | queryResult, 98 | @"{ 99 | ""heros"": [ 100 | { 101 | ""name"": ""Han Solo"", 102 | ""height"": 5.6430448 103 | }, 104 | { 105 | ""name"": ""FN-2187"", 106 | ""specialization"": ""Imperial Snowtrooper"", 107 | ""height"": 4.9 108 | }, 109 | { 110 | ""name"": ""R2-D2"", 111 | ""primaryFunction"": ""Astromech"" 112 | } 113 | ] 114 | } 115 | "); 116 | } 117 | 118 | private static readonly JsonSerializer Serializer = new JsonSerializer 119 | { 120 | Converters = { new StringEnumConverter() }, 121 | }; 122 | 123 | private static void DeepEquals(IDictionary results, string json) 124 | { 125 | var expected = JObject.Parse(json); 126 | var actual = JObject.FromObject(results, Serializer); 127 | if (expected.ToString() == actual.ToString()) 128 | return; 129 | 130 | throw new Exception($"Results do not match expectation:\n\nExpected:\n{expected}\n\nActual:\n{actual}"); 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /GraphQL.Net/GraphQLField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using GraphQL.Net.SchemaAdapters; 6 | using GraphQL.Parser; 7 | using GraphQL.Parser.Execution; 8 | 9 | namespace GraphQL.Net 10 | { 11 | internal class GraphQLField 12 | { 13 | public string Name { get; protected set; } 14 | public string Description { get; set; } 15 | public bool IsList { get; protected set; } 16 | 17 | public bool IsPost { get; protected set; } 18 | public Func PostFieldFunc { get; protected set; } 19 | 20 | public bool IsMutation { get; protected set; } 21 | 22 | protected Type FieldCLRType { get; set; } 23 | protected Type ArgsCLRType { get; set; } 24 | internal GraphQLType DefiningType { get; private set; } 25 | internal GraphQLSchema Schema { get; set; } 26 | 27 | // ExprFunc should be of type Func>> 28 | // UNLESS Field is a mutation - then should be Func>> 29 | protected Delegate ExprFunc { get; set; } 30 | 31 | // MutationFunc should be of type Action 32 | protected Delegate MutationFunc { get; set; } 33 | 34 | // lazily initialize type, fields may be defined before all types are loaded 35 | private GraphQLType _type; 36 | public GraphQLType Type => _type ?? (_type = Schema.GetGQLType(FieldCLRType)); 37 | 38 | public virtual IEnumerable> Arguments 39 | => TypeHelpers.GetArgs(Schema.VariableTypes, ArgsCLRType); 40 | 41 | public virtual LambdaExpression GetExpression(IEnumerable> inputs, object mutationReturn = null) 42 | => IsMutation 43 | ? (LambdaExpression)ExprFunc.DynamicInvoke(TypeHelpers.GetArgs(ArgsCLRType, Schema.VariableTypes, inputs), mutationReturn) 44 | : (LambdaExpression)ExprFunc.DynamicInvoke(TypeHelpers.GetArgs(ArgsCLRType, Schema.VariableTypes, inputs)); 45 | 46 | public virtual object RunMutation(TContext context, IEnumerable> inputs) 47 | => MutationFunc?.DynamicInvoke(context, TypeHelpers.GetArgs(ArgsCLRType, Schema.VariableTypes, inputs)); 48 | 49 | public Complexity Complexity { get; set; } 50 | 51 | //TODO: Remove? 52 | public ResolutionType ResolutionType { get; set; } 53 | 54 | public static GraphQLField Post(GraphQLSchema schema, string name, Func fieldFunc) 55 | { 56 | return new GraphQLField 57 | { 58 | Schema = schema, 59 | Name = name, 60 | FieldCLRType = typeof(TField), 61 | ArgsCLRType = typeof(object), 62 | IsPost = true, 63 | PostFieldFunc = () => fieldFunc(), 64 | }; 65 | } 66 | 67 | public static GraphQLField New(GraphQLSchema schema, string name, Func exprFunc, Type fieldCLRType, GraphQLType definingType) 68 | => NewInternal(schema, name, exprFunc, fieldCLRType, definingType, null); 69 | 70 | public static GraphQLField NewMutation(GraphQLSchema schema, string name, Func exprFunc, Type fieldCLRType, GraphQLType definingType, Func mutationFunc) 71 | => NewInternal(schema, name, exprFunc, fieldCLRType, definingType, mutationFunc); 72 | 73 | private static GraphQLField NewInternal(GraphQLSchema schema, string name, Delegate exprFunc, Type fieldCLRType, GraphQLType definingType, Delegate mutationFunc) 74 | { 75 | var isList = false; 76 | if (fieldCLRType.IsGenericType && TypeHelpers.IsAssignableToGenericType(fieldCLRType, typeof(IEnumerable<>))) 77 | { 78 | fieldCLRType = fieldCLRType.GetGenericArguments()[0]; 79 | isList = true; 80 | } 81 | 82 | return new GraphQLField 83 | { 84 | Schema = schema, 85 | Name = name, 86 | FieldCLRType = fieldCLRType, 87 | DefiningType = definingType, 88 | ArgsCLRType = typeof(TArgs), 89 | IsList = isList, 90 | ExprFunc = exprFunc, 91 | MutationFunc = mutationFunc, 92 | IsMutation = mutationFunc != null, 93 | }; 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /GraphQL.Parser.Test/GraphQL.Parser.Test.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 75cdf08f-dbcf-4c68-b713-377776337bbf 9 | Exe 10 | GraphQL.Parser.Test 11 | GraphQL.Parser.Test 12 | v4.5 13 | true 14 | 4.4.0.0 15 | GraphQL.Parser.Test 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | 3 26 | AnyCPU 27 | bin\Debug\GraphQL.Parser.Test.XML 28 | true 29 | 30 | 31 | pdbonly 32 | true 33 | true 34 | bin\Release\ 35 | TRACE 36 | 3 37 | AnyCPU 38 | bin\Release\GraphQL.Parser.Test.XML 39 | true 40 | 41 | 42 | 11 43 | 44 | 45 | 46 | 47 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 48 | 49 | 50 | 51 | 52 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 53 | 54 | 55 | /Library/Frameworks/Mono.framework/Versions/Current/lib/mono/Microsoft F#/v4.0/Microsoft.FSharp.Targets 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | True 72 | 73 | 74 | ..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll 75 | True 76 | 77 | 78 | 79 | 80 | 81 | GraphQL.Parser 82 | {d5f65a61-8c03-46f1-86aa-7b6da4be25f5} 83 | True 84 | 85 | 86 | 93 | -------------------------------------------------------------------------------- /GraphQL.Parser/Integration/CS.fs: -------------------------------------------------------------------------------- 1 | //MIT License 2 | // 3 | //Copyright (c) 2016 Robert Peele 4 | // 5 | //Permission is hereby granted, free of charge, to any person obtaining a copy 6 | //of this software and associated documentation files (the "Software"), to deal 7 | //in the Software without restriction, including without limitation the rights 8 | //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | //copies of the Software, and to permit persons to whom the Software is 10 | //furnished to do so, subject to the following conditions: 11 | // 12 | //The above copyright notice and this permission notice shall be included in all 13 | //copies or substantial portions of the Software. 14 | // 15 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | //SOFTWARE. 22 | 23 | namespace GraphQL.Parser.CS 24 | open GraphQL.Parser 25 | open System.Collections.Generic 26 | open System.Runtime.CompilerServices 27 | 28 | // This module adds abstract classes suitable as a starting point for implementing 29 | // a schema from a C# project. 30 | 31 | [] 32 | type SchemaCS<'s>() = 33 | abstract member Directives : IReadOnlyDictionary> 34 | default this.Directives = emptyDictionary 35 | abstract member QueryTypes : IReadOnlyDictionary> 36 | default this.QueryTypes = emptyDictionary 37 | abstract member VariableTypes : IReadOnlyDictionary 38 | default this.VariableTypes = emptyDictionary 39 | abstract member ResolveEnumValue : name : string -> EnumValue 40 | default this.ResolveEnumValue(_) = Unchecked.defaultof<_> 41 | abstract member RootType : ISchemaQueryType<'s> 42 | interface ISchema<'s> with 43 | member this.Directives = this.Directives 44 | member this.QueryTypes = this.QueryTypes 45 | member this.VariableTypes = this.VariableTypes 46 | member this.ResolveEnumValueByName(name) = this.ResolveEnumValue(name) |> obj2option 47 | member this.RootType = this.RootType 48 | 49 | [] 50 | type SchemaQueryTypeCS<'s>() = 51 | abstract member TypeName : string 52 | abstract member Description : string 53 | default this.Description = null 54 | abstract member Info : 's 55 | default this.Info = Unchecked.defaultof<'s> 56 | abstract member Fields : IReadOnlyDictionary> 57 | interface ISchemaQueryType<'s> with 58 | member this.TypeName = this.TypeName 59 | member this.Description = this.Description |> obj2option 60 | member this.Info = this.Info 61 | member this.Fields = this.Fields 62 | 63 | [] 64 | type SchemaFieldCS<'s>() = 65 | abstract member DeclaringType : ISchemaQueryType<'s> 66 | abstract member FieldType : SchemaFieldType<'s> 67 | abstract member FieldName : string 68 | abstract member Description : string 69 | default this.Description = null 70 | abstract member Info : 's 71 | default this.Info = Unchecked.defaultof<'s> 72 | abstract member Arguments : IReadOnlyDictionary> 73 | default this.Arguments = emptyDictionary 74 | abstract member EstimateComplexity : args : ISchemaArgument<'s> seq -> Complexity 75 | default this.EstimateComplexity(_) = Complexity.One 76 | interface ISchemaField<'s> with 77 | member this.DeclaringType = this.DeclaringType 78 | member this.FieldType = this.FieldType 79 | member this.FieldName = this.FieldName 80 | member this.Description = this.Description |> obj2option 81 | member this.Info = this.Info 82 | member this.Arguments = this.Arguments 83 | member this.EstimateComplexity(args) = this.EstimateComplexity(args) 84 | 85 | [] 86 | type SchemaQueryableFieldCS<'s>() = 87 | inherit SchemaFieldCS<'s>() 88 | override this.FieldType = QueryField this.QueryableFieldType 89 | abstract member QueryableFieldType : ISchemaQueryType<'s> 90 | 91 | [] 92 | type SchemaValueFieldCS<'s>() = 93 | inherit SchemaFieldCS<'s>() 94 | override this.FieldType = ValueField (new VariableType(this.ValueFieldType, this.IsNullable)) 95 | abstract member IsNullable : bool 96 | default this.IsNullable = false 97 | abstract member ValueFieldType : CoreVariableType 98 | 99 | [] 100 | type CSExtensions = 101 | [] 102 | static member Values<'a>(elements : 'a WithSource seq) = elements |> Seq.map (fun e -> e.Value) 103 | 104 | 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL.Net 2 | An implementation of GraphQL for .NET and IQueryable 3 | 4 | [docs](https://ckimes89.gitbooks.io/graphql-net/content) 5 | 6 | ## Description 7 | Many of the .NET GraphQL implementations that have come out so far only seem to work in memory. 8 | For me, this isn't terribly useful since most of my data is stored in a database (and I assume that's the case for many others). 9 | This library is an implementation of the GraphQL spec that converts GraphQL queries to IQueryable. 10 | That IQueryable can then be executed using the ORM of your choice. 11 | 12 | Here's a descriptive example, using an example from [the GraphQL spec](http://facebook.github.io/graphql/#sec-Language.Query-Document.Arguments): 13 | 14 | ``` 15 | { 16 | user(id: 4) { 17 | id 18 | name 19 | profilePic(size: 100) 20 | } 21 | } 22 | ``` 23 | 24 | The above GraphQL query could be translated to: 25 | 26 | ```csharp 27 | db.Users 28 | .Where(u => u.Id == 4) 29 | .Select(u => new 30 | { 31 | id = u.Id, 32 | name = u.Name, 33 | profilePic = db.ProfilePics 34 | .FirstOrDefault(p => p.UserId == u.Id && p.Size == 100) 35 | .Url 36 | }) 37 | .FirstOrDefault(); 38 | ``` 39 | 40 | ## Building a Schema 41 | Let's assume we have an Entity Framework DbContext that looks like this: 42 | 43 | ```csharp 44 | public class TestContext : DbContext 45 | { 46 | public IDbSet Users { get; set; } 47 | public IDbSet Accounts { get; set; } 48 | } 49 | 50 | public class User 51 | { 52 | public int Id { get; set; } 53 | public string Name { get; set; } 54 | 55 | public int AccountId { get; set; } 56 | public Account Account { get; set; } 57 | } 58 | 59 | public class Account 60 | { 61 | public int Id { get; set; } 62 | public string Name { get; set; } 63 | public bool Paid { get; set; } 64 | } 65 | ``` 66 | 67 | First, we create and set the default schema by providing a function that creates our context: 68 | 69 | ```csharp 70 | var schema = GraphQL.CreateDefaultSchema(() => new TestContext()); 71 | ``` 72 | 73 | The default schema is required to use the helper method`GraphQL.Execute(query)`, but you can execute queries against the schema without it. Next, we'll define a type in the schema and fields on that type. 74 | 75 | ```csharp 76 | var user = schema.AddType(); 77 | user.AddField(u => u.Id); 78 | user.AddField(u => u.Name); 79 | user.AddField(u => u.Account); 80 | user.AddField("totalUsers", (db, u) => db.Users.Count()); 81 | user.AddField("accountPaid", (db, u) => u.Account.Paid); 82 | ``` 83 | 84 | Fields can be defined using only a property expression, or you can specify your own fields and provide a custom resolving expression. Let's do the same for account: 85 | 86 | ```csharp 87 | schema.AddType().AddAllFields(); 88 | ``` 89 | 90 | If we just want to expose all fields, we can use the `AddAllFields` helper method. 91 | 92 | The last thing we want to do is create some queries, as fields on the schema itself. Let's add some to find users: 93 | 94 | ```csharp 95 | schema.AddListField("users", db => db.Users); 96 | schema.AddField("user", new { id = 0 }, (db, args) => db.Users.Where(u => u.Id == args.id).FirstOrDefault()); 97 | ``` 98 | 99 | In our first query, we want to see all users so we can just return the entire list. However, notice how in the second query we define the shape of an anonymous type `new { id = 0 }`. This is what is expected to be passed in from the GraphQL query. Since we've defined the shape, we can now use that in the `Where` clause to build our IQueryable. We use `FirstOrDefault` to signify that this query will return a single result. 100 | 101 | ```csharp 102 | schema.Complete(); 103 | ``` 104 | 105 | Finally, we complete the schema when we've finished setting up. Now we're ready to execute a query. 106 | 107 | ## Executing Queries 108 | 109 | ```csharp 110 | var query = @"{ 111 | user(id:1) { 112 | userId : id 113 | userName : name 114 | account { 115 | id 116 | paid 117 | } 118 | totalUsers 119 | }}"; 120 | 121 | var gql = new GraphQL(schema); 122 | var dict = gql.ExecuteQuery(query); 123 | Console.WriteLine(JsonConvert.SerializeObject(dict, Formatting.Indented)); 124 | 125 | // { 126 | // "user": { 127 | // "userId": 1, 128 | // "userName": "Joe User", 129 | // "account": { 130 | // "id": 1, 131 | // "paid": true 132 | // }, 133 | // "totalUsers": 2 134 | // } 135 | // } 136 | ``` 137 | 138 | The results from executing the query are returned as a nested Dictionary which can easily be converted to JSON and returned to the user. 139 | 140 | ## Installation/NuGet 141 | Add GraphQL.Net to your project via the Package Manager Console. 142 | 143 | ``` 144 | PM> Install-Package GraphQL.Net 145 | ``` 146 | 147 | ## TODO 148 | Support directives like @skip. 149 | Support field arguments with complex types. 150 | Support enum types. 151 | Support custom primitive types (ID, datetime, etc). 152 | Introspection 153 | -------------------------------------------------------------------------------- /Tests/InMemoryExecutionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GraphQL.Net; 5 | using NUnit.Framework; 6 | 7 | namespace Tests 8 | { 9 | [TestFixture] 10 | public class InMemoryExecutionTests 11 | { 12 | [Test] public void LookupSingleEntity() => GenericTests.LookupSingleEntity(MemContext.CreateDefaultContext()); 13 | [Test] public void AliasOneField() => GenericTests.AliasOneField(MemContext.CreateDefaultContext()); 14 | [Test] public void NestedEntity() => GenericTests.NestedEntity(MemContext.CreateDefaultContext()); 15 | [Test] public void NoUserQueryReturnsNull() => GenericTests.NoUserQueryReturnsNull(MemContext.CreateDefaultContext()); 16 | [Test] public void CustomFieldSubQuery() => GenericTests.CustomFieldSubQuery(MemContext.CreateDefaultContext()); 17 | [Test] public void CustomFieldSubQueryUsingContext() => GenericTests.CustomFieldSubQueryUsingContext(MemContext.CreateDefaultContext()); 18 | [Test] public void List() => GenericTests.List(MemContext.CreateDefaultContext()); 19 | [Test] public void ListTypeIsList() => GenericTests.ListTypeIsList(MemContext.CreateDefaultContext()); 20 | [Test] public void NestedEntityList() => GenericTests.NestedEntityList(MemContext.CreateDefaultContext()); 21 | [Test] public void PostField() => GenericTests.PostField(MemContext.CreateDefaultContext()); 22 | [Test] public void PostFieldSubQuery() => GenericTests.PostFieldSubQuery(MemContext.CreateDefaultContext()); 23 | [Test] public void TypeName() => GenericTests.TypeName(MemContext.CreateDefaultContext()); 24 | [Test] public void DateTimeFilter() => GenericTests.DateTimeFilter(MemContext.CreateDefaultContext()); 25 | [Test] public void EnumerableSubField() => GenericTests.EnumerableSubField(MemContext.CreateDefaultContext()); 26 | [Test] public void EnumFieldQuery() => GenericTests.EnumFieldQuery(MemContext.CreateDefaultContext()); 27 | [Test] public void SimpleMutation() => GenericTests.SimpleMutation(MemContext.CreateDefaultContext()); 28 | [Test] public void MutationWithReturn() => GenericTests.MutationWithReturn(MemContext.CreateDefaultContext()); 29 | [Test] public void NullPropagation() => GenericTests.NullPropagation(MemContext.CreateDefaultContext()); 30 | [Test] public void GuidField() => GenericTests.GuidField(MemContext.CreateDefaultContext()); 31 | [Test] public void GuidParameter() => GenericTests.GuidParameter(MemContext.CreateDefaultContext()); 32 | [Test] public void ByteArrayParameter() => GenericTests.ByteArrayParameter(MemContext.CreateDefaultContext()); 33 | [Test] public void ChildListFieldWithParameters() => GenericTests.ChildListFieldWithParameters(MemContext.CreateDefaultContext()); 34 | [Test] public void ChildFieldWithParameters() => GenericTests.ChildFieldWithParameters(MemContext.CreateDefaultContext()); 35 | [Test] public static void Fragements() => GenericTests.Fragements(MemContext.CreateDefaultContext()); 36 | [Test] public static void InlineFragements() => GenericTests.InlineFragements(MemContext.CreateDefaultContext()); 37 | [Test] public static void InlineFragementWithListField() => GenericTests.InlineFragementWithListField(MemContext.CreateDefaultContext()); 38 | [Test] public static void FragementWithMultiLevelInheritance() => GenericTests.FragementWithMultiLevelInheritance(MemContext.CreateDefaultContext()); 39 | [Test] public static void InlineFragementWithoutTypenameField() => GenericTests.InlineFragementWithoutTypenameField(MemContext.CreateDefaultContext()); 40 | [Test] public static void FragementWithoutTypenameField() => GenericTests.FragementWithoutTypenameField(MemContext.CreateDefaultContext()); 41 | [Test] public static void InlineFragementWithoutTypenameFieldWithoutOtherFields() => GenericTests.InlineFragementWithoutTypenameFieldWithoutOtherFields(MemContext.CreateDefaultContext()); 42 | [Test] public static void FragementWithMultipleTypenameFields() => GenericTests.FragementWithMultipleTypenameFields(MemContext.CreateDefaultContext()); 43 | [Test] public static void FragementWithMultipleTypenameFieldsMixedWithInlineFragment() => GenericTests.FragementWithMultipleTypenameFieldsMixedWithInlineFragment(MemContext.CreateDefaultContext()); 44 | [Test] public void LookupSingleEntityError() => GenericTests.LookupSingleEntityError(MemContext.CreateDefaultContext()); 45 | 46 | [Test] 47 | public void AddAllFields() 48 | { 49 | var schema = GraphQL.CreateDefaultSchema(() => new MemContext()); 50 | schema.AddType().AddAllFields(); 51 | schema.AddType().AddAllFields(); 52 | schema.AddField("user", new { id = 0 }, (db, args) => db.Users.AsQueryable().FirstOrDefault(u => u.Id == args.id)); 53 | schema.Complete(); 54 | 55 | var gql = new GraphQL(schema); 56 | var results = gql.ExecuteQuery("{ user(id:1) { id, name } }"); 57 | Test.DeepEquals(results, "{ user: { id: 1, name: 'Joe User' } }"); 58 | } 59 | } 60 | } 61 | --------------------------------------------------------------------------------