{"id":13432266,"url":"https://github.com/JacekKosciesza/StarWars","last_synced_at":"2025-03-16T23:31:02.370Z","repository":{"id":42788641,"uuid":"81738087","full_name":"JacekKosciesza/StarWars","owner":"JacekKosciesza","description":"GraphQL 'Star Wars' example using GraphQL for .NET, ASP.NET Core, Entity Framework Core","archived":true,"fork":false,"pushed_at":"2017-12-04T21:53:30.000Z","size":144,"stargazers_count":622,"open_issues_count":10,"forks_count":98,"subscribers_count":51,"default_branch":"master","last_synced_at":"2024-10-27T11:50:17.026Z","etag":null,"topics":["asp-net-core","dotnet-core","entity-framework-core","graphiql","graphql","graphql-dotnet","star-wars"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JacekKosciesza.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-12T16:01:26.000Z","updated_at":"2024-10-16T08:54:12.000Z","dependencies_parsed_at":"2022-09-12T15:14:01.518Z","dependency_job_id":null,"html_url":"https://github.com/JacekKosciesza/StarWars","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacekKosciesza%2FStarWars","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacekKosciesza%2FStarWars/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacekKosciesza%2FStarWars/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacekKosciesza%2FStarWars/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JacekKosciesza","download_url":"https://codeload.github.com/JacekKosciesza/StarWars/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243949869,"owners_count":20373653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-core","dotnet-core","entity-framework-core","graphiql","graphql","graphql-dotnet","star-wars"],"created_at":"2024-07-31T02:01:09.879Z","updated_at":"2025-03-16T23:31:02.053Z","avatar_url":"https://github.com/JacekKosciesza.png","language":"C#","funding_links":[],"categories":["Sample Projects","graphql","C#","C\\#","例子","Sample \u0026 Reference Applications","C# #"],"sub_categories":["Workflow","工作流"],"readme":"# GraphQL 'Star Wars' example using GraphQL for .NET, ASP.NET Core, Entity Framework Core\n![Build Status](https://jkff.visualstudio.com/_apis/public/build/definitions/80056780-12c1-4732-b8a9-d9518cbbe430/1/badge)\n\n## Examples\n* Basic - simple 'Hello GraphQL!' example based on console version from [GraphQL for .NET on GitHub](https://github.com/graphql-dotnet/graphql-dotnet),\nbut using ASP.NET Core, Entity Framework Core and some best practices, patterns and principles.\n\n* Advanced - GraphQL queries and mutations with full 'Star Wars' database (see [GraphQL Specification by Facebook](https://github.com/facebook/graphql) and [GraphQL.js - reference implementation](https://github.com/graphql/graphql-js))\n\n## Roadmap\n- [x] Basic\n    - [x] Simple tutorial (step/screenshot/code)\n    - [ ] Detailed tutorial (steps explanation)\n    - [x] 3-Layers (Api, Core, Data) architecture\n    - [x] DDD (Domain Driven Design) hexagonal architecture\n    - [x] Dependency Inversion (deafult ASP.NET Core IoC container)\n    - [x] GraphQL controller\n    - [x] In Memory 'Droid' Repository\n    - [x] Entity Framework 'Droid' Repository\n    - [x] Automatic database creation\n    - [x] Seed database data\n    - [x] EF Migrations\n    - [x] Graph*i*QL\n    - [x] Unit Tests\n    - [x] Visual Studio 2017 RC upgrade\n    - [x] Integration Tests\n    - [x] Logs\n    - [x] Code Coverage\n    - [x] Continous Integration\n- [ ] Advanced\n    - [x] Full 'Star Wars' database (Episodes, Characters, Planets, Humans etc.)\n    - [x] Base/generic repository\n    - [x] Visual Studio 2017 RTM upgrade\n    - [x] Repositories\n    - [ ] GraphQL queries\n    - [ ] GraphQL mutations\n    - [ ] Docker\n- [ ] PWA (Progressive Web App)\n    - [ ] Identity microservice\n    - [ ] Angular frontend\n    - [ ] Apollo GraphQL Client for Angular\n    - [ ] Service Worker\n    - [ ] IndexedDB\n    - ...\n\n## Tutorials\n\n### Basic\n\n* Create 'StarWars' empty solution\n![empty-solution](https://cloud.githubusercontent.com/assets/8171434/22863729/0831261c-f146-11e6-9fef-040e20462bfe.png)\n\n* Add 'ASP.NET Core Web Application (.NET Core)' project named 'StarWars.Api'\n![aspnet-core-web-application](https://cloud.githubusercontent.com/assets/8171434/22863870/edcbf2f0-f147-11e6-96eb-6a8cc14c3588.png)\n\n* Select Web API template\n![aspnet-core-web-api](https://cloud.githubusercontent.com/assets/8171434/22863872/eff07d80-f147-11e6-9614-d853da97d1aa.png)\n\n* Update all NuGet packages\n![update-all-nuget-packages](https://cloud.githubusercontent.com/assets/8171434/22864011/93533b2e-f149-11e6-883a-ead44bf1f403.png)\n\n* Update project.json with correct runtime\n```json\n\"runtimes\": {\n   \"win10-x64\": { }\n}\n```\n\n* Install GraphQL NuGet package\n![install-graphql-nuget-package](https://cloud.githubusercontent.com/assets/8171434/22864254/50c06f70-f14e-11e6-80d2-6c94f3088c8a.png)\n\n* Create 'StarWars.Core' project\n![starwars-core-project](https://cloud.githubusercontent.com/assets/8171434/22866500/e4313996-f177-11e6-90ce-44843e588dff.png)\n\n* Create 'Droid' model\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Droid\n    {\n        public int Id { get; set; }\n        public string Name { get; set; }\n    }\n}\n```\n\n* Create 'DroidType' model\n```csharp\nusing GraphQL.Types;\nusing StarWars.Core.Models;\n\nnamespace StarWars.Api.Models\n{\n    public class DroidType : ObjectGraphType\u003cDroid\u003e\n    {\n        public DroidType()\n        {\n            Field(x =\u003e x.Id).Description(\"The Id of the Droid.\");\n            Field(x =\u003e x.Name, nullable: true).Description(\"The name of the Droid.\");\n        }\n    }\n}\n```\n\n* Create 'StarWarsQuery' model\n```csharp\nusing GraphQL.Types;\nusing StarWars.Core.Models;\n\nnamespace StarWars.Api.Models\n{\n    public class StarWarsQuery : ObjectGraphType\n    {\n        public StarWarsQuery()\n        {\n            Field\u003cDroidType\u003e(\n              \"hero\",\n              resolve: context =\u003e new Droid { Id = 1, Name = \"R2-D2\" }\n            );\n        }\n    }\n}\n```\n\n* Create 'GraphQLQuery' model\n```csharp\nnamespace StarWars.Api.Models\n{\n    public class GraphQLQuery\n    {\n        public string OperationName { get; set; }\n        public string NamedQuery { get; set; }\n        public string Query { get; set; }\n        public string Variables { get; set; }\n    }\n}\n```\n\n* Create 'GraphQLController'\n```csharp\nusing GraphQL;\nusing GraphQL.Types;\nusing Microsoft.AspNetCore.Mvc;\nusing StarWars.Api.Models;\nusing System.Threading.Tasks;\n\nnamespace StarWars.Api.Controllers\n{\n    [Route(\"graphql\")]\n    public class GraphQLController : Controller\n    {\n        [HttpPost]\n        public async Task\u003cIActionResult\u003e Post([FromBody] GraphQLQuery query)\n        {\n            var schema = new Schema { Query = new StarWarsQuery() };\n\n            var result = await new DocumentExecuter().ExecuteAsync(_ =\u003e\n            {\n                _.Schema = schema;\n                _.Query = query.Query;\n\n            }).ConfigureAwait(false);\n\n            if (result.Errors?.Count \u003e 0)\n            {\n                return BadRequest();\n            }\n\n            return Ok(result);\n        }\n    }\n}\n```\n\n* Test using Postman\n![postman-test-query](https://cloud.githubusercontent.com/assets/8171434/22866705/17985b54-f17b-11e6-848c-6482b45e4934.png)\n\n* Create 'IDroidRepository' interface\n```csharp\nusing StarWars.Core.Models;\nusing System.Threading.Tasks;\n\nnamespace StarWars.Core.Data\n{\n    public interface IDroidRepository\n    {\n        Task\u003cDroid\u003e Get(int id);\n    }\n}\n```\n* Create 'StarWars.Data' project\n![starwars-data-project](https://cloud.githubusercontent.com/assets/8171434/22888090/dd357674-f204-11e6-8613-e2cac5087180.png)\n\n* Create in memory 'DroidRepository'\n```csharp\nusing StarWars.Core.Data;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing StarWars.Core.Models;\nusing System.Linq;\n\nnamespace StarWars.Data.InMemory\n{\n    public class DroidRepository : IDroidRepository\n    {\n        private List\u003cDroid\u003e _droids = new List\u003cDroid\u003e {\n            new Droid { Id = 1, Name = \"R2-D2\" }\n        };\n\n        public Task\u003cDroid\u003e Get(int id)\n        {\n            return Task.FromResult(_droids.FirstOrDefault(droid =\u003e droid.Id == id));\n        }\n    }\n}\n```\n\n* Use 'IDroidRepository' in StarWarsQuery\n```csharp\nusing GraphQL.Types;\nusing StarWars.Core.Data;\n\nnamespace StarWars.Api.Models\n{\n    public class StarWarsQuery : ObjectGraphType\n    {\n        private IDroidRepository _droidRepository { get; set; }\n\n        public StarWarsQuery(IDroidRepository _droidRepository)\n        {\n            Field\u003cDroidType\u003e(\n              \"hero\",\n              resolve: context =\u003e _droidRepository.Get(1)\n            );\n        }\n    }\n}\n```\n\n* Update creation of StarWarsQuery in GraphQLController\n```csharp\n// ...\npublic async Task\u003cIActionResult\u003e Post([FromBody] GraphQLQuery query)\n{\n    var schema = new Schema { Query = new StarWarsQuery(new DroidRepository()) };\n// ...\n```\n\n* Test using Postman\n\n* Configure dependency injection in Startup.cs\n```csharp\n// ...\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddMvc();\n\n    services.AddTransient\u003cStarWarsQuery\u003e();\n    services.AddTransient\u003cIDroidRepository, DroidRepository\u003e();\n}\n// ...\n```\n\n\n* Use constructor injection of StarWarsQuery in GraphQLController\n```csharp\n// ...\npublic class GraphQLController : Controller\n{\n    private StarWarsQuery _starWarsQuery { get; set; }\n\n    public GraphQLController(StarWarsQuery starWarsQuery)\n    {\n        _starWarsQuery = starWarsQuery;\n    }\n\n    [HttpPost]\n    public async Task\u003cIActionResult\u003e Post([FromBody] GraphQLQuery query)\n    {\n        var schema = new Schema { Query = _starWarsQuery };\n// ...\n```\n\n* Add Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer Nuget packages to StarWars.Data project\n![entity-framework-core-nuget](https://cloud.githubusercontent.com/assets/8171434/22892209/0ff54e92-f212-11e6-9c95-e55f103b6c79.png)\n![entity-framework-sql-server-provider-nuget](https://cloud.githubusercontent.com/assets/8171434/22892268/39ac8b24-f212-11e6-805b-8462ce9a5c02.png)\n\n* Create StarWarsContext\n```csharp\nusing Microsoft.EntityFrameworkCore;\nusing StarWars.Core.Models;\n\nnamespace StarWars.Data.EntityFramework\n{\n    public class StarWarsContext : DbContext\n    {\n        public StarWarsContext(DbContextOptions options)\n            : base(options)\n        {\n            Database.EnsureCreated();\n        }\n\n        public DbSet\u003cDroid\u003e Droids { get; set; }\n    }\n}\n```\n\n* Update 'appsetting.json' with database connection\n```json\n{\n  \"Logging\": {\n    \"IncludeScopes\": false,\n    \"LogLevel\": {\n      \"Default\": \"Debug\",\n      \"System\": \"Information\",\n      \"Microsoft\": \"Information\"\n    }\n  },\n  \"ConnectionStrings\": {\n    \"StarWarsDatabaseConnection\": \"Data Source=(localdb)\\\\MSSQLLocalDB;Initial Catalog=StarWars;Integrated Security=SSPI;integrated security=true;MultipleActiveResultSets=True;\"\n  }\n}\n```\n\n* Create EF droid repository\n```csharp\nusing StarWars.Core.Data;\nusing System.Threading.Tasks;\nusing StarWars.Core.Models;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace StarWars.Data.EntityFramework.Repositories\n{\n    public class DroidRepository : IDroidRepository\n    {\n        private StarWarsContext _db { get; set; }\n\n        public DroidRepository(StarWarsContext db)\n        {\n            _db = db;\n        }\n\n        public Task\u003cDroid\u003e Get(int id)\n        {\n            return _db.Droids.FirstOrDefaultAsync(droid =\u003e droid.Id == id);\n        }\n    }\n}\n```\n\n* Create seed data as an extension to StarWarsContext\n```csharp\nusing StarWars.Core.Models;\nusing System.Linq;\n\nnamespace StarWars.Data.EntityFramework.Seed\n{\n    public static class StarWarsSeedData\n    {\n        public static void EnsureSeedData(this StarWarsContext db)\n        {\n            if (!db.Droids.Any())\n            {\n                var droid = new Droid\n                {\n                    Name = \"R2-D2\"\n                };\n                db.Droids.Add(droid);\n                db.SaveChanges();\n            }\n        }\n    }\n}\n```\n\n* Configure dependency injection and run data seed in Startup.cs\n```csharp\n// ...\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddMvc();\n\n    services.AddTransient\u003cStarWarsQuery\u003e();\n    services.AddTransient\u003cIDroidRepository, DroidRepository\u003e();\n    services.AddDbContext\u003cStarWarsContext\u003e(options =\u003e \n        options.UseSqlServer(Configuration[\"ConnectionStrings:StarWarsDatabaseConnection\"])\n    );\n}\n\npublic void Configure(IApplicationBuilder app, IHostingEnvironment env,\n                        ILoggerFactory loggerFactory, StarWarsContext db)\n{\n    loggerFactory.AddConsole(Configuration.GetSection(\"Logging\"));\n    loggerFactory.AddDebug();\n\n    app.UseMvc();\n\n    db.EnsureSeedData();\n}\n// ...\n```\n* Run application and make sure database is created\n![ssms-starwars-database-created](https://cloud.githubusercontent.com/assets/8171434/22923521/1fb046da-f2a2-11e6-8e56-c00661e27318.png)\n\n* Final test using Postman\n![postman-test-query](https://cloud.githubusercontent.com/assets/8171434/22866705/17985b54-f17b-11e6-848c-6482b45e4934.png)\n\n#### Entity Framework Migrations\n\n* Add 'Microsoft.EntityFrameworkCore.Design' NuGet package to 'StarWars.Data' project\n![ef-design-nuget](https://cloud.githubusercontent.com/assets/8171434/22964859/fde4e42a-f35a-11e6-89ad-1b5dfeda4e67.png)\n\n* Add 'Microsoft.EntityFrameworkCore.Tools.DotNet' NuGet package to 'StarWars.Data' project\n![ef-tools-dotnet-nuget](https://cloud.githubusercontent.com/assets/8171434/22964861/ff9afdf4-f35a-11e6-8e42-87ed30009877.png)\n\n* Add tools section in project.json (StarWars.Data)\n```json\n\"tools\": {\n    \"Microsoft.EntityFrameworkCore.Tools.DotNet\": \"1.1.0-preview4-final\"\n}\n```\n* Add official workaround for problems with targeting class library (Modify your class library to be a startup application)\n    * Add main entry point\n    ```csharp\n    namespace StarWars.Data.EntityFramework.Workaround\n    {\n        // WORKAROUND: https://docs.efproject.net/en/latest/miscellaneous/cli/dotnet.html#targeting-class-library-projects-is-not-supported\n        public static class Program\n        {\n            public static void Main() { }\n        }\n    }\n    ```\n    * Add build option in project.json\n    ```json\n    \"buildOptions\": {\n        \"emitEntryPoint\": true\n    }\n    ```\n* Run migrations command from the console\n```\ndotnet ef migrations add Inital -o .\\EntityFramework\\Migrations\n```\n![dotnet-ef-migrations](https://cloud.githubusercontent.com/assets/8171434/22965430/d8d02eda-f35d-11e6-8425-6edeaeaa88c9.png)\n\n#### Grahp*i*QL\n\n* Add NPM configuration file 'package.json' to StarWars.Api project\n![npm-configuration-file](https://cloud.githubusercontent.com/assets/8171434/22973053/b6e4c0a0-f37c-11e6-898b-938c50bd2f83.png)\n\n* Add GraphiQL dependencies and webpack bundle task\n```json\n{\n  \"version\": \"1.0.0\",\n  \"name\": \"starwars-graphiql\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"webpack --progress\"\n  },\n  \"dependencies\": {\n    \"graphiql\": \"^0.7.8\",\n    \"graphql\": \"^0.7.0\",\n    \"isomorphic-fetch\": \"^2.1.1\",\n    \"react\": \"^15.3.1\",\n    \"react-dom\": \"^15.3.1\"\n  },\n  \"devDependencies\": {\n    \"babel\": \"^5.6.14\",\n    \"babel-loader\": \"^5.3.2\",\n    \"css-loader\": \"^0.24.0\",\n    \"extract-text-webpack-plugin\": \"^1.0.1\",\n    \"postcss-loader\": \"^0.10.1\",\n    \"style-loader\": \"^0.13.1\",\n    \"webpack\": \"^1.13.0\"\n  }\n}\n```\n\n* Add webpack configuration 'webpack.config.js'\n```javascript\nvar webpack = require('webpack');\nvar ExtractTextPlugin = require('extract-text-webpack-plugin');\n\nvar output = './wwwroot';\n\nmodule.exports = {\n    entry: {\n        'bundle': './Scripts/app.js'\n    },\n\n    output: {\n        path: output,\n        filename: '[name].js'\n    },\n\n    resolve: {\n        extensions: ['', '.js', '.json']\n    },\n\n    module: {\n        loaders: [\n          { test: /\\.js/, loader: 'babel', exclude: /node_modules/ },\n          { test: /\\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader') }\n        ]\n    },\n\n    plugins: [\n      new ExtractTextPlugin('style.css', { allChunks: true })\n    ]\n};\n```\n\n* Install 'NPM Task Runner' extension\n![npm-task-runner](https://cloud.githubusercontent.com/assets/8171434/22973925/2a6cd7ee-f380-11e6-89b8-b27fe68a6d1b.png)\n\n* Configure 'After Build' step in 'Task Runner Explorer'\n![after-build-task-runner-explorer](https://cloud.githubusercontent.com/assets/8171434/22974769/25727768-f384-11e6-96ca-8670b67b805c.png)\n```json\n  \"-vs-binding\": { \"AfterBuild\": [ \"start\" ] }\n```\n\n* Add 'Get' action to GraphQL controller and GraphiQL view (~/Views/GraphQL/index.cshtml)\n```csharp\n// ...\npublic class GraphQLController : Controller\n{\n    // ...\n    [HttpGet]\n    public IActionResult Index()\n    {\n        return View();\n    }    \n// ...\n}\n```\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width\" /\u003e\n    \u003ctitle\u003eGraphiQL\u003c/title\u003e\n    \u003clink rel=\"stylesheet\" href=\"~/style.css\" /\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003cdiv id=\"app\"\u003e\u003c/div\u003e\n    \u003cscript src=\"~/bundle.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n* Add GraphiQL scripts and styles (app.js and app.css to ~/GraphiQL)\n    * app.js\n    ```javascript\n    import React from 'react';\n    import ReactDOM from 'react-dom';\n    import GraphiQL from 'graphiql';\n    import fetch from 'isomorphic-fetch';\n    import 'graphiql/graphiql.css';\n    import './app.css';\n\n    function graphQLFetcher(graphQLParams) {\n        return fetch(window.location.origin + '/graphql', {\n            method: 'post',\n            headers: { 'Content-Type': 'application/json' },\n            body: JSON.stringify(graphQLParams)\n        }).then(response =\u003e response.json());\n    }\n\n    ReactDOM.render(\u003cGraphiQL fetcher={graphQLFetcher}/\u003e, document.getElementById('app'));\n    ```\n    * app.css\n    ```css\n    html, body {\n        height: 100%;\n        margin: 0;\n        overflow: hidden;\n        width: 100%;\n    }\n\n    #app {\n        height: 100vh;\n    }\n    ```\n\n* Add static files support\n    * Add 'Microsoft.AspNetCore.StaticFiles' NuGet\n    ![static-files-nuget](https://cloud.githubusercontent.com/assets/8171434/22978442/fa3872d2-f392-11e6-8839-634f380fee51.png)\n\n    * Update configuration in 'Startup.cs'\n    ```csharp\n    // ...\n    public void Configure(IApplicationBuilder app, IHostingEnvironment env,\n                              ILoggerFactory loggerFactory, StarWarsContext db)\n    {\n        loggerFactory.AddConsole(Configuration.GetSection(\"Logging\"));\n        loggerFactory.AddDebug();\n\n        app.UseStaticFiles();\n        app.UseMvc();\n\n        db.EnsureSeedData();\n    }\n    // ...\n    ```\n* Build project and check if bundles were created by webpack under ~/wwwroot\n![bundles-created-by-webpack](https://cloud.githubusercontent.com/assets/8171434/22978874/b01d6372-f394-11e6-80eb-8b3f8fbe7ad0.png)\n\n* Run project and enjoy Graph*i*QL\n![graphiql](https://cloud.githubusercontent.com/assets/8171434/22978970/fe8700fe-f394-11e6-9821-f1d0606a2b9b.png)\n\n#### Unit Tests\n\n* Create 'Class Library (.NET Core)' type 'StarWars.Tests.Unit' project\n![unit-tests-project](https://cloud.githubusercontent.com/assets/8171434/22991495/ae9ead08-f3bc-11e6-83e0-17ee0015823f.png)\n\n* Install 'xunit' NuGet package in StarWars.Tests.Unit project\n![xunit-nuget](https://cloud.githubusercontent.com/assets/8171434/23019197/6236a94a-f441-11e6-86fd-2d2195366d2a.png)\n\n* Install 'dotnet-test-xunit' NuGet package in StarWars.Tests.Unit project\n![dotnet-test-xunit-nuget](https://cloud.githubusercontent.com/assets/8171434/23020067/94a2a3da-f445-11e6-8674-0b33847435c1.png)\n\n* Make changes to project.json\n    * Set 'testRunner'\n    * Reference 'StarWars.Data' project\n    * Set 'runtimes'\n```json\n{\n  \"version\": \"1.0.0-*\",\n  \"testRunner\": \"xunit\",\n  \"dependencies\": {\n    \"dotnet-test-xunit\": \"2.2.0-preview2-build1029\",\n    \"Microsoft.NETCore.App\": \"1.1.0\",\n    \"xunit\": \"2.1.0\",\n    \"StarWars.Data\": {\n      \"target\": \"project\"\n    }\n  },\n\n  \"frameworks\": {\n    \"netcoreapp1.1\": {\n      \"imports\": [\n        \"dotnet5.6\",\n        \"portable-net45+win8\"\n      ]\n    }\n  },\n\n  \"runtimes\": {\n    \"win10-x64\": {}\n  }\n}\n```\n\n* Create first test for in memory droid repository\n```csharp\nusing StarWars.Data.InMemory;\nusing Xunit;\n\nnamespace StarWars.Tests.Unit.Data.InMemory\n{\n    public class DroidRepositoryShould\n    {\n        private readonly DroidRepository _droidRepository;\n        public DroidRepositoryShould()\n        {\n            // Given\n            _droidRepository = new DroidRepository();\n        }\n\n        [Fact]\n        public async void ReturnR2D2DroidGivenIdOf1()\n        {\n            // When\n            var droid = await _droidRepository.Get(1);\n\n            // Then\n            Assert.NotNull(droid);\n            Assert.Equal(\"WRONG_NAME\", droid.Name);\n        }\n    }\n}\n```\n\n* Build and make sure that test is discovered by 'Test Explorer'\n![test-explorer-first-unit-test](https://cloud.githubusercontent.com/assets/8171434/23020737/c1b268ee-f448-11e6-96de-80928174a335.png)\n\n* Run test - it should fail (we want to make sure that we are testing the right thing)\n![first-unit-test-fail](https://cloud.githubusercontent.com/assets/8171434/23022083/56733e6c-f44f-11e6-8f64-84e9da06d879.png)\n\n* Fix test\n```csharp\n// ...\n[Fact]\npublic async void ReturnR2D2DroidGivenIdOf1()\n{\n    // When\n    var droid = await _droidRepository.Get(1);\n\n    // Then\n    Assert.NotNull(droid);\n    Assert.Equal(\"R2-D2\", droid.Name);\n}\n// ...\n```\n* Run test again - it should pass\n![first-unit-test-pass](https://cloud.githubusercontent.com/assets/8171434/23022004/ff769a14-f44e-11e6-90d8-12d33eaac801.png)\n\n* Install 'Moq' NuGet package\n![moq-nuget](https://cloud.githubusercontent.com/assets/8171434/23029811/03d08832-f46c-11e6-8b63-1d458e16c37d.png)\n\n* Install 'Microsoft.EntityFrameworkCore.InMemory' NuGet package\n![ef-in-memory-nuget](https://cloud.githubusercontent.com/assets/8171434/23035364/1c0eb1f4-f47f-11e6-852d-765a4e2d4a0e.png)\n\n* Add reference to 'StarWars.Core' in project.json\n```json\n{\n  \"dependencies\": {\n    \"StarWars.Core\": {\n      \"target\": \"project\"\n    }\n  }\n}\n```\n\n* Create EF droid repository unit test\n```csharp\nusing Microsoft.EntityFrameworkCore;\nusing StarWars.Core.Models;\nusing StarWars.Data.EntityFramework;\nusing StarWars.Data.EntityFramework.Repositories;\nusing Xunit;\n\nnamespace StarWars.Tests.Unit.Data.EntityFramework.Repositories\n{\n    public class DroidRepositoryShould\n    {\n        private readonly DroidRepository _droidRepository;\n        public DroidRepositoryShould()\n        {\n            // Given\n            // https://docs.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory\n            var options = new DbContextOptionsBuilder\u003cStarWarsContext\u003e()\n                .UseInMemoryDatabase(databaseName: \"StarWars\")\n                .Options;\n            using (var context = new StarWarsContext(options))\n            {\n                context.Droids.Add(new Droid { Id = 1, Name = \"R2-D2\" });\n                context.SaveChanges();\n            }\n            var starWarsContext = new StarWarsContext(options);\n            _droidRepository = new DroidRepository(starWarsContext);\n        }\n\n        [Fact]\n        public async void ReturnR2D2DroidGivenIdOf1()\n        {\n            // When\n            var droid = await _droidRepository.Get(1);\n\n            // Then\n            Assert.NotNull(droid);\n            Assert.Equal(\"R2-D2\", droid.Name);\n        }\n    }\n}\n```\n\n* Create GraphQLController unit test\n    * First refactor controller to be more testable by using constructor injection\n    ```csharp\n    using GraphQL;\n    using GraphQL.Types;\n    using Microsoft.AspNetCore.Mvc;\n    using StarWars.Api.Models;\n    using System.Threading.Tasks;\n\n    namespace StarWars.Api.Controllers\n    {\n        [Route(\"graphql\")]\n        public class GraphQLController : Controller\n        {\n            private IDocumentExecuter _documentExecuter { get; set; }\n            private ISchema _schema { get; set; }\n\n            public GraphQLController(IDocumentExecuter documentExecuter, ISchema schema)\n            {\n                _documentExecuter = documentExecuter;\n                _schema = schema;\n            }\n\n            [HttpGet]\n            public IActionResult Index()\n            {\n                return View();\n            }\n\n            [HttpPost]\n            public async Task\u003cIActionResult\u003e Post([FromBody] GraphQLQuery query)\n            {\n                var executionOptions = new ExecutionOptions { Schema = _schema, Query = query.Query };\n                var result = await _documentExecuter.ExecuteAsync(executionOptions).ConfigureAwait(false);\n\n                if (result.Errors?.Count \u003e 0)\n                {\n                    return BadRequest(result.Errors);\n                }\n\n                return Ok(result);\n            }\n        }\n    }\n    ```\n    * Configure dependency injection in 'Startup.cs'\n    ```csharp\n    // ...\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // ...        \n        services.AddTransient\u003cIDocumentExecuter, DocumentExecuter\u003e();\n        var sp = services.BuildServiceProvider();\n        services.AddTransient\u003cISchema\u003e(_ =\u003e new Schema { Query = sp.GetService\u003cStarWarsQuery\u003e() });\n    }\n    // ...\n    ```\n    * Create test for 'Index' and 'Post' actions\n    ```csharp\n    using GraphQL;\n    using GraphQL.Types;\n    using Microsoft.AspNetCore.Mvc;\n    using Moq;\n    using StarWars.Api.Controllers;\n    using StarWars.Api.Models;\n    using System.Threading.Tasks;\n    using Xunit;\n\n    namespace StarWars.Tests.Unit.Api.Controllers\n    {\n        public class GraphQLControllerShould\n        {\n            private GraphQLController _graphqlController { get; set; }\n\n            public GraphQLControllerShould()\n            {\n                // Given\n                var documentExecutor = new Mock\u003cIDocumentExecuter\u003e();\n                documentExecutor.Setup(x =\u003e x.ExecuteAsync(It.IsAny\u003cExecutionOptions\u003e())).Returns(Task.FromResult(new ExecutionResult()));\n                var schema = new Mock\u003cISchema\u003e();\n                _graphqlController = new GraphQLController(documentExecutor.Object, schema.Object);\n            }\n\n            [Fact]\n            public void ReturnNotNullViewResult()\n            {\n                // When\n                var result = _graphqlController.Index() as ViewResult;\n\n                // Then\n                Assert.NotNull(result);\n                Assert.IsType\u003cViewResult\u003e(result);\n            }\n\n            [Fact]\n            public async void ReturnNotNullExecutionResult()\n            {\n                // Given\n                var query = new GraphQLQuery { Query = @\"{ \"\"query\"\": \"\"query { hero { id name } }\"\"\" };\n\n                // When\n                var result = await _graphqlController.Post(query);\n\n                // Then\n                Assert.NotNull(result);\n                var okObjectResult =  Assert.IsType\u003cOkObjectResult\u003e(result);\n                var executionResult = okObjectResult.Value;\n                Assert.NotNull(executionResult);\n            }\n        }\n    }\n    ```\n\n#### Visual Studio 2017 RC upgrade\n\n* Open solution in VS 2017 and let the upgrade tool do the job\n![vs-2017-rc-upgrade](https://cloud.githubusercontent.com/assets/8171434/23065886/0d9aae72-f518-11e6-8f60-f9c226c6b49a.png)\n\n* Upgrade of 'StarWars.Tests.Unit' failed, so I had to remove all project dependencies and reload it\n```json\n{\n  \"dependencies\": {\n    // remove this:\n    \"StarWars.Data\": {\n      \"target\": \"project\"\n    },\n    \"StarWars.Core\": {\n      \"target\": \"project\"\n    }\n    // ...\n  }\n}\n```\n\n* Replace old test txplorer runner for the xUnit.net framework (dotnet-test-xunit) with new one (xunit.runner.visualstudio)\n![xunit-runner-visualstudio-nuget](https://cloud.githubusercontent.com/assets/8171434/23066060/e808053c-f518-11e6-8426-5398565ff8a1.png)\n\n* Install (xunit.runner.visualstudio) dependency (Microsoft.DotNet.InternalAbstractions)\n![microsoft-dotnet-internal-abstractions-nuget](https://cloud.githubusercontent.com/assets/8171434/23066097/24ff49f0-f519-11e6-9a1c-8f6645bf66a4.png)\n\n#### Integration Tests\n\n* Create 'xUnit Test Project (.NET Core)' type 'StarWars.Tests.Integration' project\n![integration-tests-project](https://cloud.githubusercontent.com/assets/8171434/23093375/6b3f9fe6-f5e1-11e6-8d4c-d89f16ef3204.png)\n\n* Change target framework from 'netcoreapp1.0' to 'netcoreapp1.1'\n```xml\n\u003cProject Sdk=\"Microsoft.NET.Sdk\"\u003e\n  \u003cPropertyGroup\u003e    \n    \u003cTargetFramework\u003enetcoreapp1.1\u003c/TargetFramework\u003e\n  \u003c/PropertyGroup\u003e\n  \u003c!--...--\u003e\n\u003c/Project\u003e\n```\n\n* Install 'Microsoft.AspNetCore.TestHost' NuGet package\n![test-host-nuget](https://cloud.githubusercontent.com/assets/8171434/23100598/a5576138-f685-11e6-9ba1-aa04e9a31917.png)\n\n* Use EF in memory database for 'Test' evironment\n    * Install 'Microsoft.EntityFrameworkCore.InMemory' NuGet package\n    ![ef-in-memory-nuget-api-project](https://cloud.githubusercontent.com/assets/8171434/23127639/14204be2-f77c-11e6-81de-336d88cb5db7.png)\n\n    * Configure it in 'Startup.cs'\n    ```csharp\n    // ...\n    private IHostingEnvironment Env { get; set; }\n\n    public class Startup\n    {\n        // ...\n        Env = env;\n    }\n\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // ...\n        if (Env.IsEnvironment(\"Test\"))\n        {\n            services.AddDbContext\u003cStarWarsContext\u003e(options =\u003e\n                options.UseInMemoryDatabase(databaseName: \"StarWars\"));\n        }\n        else\n        {\n            services.AddDbContext\u003cStarWarsContext\u003e(options =\u003e\n                options.UseSqlServer(Configuration[\"ConnectionStrings:StarWarsDatabaseConnection\"]));\n        }\n        // ...\n    }\n    // ...\n    ```\n* Create integration test for GraphQL query (POST)\n```csharp\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.TestHost;\nusing StarWars.Api;\nusing System.Net.Http;\nusing System.Text;\nusing Xunit;\n\nnamespace StarWars.Tests.Integration.Api.Controllers\n{\n    public class GraphQLControllerShould\n    {\n        private readonly TestServer _server;\n        private readonly HttpClient _client;\n\n        public GraphQLControllerShould()\n        {\n            _server = new TestServer(new WebHostBuilder()\n                .UseEnvironment(\"Test\")\n                .UseStartup\u003cStartup\u003e()\n            );\n            _client = _server.CreateClient();\n        }\n\n        [Fact]\n        public async void ReturnR2D2Droid()\n        {\n            // Given\n            var query = @\"{\n                \"\"query\"\": \"\"query { hero { id name } }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.Contains(\"R2-D2\", responseString);\n        }\n    }\n}\n```\n#### Logs\n\n* Make sure that logger is configured in Startup.cs\n```csharp\npublic void Configure(IApplicationBuilder app, IHostingEnvironment env,\n                              ILoggerFactory loggerFactory, StarWarsContext db)\n{\n    loggerFactory.AddConsole(Configuration.GetSection(\"Logging\"));\n    loggerFactory.AddDebug();\n    // ...\n}\n```\n\n* Override ToString method of GraphQLQuery class\n```csharp\npublic override string ToString()\n{\n    var builder = new StringBuilder();\n    builder.AppendLine();\n    if (!string.IsNullOrWhiteSpace(OperationName))\n    {\n        builder.AppendLine($\"OperationName = {OperationName}\");\n    }\n    if (!string.IsNullOrWhiteSpace(NamedQuery))\n    {\n        builder.AppendLine($\"NamedQuery = {NamedQuery}\");\n    }\n    if (!string.IsNullOrWhiteSpace(Query))\n    {\n        builder.AppendLine($\"Query = {Query}\");\n    }\n    if (!string.IsNullOrWhiteSpace(Variables))\n    {\n        builder.AppendLine($\"Variables = {Variables}\");\n    }\n\n    return builder.ToString();\n}\n```\n* Add logger to GraphQLController\n```csharp\npublic class GraphQLController : Controller\n{\n    // ...\n    private readonly ILogger _logger;\n\n    public GraphQLController(IDocumentExecuter documentExecuter, ISchema schema, ILogger\u003cGraphQLController\u003e logger)\n    {\n        // ...\n        _logger = logger;\n    }\n\n    [HttpGet]\n    public IActionResult Index()\n    {\n        _logger.LogInformation(\"Got request for GraphiQL. Sending GUI back\");\n        return View();\n    }\n\n    [HttpPost]\n    public async Task\u003cIActionResult\u003e Post([FromBody] GraphQLQuery query)\n    {\n        // ...\n        if (result.Errors?.Count \u003e 0)\n        {\n            _logger.LogError(\"GraphQL errors: {0}\", result.Errors);\n            return BadRequest(result);\n        }\n\n        _logger.LogDebug(\"GraphQL execution result: {result}\", JsonConvert.SerializeObject(result.Data));\n        return Ok(result);\n    }\n}\n```\n\n* Add logger to DroidRepository\n```csharp\nnamespace StarWars.Data.EntityFramework.Repositories\n{\n    public class DroidRepository : IDroidRepository\n    {\n        private StarWarsContext _db { get; set; }\n        private readonly ILogger _logger;\n\n        public DroidRepository(StarWarsContext db, ILogger\u003cDroidRepository\u003e logger)\n        {\n            _db = db;\n            _logger = logger;\n        }\n\n        public Task\u003cDroid\u003e Get(int id)\n        {\n            _logger.LogInformation(\"Get droid with id = {id}\", id);\n            return _db.Droids.FirstOrDefaultAsync(droid =\u003e droid.Id == id);\n        }\n    }\n}\n```\n\n* Add logger to StarWarsContext\n```csharp\nnamespace StarWars.Data.EntityFramework\n{\n    public class StarWarsContext : DbContext\n    {\n        public readonly ILogger _logger;\n\n        public StarWarsContext(DbContextOptions options, ILogger\u003cStarWarsContext\u003e logger)\n            : base(options)\n        {\n            _logger = logger;\n            // ...\n        }\n    }\n}\n```\n\n* Add logger to StarWarsSeedData\n```csharp\nnamespace StarWars.Data.EntityFramework.Seed\n{\n    public static class StarWarsSeedData\n    {\n        public static void EnsureSeedData(this StarWarsContext db)\n        {\n            db._logger.LogInformation(\"Seeding database\");\n            if (!db.Droids.Any())\n            {\n                db._logger.LogInformation(\"Seeding droids\");\n                // ...\n            }\n        }\n    }\n}\n```\n\n* Fix controller unit test\n```csharp\npublic class GraphQLControllerShould\n{\n    public GraphQLControllerShould()\n    {\n        // ...\n        var logger = new Mock\u003cILogger\u003cGraphQLController\u003e\u003e();\n        _graphqlController = new GraphQLController(documentExecutor.Object, schema.Object, logger.Object);\n    }\n    // ...\n}\n```\n\n* Fix repository unit test\n```csharp\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.Logging;\nusing Moq;\nusing StarWars.Core.Models;\nusing StarWars.Data.EntityFramework;\nusing StarWars.Data.EntityFramework.Repositories;\nusing Xunit;\n\nnamespace StarWars.Tests.Unit.Data.EntityFramework.Repositories\n{\n    public class DroidRepositoryShould\n    {\n        public DroidRepositoryShould()\n        {\n            var dbLogger = new Mock\u003cILogger\u003cStarWarsContext\u003e\u003e();\n            // ...\n            using (var context = new StarWarsContext(options, dbLogger.Object))\n            {\n                // ...\n            }\n            // ...\n            var repoLogger = new Mock\u003cILogger\u003cDroidRepository\u003e\u003e();\n            _droidRepository = new DroidRepository(starWarsContext, repoLogger.Object);\n        }\n    }\n}\n```\n\n* Enjoy console logs\n![console-logs-1](https://cloud.githubusercontent.com/assets/8171434/23155559/6b17afe8-f813-11e6-88e0-2923270f4ee8.png)\n![console-logs-2](https://cloud.githubusercontent.com/assets/8171434/23155802/68f8e604-f814-11e6-90a7-44d09e851a51.png)\n\n#### Code Coverage\n\n* Install OpenCover NuGet package\n![open-cover-nuget](https://cloud.githubusercontent.com/assets/8171434/23206746/247aa694-f8ef-11e6-9b14-d0ade0453b94.png)\n\n* Add path to OpenCover tools to 'Path' environment variable. In my case it was:\n```\nC:\\Users\\Jacek_Kosciesza\\.nuget\\packages\\opencover\\4.6.519\\tools\\\n```\n\n* Set 'Full' debug type in all projects (StarWars.Api.csproj, StarWars.Core.csproj, StarWars.Data.csproj). This is needed to produce *.pdb files which are understandable by OpenCover.\n```xml\n\u003cProject Sdk=\"Microsoft.NET.Sdk.Web\"\u003e\n  \u003cPropertyGroup\u003e\n    \u003c!--...--\u003e\n    \u003cDebugType\u003eFull\u003c/DebugType\u003e\n  \u003c/PropertyGroup\u003e\n  \u003c!--...--\u003e\n\u003c/Project\u003e\n```\n* Run OpenCover in the console\n```\nOpenCover.Console.exe\n    -target:\"dotnet.exe\"\n    -targetargs:\"test -f netcoreapp1.1 -c Release Tests/StarWars.Tests.Unit/StarWars.Tests.Unit.csproj\"\n    -hideskipped:File\n    -output:coverage/unit/coverage.xml\n    -oldStyle\n    -filter:\"+[StarWars*]* -[StarWars.Tests*]* -[StarWars.Api]*Program -[StarWars.Api]*Startup -[StarWars.Data]*EntityFramework.Workaround.Program -[StarWars.Data]*EntityFramework.Migrations* -[StarWars.Data]*EntityFramework.Seed*\"\n    -searchdirs:\"Tests/StarWars.Tests.Unit/bin/Release/netcoreapp1.1\"\n    -register:user\n```\n![open-cover-unit-tests-results](https://cloud.githubusercontent.com/assets/8171434/23207392/70e8c8b0-f8f1-11e6-8284-2c6a1ead0f0c.png)\n\n* Install 'ReportGenerator' NuGet package\n![report-generator-nuget](https://cloud.githubusercontent.com/assets/8171434/23207452/a84e4e1a-f8f1-11e6-87fb-a28888f59ce8.png)\n\n* Create simple script (unit-tests.bat)\n```\nmkdir coverage\\unit\nOpenCover.Console.exe -target:\"dotnet.exe\" -targetargs:\"test -f netcoreapp1.1 -c Release Tests/StarWars.Tests.Unit/StarWars.Tests.Unit.csproj\" -hideskipped:File -output:coverage/unit/coverage.xml -oldStyle -filter:\"+[StarWars*]* -[StarWars.Tests*]* -[StarWars.Api]*Program -[StarWars.Api]*Startup -[StarWars.Data]*EntityFramework.Workaround.Program -[StarWars.Data]*EntityFramework.Migrations* -[StarWars.Data]*EntityFramework.Seed*\" -searchdirs:\"Tests/StarWars.Tests.Unit/bin/Release/netcoreapp1.1\" -register:user\nReportGenerator.exe -reports:coverage/unit/coverage.xml -targetdir:coverage/unit -verbosity:Error\nstart .\\coverage\\unit\\index.htm\n```\n\n* Enjoy HTML based code coverage report\n![open-cover-html-report-results](https://cloud.githubusercontent.com/assets/8171434/23207608/2d314e7a-f8f2-11e6-81bc-1a91d1254c78.png)\n \n #### Continous Integration\n\n* Create new project in VSTS (Visual Studio Team Services)\n![vsts-new-project](https://cloud.githubusercontent.com/assets/8171434/24324212/6dd943b2-1182-11e7-915f-0a62521556bf.png)\n\n* Create new build definition \"ASP.NET Core Preview\". Select GitHub, Hosted VS2017 default agent queue and continous integration. ~~At the moment hosted agents don't support *.csproj based .NET Core projects, so we have to wait for a while, see this issue: [Support for .NET Core .csproj files? #3311](https://github.com/Microsoft/vsts-tasks/issues/3311)~~\n![vsts-new-build-definition](https://cloud.githubusercontent.com/assets/8171434/24324292/feb86ca4-1183-11e7-8ea0-b021338ae17f.png)\n![vsts-new-build-definition-github-hosted-vs2017](https://cloud.githubusercontent.com/assets/8171434/24324328/c113efa8-1184-11e7-9864-045987b75bf3.png)\n\n* Add new GitHub service connection\n![vsts-new-github-service-connection](https://cloud.githubusercontent.com/assets/8171434/24324400/206429ea-1186-11e7-95ae-6ecc28cbda53.png)\n\n* Setup repository\n![vsts-setup-repository](https://cloud.githubusercontent.com/assets/8171434/24324427/7b83e234-1186-11e7-85cc-84cb61f33b0b.png)\n\n* Switch to \"New Build Editor\"\n![vsts-new-build-editor](https://cloud.githubusercontent.com/assets/8171434/24324617/e08df05e-1189-11e7-89d4-3e3f6ba8da5b.png)\n\n* Setup build process (tasks, build steps)\n![vsts-setup-build-steps](https://cloud.githubusercontent.com/assets/8171434/24324633/2bd86ed6-118a-11e7-8cdd-a69dd1b5f75d.png)\n\n* Setup projects in Test build step\n```\n**/Tests/StarWars.Tests.Unit/StarWars.Tests.Unit.csproj;**/Tests/StarWars.Tests.Integration/StarWars.Tests.Integration.csproj\n```\n![vsts-setup-projects-test-build-setp](https://cloud.githubusercontent.com/assets/8171434/24324637/5d4dd30c-118a-11e7-873d-1f22cd6daca1.png)\n\n * Queue build. Make sure it succeeded and executed unit and integration tests.\n![vsts-queue-build](https://cloud.githubusercontent.com/assets/8171434/24324564/ee7b285e-1188-11e7-89c8-674f498b43d6.png)\n![vsts-build-succeeded](https://cloud.githubusercontent.com/assets/8171434/24324585/52193400-1189-11e7-84c4-9cc21659fb6e.png)\n\n* Enable build badge (after save you will see link to build status image).\n![vsts-enable-badge](https://cloud.githubusercontent.com/assets/8171434/24324660/c6927c82-118a-11e7-8d6d-0a105306b40f.png)\n\n\n### Advanced\n\n#### Full 'Star Wars' database (see [Facebook GraphQL](https://github.com/facebook/graphql) and [GraphQL.js](https://github.com/graphql/graphql-js))\n\n* Create models\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Episode\n    {\n        public int  Id  { get; set; }\n        public string Title { get; set; }\n        public virtual ICollection\u003cCharacterEpisode\u003e CharacterEpisodes { get; set; }\n    }\n}\n```\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Planet\n    {\n        public int Id { get; set; }\n        public string Name { get; set; }\n        public ICollection\u003cHuman\u003e Humans { get; set; }\n    }\n}\n```\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Character\n    {\n        public int Id { get; set; }\n        public string Name { get; set; }\n        public virtual ICollection\u003cCharacterEpisode\u003e CharacterEpisodes { get; set; }\n        public virtual ICollection\u003cCharacterFriend\u003e CharacterFriends { get; set; }\n        public virtual ICollection\u003cCharacterFriend\u003e FriendCharacters { get; set; }\n    }\n}\n```\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class CharacterEpisode\n    {\n        public int CharacterId { get; set; }\n        public Character Character { get; set; }\n\n        public int EpisodeId { get; set; }\n        public Episode Episode { get; set; }\n    }\n}\n```\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class CharacterFriend\n    {\n        public int CharacterId { get; set; }\n        public Character Character { get; set; }\n\n        public int FriendId { get; set; }\n        public Character Friend { get; set; }\n    }\n}\n```\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Droid : Character\n    {\n        public string PrimaryFunction { get; set; }\n    }\n}\n```\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Human : Character\n    {\n        public Planet HomePlanet { get; set; }\n    }\n}\n```\n\n* Update StarWarsContext\n```csharp\nnamespace StarWars.Data.EntityFramework\n{\n    public class StarWarsContext : DbContext\n    {\n        // ...\n        protected override void OnModelCreating(ModelBuilder modelBuilder)\n        {\n            // https://docs.microsoft.com/en-us/ef/core/modeling/relationships\n            // http://stackoverflow.com/questions/38520695/multiple-relationships-to-the-same-table-in-ef7core\n\n            // episodes\n            modelBuilder.Entity\u003cEpisode\u003e().HasKey(c =\u003e c.Id);\n            modelBuilder.Entity\u003cEpisode\u003e().Property(e =\u003e e.Id).ValueGeneratedNever();\n\n            // planets\n            modelBuilder.Entity\u003cPlanet\u003e().HasKey(c =\u003e c.Id);\n            modelBuilder.Entity\u003cPlanet\u003e().Property(e =\u003e e.Id).ValueGeneratedNever();\n\n            // characters\n            modelBuilder.Entity\u003cCharacter\u003e().HasKey(c =\u003e c.Id);\n            modelBuilder.Entity\u003cCharacter\u003e().Property(e =\u003e e.Id).ValueGeneratedNever();\n\n            // characters-friends\n            modelBuilder.Entity\u003cCharacterFriend\u003e().HasKey(t =\u003e new { t.CharacterId, t.FriendId});\n\n            modelBuilder.Entity\u003cCharacterFriend\u003e()\n                .HasOne(cf =\u003e cf.Character)\n                .WithMany(c =\u003e c.CharacterFriends)\n                .HasForeignKey(cf =\u003e cf.CharacterId)                \n                .OnDelete(DeleteBehavior.Restrict);               \n\n            modelBuilder.Entity\u003cCharacterFriend\u003e()\n                .HasOne(cf =\u003e cf.Friend)\n                .WithMany(t =\u003e t.FriendCharacters)\n                .HasForeignKey(cf =\u003e cf.FriendId)\n                .OnDelete(DeleteBehavior.Restrict);\n\n            // characters-episodes\n            modelBuilder.Entity\u003cCharacterEpisode\u003e().HasKey(t =\u003e new { t.CharacterId, t.EpisodeId });\n\n            modelBuilder.Entity\u003cCharacterEpisode\u003e()\n                .HasOne(cf =\u003e cf.Character)\n                .WithMany(c =\u003e c.CharacterEpisodes)\n                .HasForeignKey(cf =\u003e cf.CharacterId)\n                .OnDelete(DeleteBehavior.Restrict);\n\n            modelBuilder.Entity\u003cCharacterEpisode\u003e()\n                .HasOne(cf =\u003e cf.Episode)\n                .WithMany(t =\u003e t.CharacterEpisodes)\n                .HasForeignKey(cf =\u003e cf.EpisodeId)\n                .OnDelete(DeleteBehavior.Restrict);\n\n            // humans\n            modelBuilder.Entity\u003cHuman\u003e().HasOne(h =\u003e h.HomePlanet).WithMany(p =\u003e p.Humans);\n        }\n\n        public virtual DbSet\u003cEpisode\u003e Episodes { get; set; }\n        public virtual DbSet\u003cPlanet\u003e Planets { get; set; }\n        public virtual DbSet\u003cCharacter\u003e Characters { get; set; }\n        public virtual DbSet\u003cCharacterFriend\u003e CharacterFriends { get; set; }\n        public virtual DbSet\u003cCharacterEpisode\u003e CharacterEpisodes { get; set; }\n        public virtual DbSet\u003cDroid\u003e Droids { get; set; }\n        public virtual DbSet\u003cHuman\u003e Humans { get; set; }\n    }\n}\n```\n\n* Update database seed data\n```csharp\nnamespace StarWars.Data.EntityFramework.Seed\n{\n    public static class StarWarsSeedData\n    {\n        public static void EnsureSeedData(this StarWarsContext db)\n        {\n            db._logger.LogInformation(\"Seeding database\");\n\n            // episodes\n            var newhope = new Episode { Id = 4, Title = \"NEWHOPE\" };\n            var empire = new Episode { Id = 5, Title = \"EMPIRE\" };\n            var jedi = new Episode { Id = 6, Title = \"JEDI\" };\n            var episodes = new List\u003cEpisode\u003e\n            {\n                newhope,\n                empire,\n                jedi,\n            };\n            if (!db.Episodes.Any())\n            {\n                db._logger.LogInformation(\"Seeding episodes\");\n                db.Episodes.AddRange(episodes);\n                db.SaveChanges();\n            }\n\n            // planets\n            var tatooine = new Planet { Id = 1, Name = \"Tatooine\" };\n            var alderaan = new Planet { Id = 2, Name = \"Alderaan\" };\n            var planets = new List\u003cPlanet\u003e\n            {\n                tatooine,\n                alderaan\n            };\n            if (!db.Planets.Any())\n            {\n                db._logger.LogInformation(\"Seeding planets\");\n                db.Planets.AddRange(planets);\n                db.SaveChanges();\n            }\n\n            // humans\n            var luke = new Human\n            {\n                Id = 1000,\n                Name = \"Luke Skywalker\",\n                CharacterEpisodes = new List\u003cCharacterEpisode\u003e\n                {\n                    new CharacterEpisode { Episode = newhope },\n                    new CharacterEpisode { Episode = empire },\n                    new CharacterEpisode { Episode = jedi }\n                },\n                HomePlanet = tatooine\n            };\n            var vader = new Human\n            {\n                Id = 1001,\n                Name = \"Darth Vader\",\n                CharacterEpisodes = new List\u003cCharacterEpisode\u003e\n                {\n                    new CharacterEpisode { Episode = newhope },\n                    new CharacterEpisode { Episode = empire },\n                    new CharacterEpisode { Episode = jedi }\n                },\n                HomePlanet = tatooine\n            };\n            var han = new Human\n            {\n                Id = 1002,\n                Name = \"Han Solo\",\n                CharacterEpisodes = new List\u003cCharacterEpisode\u003e\n                {\n                    new CharacterEpisode { Episode = newhope },\n                    new CharacterEpisode { Episode = empire },\n                    new CharacterEpisode { Episode = jedi }\n                },\n                HomePlanet = tatooine\n            };\n            var leia = new Human\n            {\n                Id = 1003,\n                Name = \"Leia Organa\",\n                CharacterEpisodes = new List\u003cCharacterEpisode\u003e\n                {\n                    new CharacterEpisode { Episode = newhope },\n                    new CharacterEpisode { Episode = empire },\n                    new CharacterEpisode { Episode = jedi }\n                },\n                HomePlanet = alderaan\n            };\n            var tarkin = new Human\n            {\n                Id = 1004,\n                Name = \"Wilhuff Tarkin\",\n                CharacterEpisodes = new List\u003cCharacterEpisode\u003e\n                {\n                    new CharacterEpisode { Episode = newhope }                    \n                },\n            };\n            var humans = new List\u003cHuman\u003e\n            {\n                luke,\n                vader,\n                han,\n                leia,\n                tarkin\n            };\n            if (!db.Humans.Any())\n            {\n                db._logger.LogInformation(\"Seeding humans\");                \n                db.Humans.AddRange(humans);\n                db.SaveChanges();\n            }\n\n            // droids\n            var threepio = new Droid\n            {\n                Id = 2000,\n                Name = \"C-3PO\",\n                CharacterEpisodes = new List\u003cCharacterEpisode\u003e\n                {\n                    new CharacterEpisode { Episode = newhope },\n                    new CharacterEpisode { Episode = empire },\n                    new CharacterEpisode { Episode = jedi }\n                },\n                PrimaryFunction = \"Protocol\"\n            };\n            var artoo = new Droid\n            {\n                Id = 2001,\n                Name = \"R2-D2\",\n                CharacterEpisodes = new List\u003cCharacterEpisode\u003e\n                {\n                    new CharacterEpisode { Episode = newhope },\n                    new CharacterEpisode { Episode = empire },\n                    new CharacterEpisode { Episode = jedi }\n                },\n                PrimaryFunction = \"Astromech\"\n            };\n            var droids = new List\u003cDroid\u003e\n            {\n                threepio,\n                artoo\n            };\n            if (!db.Droids.Any())\n            {\n                db._logger.LogInformation(\"Seeding droids\");\n                db.Droids.AddRange(droids);\n                db.SaveChanges();\n            }\n\n            // update character's friends\n            luke.CharacterFriends = new List\u003cCharacterFriend\u003e\n            {\n                new CharacterFriend { Friend = han },\n                new CharacterFriend { Friend = leia },\n                new CharacterFriend { Friend = threepio },\n                new CharacterFriend { Friend = artoo }\n            };\n            vader.CharacterFriends = new List\u003cCharacterFriend\u003e\n            {\n                new CharacterFriend { Friend = tarkin }\n            };\n            han.CharacterFriends = new List\u003cCharacterFriend\u003e\n            {\n                new CharacterFriend { Friend = luke },\n                new CharacterFriend { Friend = leia },\n                new CharacterFriend { Friend = artoo }\n            };\n            leia.CharacterFriends = new List\u003cCharacterFriend\u003e\n            {\n                new CharacterFriend { Friend = luke },\n                new CharacterFriend { Friend = han },\n                new CharacterFriend { Friend = threepio },\n                new CharacterFriend { Friend = artoo }\n            };\n            tarkin.CharacterFriends = new List\u003cCharacterFriend\u003e\n            {\n                new CharacterFriend { Friend = vader }\n            };\n            threepio.CharacterFriends = new List\u003cCharacterFriend\u003e\n            {\n                new CharacterFriend { Friend = luke },\n                new CharacterFriend { Friend = han },\n                new CharacterFriend { Friend = leia },\n                new CharacterFriend { Friend = artoo }\n            };\n            artoo.CharacterFriends = new List\u003cCharacterFriend\u003e\n            {\n                new CharacterFriend { Friend = luke },\n                new CharacterFriend { Friend = han },\n                new CharacterFriend { Friend = leia }\n            };\n            var characters = new List\u003cCharacter\u003e\n            {\n                luke,\n                vader,\n                han,\n                leia,\n                tarkin,\n                threepio,\n                artoo\n            };\n            if (!db.CharacterFriends.Any())\n            {\n                db._logger.LogInformation(\"Seeding character's friends\");\n                db.Characters.UpdateRange(characters);\n                db.SaveChanges();\n            }\n        }\n    }\n}\n```\n\n* Add 'Microsoft.EntityFrameworkCore.Tools' NuGet\n![ef-tools-nuget](https://cloud.githubusercontent.com/assets/8171434/23588252/035b1fd2-01bb-11e7-888a-11622b3b364f.png)\n\n* Set 'StarWars.Data' as a StartUp project\n\n* Add 'Full' migrations\n![ef-full-migration](https://cloud.githubusercontent.com/assets/8171434/23591647/f7e21834-01f3-11e7-9460-69baaa48e338.png)\n\n* Update database\n![ef-update-database-full](https://cloud.githubusercontent.com/assets/8171434/23591671/898a4a2c-01f4-11e7-827b-de09b09af1e1.png)\n![ef-update-database-full-ssms](https://cloud.githubusercontent.com/assets/8171434/23591707/0cb4c4ae-01f5-11e7-89de-958880b60b9b.png)\n\n* Set 'StarWars.Api' as a StartUp project\n\n* Run 'StarWars.Api' to seed database\n![ef-seed-full-database](https://cloud.githubusercontent.com/assets/8171434/23591737/7e581b10-01f5-11e7-9d78-e7b92cf7ba10.png)\n![seeded-full-database-smss](https://cloud.githubusercontent.com/assets/8171434/23591764/e902dfa4-01f5-11e7-8dbc-752da3305d45.png)\n\n* Create integration test checking EF configuration and seeded data\n```csharp\nnamespace StarWars.Tests.Integration.Data.EntityFramework\n{\n    public class StarWarsContextShould\n    {\n        [Fact]\n        public async void ReturnR2D2Droid()\n        {\n            // Given\n            using (var db = new StarWarsContext())\n            {\n                // When\n                var r2d2 = await db.Droids\n                    .Include(\"CharacterEpisodes.Episode\")\n                    .Include(\"CharacterFriends.Friend\")\n                    .FirstOrDefaultAsync(d =\u003e d.Id == 2001);\n\n                // Then\n                Assert.NotNull(r2d2);\n                Assert.Equal(\"R2-D2\", r2d2.Name);\n                Assert.Equal(\"Astromech\", r2d2.PrimaryFunction);\n                var episodes = r2d2.CharacterEpisodes.Select(e =\u003e e.Episode.Title);\n                Assert.Equal(new string[] { \"NEWHOPE\", \"EMPIRE\", \"JEDI\" }, episodes);\n                var friends = r2d2.CharacterFriends.Select(e =\u003e e.Friend.Name);\n                Assert.Equal(new string[] { \"Luke Skywalker\", \"Han Solo\", \"Leia Organa\" }, friends);\n            }\n        }\n    }\n}\n```\n\n* Make sure all tests pass\n![all-tests-pass-full-database](https://cloud.githubusercontent.com/assets/8171434/23591793/60ab3506-01f6-11e7-88b5-f096b2fc9777.png)\n\n* Update StarWarsQuery with new hero (\"R2-D2\") ID (2001)\n```csharp\nnamespace StarWars.Api.Models\n{\n    public class StarWarsQuery : ObjectGraphType\n    {\n        // ...\n        public StarWarsQuery(IDroidRepository _droidRepository)\n        {\n            Field\u003cDroidType\u003e(\n              \"hero\",\n              resolve: context =\u003e _droidRepository.Get(2001)\n            );\n        }\n    }\n}\n``` \n\n* Make sure application still works\n![graphiql-full-database](https://cloud.githubusercontent.com/assets/8171434/23591775/23ec4f60-01f6-11e7-92ef-f98ea24c1293.png)\n\n#### Base/generic repository\n\n* Create generic entity interface\n```csharp\nnamespace StarWars.Core.Data\n{\n    public interface IEntity\u003cTKey\u003e\n    {\n        TKey Id { get; set; }\n    }\n}\n```\n\n* Update models to inherit from IEntity interface (integer based id)\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Character : IEntity\u003cint\u003e\n    {\n        // ...\n    }\n}\n```\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Episode : IEntity\u003cint\u003e\n    {\n        // ...\n    }\n}\n```\n```csharp\nnamespace StarWars.Core.Models\n{\n    public class Planet : IEntity\u003cint\u003e\n    {\n        // ...\n    }\n}\n```\n\n* Create base/generic repository interface\n```csharp\nnamespace StarWars.Core.Data\n{\n    public interface IBaseRepository\u003cTEntity, in TKey\u003e\n        where TEntity : class\n    {\n        Task\u003cList\u003cTEntity\u003e\u003e GetAll();\n        Task\u003cTEntity\u003e Get(TKey id);\n        TEntity Add(TEntity entity);\n        void AddRange(IEnumerable\u003cTEntity\u003e entities);\n        void Delete(TKey id);\n        void Update(TEntity entity);\n        Task\u003cbool\u003e SaveChangesAsync();\n    }\n}\n```\n* Create Entity Framework base/generic repository\n```csharp\nnamespace StarWars.Data.EntityFramework.Repositories\n{\n    public abstract class BaseRepository\u003cTEntity, TKey\u003e : IBaseRepository\u003cTEntity, TKey\u003e\n        where TEntity : class, IEntity\u003cTKey\u003e, new()\n    {\n        protected DbContext _db;\n        protected readonly ILogger _logger;\n\n        protected BaseRepository() { }\n\n        protected BaseRepository(DbContext db, ILogger logger)\n        {\n            _db = db;\n            _logger = logger;\n        }\n\n        public virtual Task\u003cList\u003cTEntity\u003e\u003e GetAll()\n        {\n            return _db.Set\u003cTEntity\u003e().ToListAsync();\n        }\n\n        public virtual Task\u003cTEntity\u003e Get(TKey id)\n        {\n            _logger.LogInformation(\"Get {type} with id = {id}\", typeof(TEntity).Name, id);\n            return _db.Set\u003cTEntity\u003e().SingleOrDefaultAsync(c =\u003e c.Id.Equals(id));\n        }\n\n        public virtual TEntity Add(TEntity entity)\n        {\n            _db.Set\u003cTEntity\u003e().Add(entity);\n            return entity;\n        }\n\n        public void AddRange(IEnumerable\u003cTEntity\u003e entities)\n        {\n            _db.Set\u003cTEntity\u003e().AddRange(entities);\n        }\n\n        public virtual void Delete(TKey id)\n        {\n            var entity = new TEntity { Id = id };\n            _db.Set\u003cTEntity\u003e().Attach(entity);\n            _db.Set\u003cTEntity\u003e().Remove(entity);\n        }\n\n        public virtual async Task\u003cbool\u003e SaveChangesAsync()\n        {\n            return (await _db.SaveChangesAsync()) \u003e 0;\n        }\n\n        public virtual void Update(TEntity entity)\n        {\n            _db.Set\u003cTEntity\u003e().Attach(entity);\n            _db.Entry(entity).State = EntityState.Modified;\n        }\n    }\n}\n```\n\n* Refactor EF Droid repository\n```csharp\nnamespace StarWars.Core.Data\n{\n    public interface IDroidRepository : IBaseRepository\u003cDroid, int\u003e { }\n}\n```\n```csharp\nnamespace StarWars.Data.EntityFramework.Repositories\n{\n    public class DroidRepository : BaseRepository\u003cDroid, int\u003e, IDroidRepository\n    {\n        public DroidRepository() { }\n\n        public DroidRepository(StarWarsContext db, ILogger\u003cDroidRepository\u003e logger)\n            : base(db, logger)\n        {\n        }\n    }\n}\n```\n* Refactor in-memeory Droid repository\n```csharp\nnamespace StarWars.Data.InMemory\n{\n    public class DroidRepository : IDroidRepository\n    {\n        private readonly ILogger _logger;\n\n        public DroidRepository() { }\n\n        public DroidRepository(ILogger\u003cDroidRepository\u003e logger)\n        {\n            _logger = logger;\n        }\n\n        private List\u003cDroid\u003e _droids = new List\u003cDroid\u003e {\n            new Droid { Id = 1, Name = \"R2-D2\" }\n        };\n\n        public Task\u003cDroid\u003e Get(int id)\n        {\n            _logger.LogInformation(\"Get droid with id = {id}\", id);\n            return Task.FromResult(_droids.FirstOrDefault(droid =\u003e droid.Id == id));\n        }\n\n        // ...\n        // rest of the methods are not implemented\n        // for now they are just throwing  NotImplementedException       \n    }\n}\n```\n\n* Make sure tests and api stil works\n\n####  Visual Studio 2017 RTM upgrade\n\n* Update all NuGet packages for the solution (especially .NET Core v1.1.1)\n![vs-2017-rtm-nugets-update](https://cloud.githubusercontent.com/assets/8171434/23823157/c6a30c34-065c-11e7-8d69-81e43fbdd8a1.png)\n\n* Use 'Package Manger Console' to fix problems with upgrading 'Microsoft.NETCore.App' from v1.1.0 to v.1.1.1 (for some reason Consolidate option does not work). Do upgrade for all projects.\n![consolidate-netcore-app](https://cloud.githubusercontent.com/assets/8171434/23823175/31e33df2-065d-11e7-8645-f4f040a36723.png)\n```\nInstall-Package Microsoft.NETCore.App\n```\n![package-manager-console-netcore-app-upgrade](https://cloud.githubusercontent.com/assets/8171434/23823182/4b07003e-065d-11e7-9f69-7803bd62a236.png)\n\n* Fix 'DroidType' unit test (capitalization of field names)\n```csharp\nnamespace StarWars.Tests.Unit.Api.Models\n{\n    public class DroidTypeShould\n    {\n        [Fact]\n        public void HaveIdAndNameFields()\n        {\n            // When\n            var droidType = new DroidType();\n\n            // Then\n            Assert.NotNull(droidType);\n            Assert.True(droidType.HasField(\"Id\"));\n            Assert.True(droidType.HasField(\"Name\"));\n        }\n    }\n}\n```\n\n#### Repositories\n\n* Create rest of the repositories (Character, Episode, Human, Planet)\n```csharp\nnamespace StarWars.Core.Data\n{\n    public interface IHumanRepository : IBaseRepository\u003cHuman, int\u003e { }\n}\n```\n```csharp\nnamespace StarWars.Data.EntityFramework.Repositories\n{\n    public class HumanRepository : BaseRepository\u003cHuman, int\u003e, IHumanRepository\n    {\n        public HumanRepository() { }\n\n        public HumanRepository(StarWarsContext db, ILogger\u003cHumanRepository\u003e logger)\n            : base(db, logger)\n        {\n        }\n    }\n}\n```\n\n* Update base repository with 'include' versions\n```csharp\nnamespace StarWars.Core.Data\n{\n    public interface IBaseRepository\u003cTEntity, in TKey\u003e\n        where TEntity : class\n    {\n        // ...\n        Task\u003cList\u003cTEntity\u003e\u003e GetAll(string include);\n        Task\u003cList\u003cTEntity\u003e\u003e GetAll(IEnumerable\u003cstring\u003e includes);\n        \n        // ...\n\n        Task\u003cTEntity\u003e Get(TKey id, string include);\n        Task\u003cTEntity\u003e Get(TKey id, IEnumerable\u003cstring\u003e includes);\n        // ...\n    }\n}\n```\n```csharp\nnamespace StarWars.Data.EntityFramework.Repositories\n{\n    public abstract class BaseRepository\u003cTEntity, TKey\u003e : IBaseRepository\u003cTEntity, TKey\u003e\n        where TEntity : class, IEntity\u003cTKey\u003e, new()\n    {\n        // ...\n        public Task\u003cList\u003cTEntity\u003e\u003e GetAll(string include)\n        {\n            _logger.LogInformation(\"Get all {type}s (including {include})\", typeof(TEntity).Name, include);\n            return _db.Set\u003cTEntity\u003e().Include(include).ToListAsync();\n        }\n\n        public Task\u003cList\u003cTEntity\u003e\u003e GetAll(IEnumerable\u003cstring\u003e includes)\n        {\n            _logger.LogInformation(\"Get all {type}s (including [{includes}])\", typeof(TEntity).Name, string.Join(\",\", includes));\n            var query = _db.Set\u003cTEntity\u003e().AsQueryable();\n            query = includes.Aggregate(query, (current, include) =\u003e current.Include(include));\n            return query.ToListAsync();\n        }\n\n        // ...\n\n        public Task\u003cTEntity\u003e Get(TKey id, string include)\n        {\n            _logger.LogInformation(\"Get {type} with id = {id} (including {include})\", typeof(TEntity).Name, id, include);\n            return _db.Set\u003cTEntity\u003e().Include(include).SingleOrDefaultAsync(c =\u003e c.Id.Equals(id));\n        }\n\n        public Task\u003cTEntity\u003e Get(TKey id, IEnumerable\u003cstring\u003e includes)\n        {\n            _logger.LogInformation(\"Get {type} with id = {id} (including [{include}])\", typeof(TEntity).Name, id, string.Join(\",\", includes));\n            var query = _db.Set\u003cTEntity\u003e().AsQueryable();\n            query = includes.Aggregate(query, (current, include) =\u003e current.Include(include));\n            return query.SingleOrDefaultAsync(c =\u003e c.Id.Equals(id));\n        }\n\n        // ...\n    }\n}\n```\n\n* Create repositories CRUD unit tests\n```csharp\nnamespace StarWars.Tests.Unit.Data.EntityFramework.Repositories\n{\n    public class HumanRepositoryShould\n    {\n        private readonly HumanRepository _humanRepository;\n        private DbContextOptions\u003cStarWarsContext\u003e _options;\n        private Mock\u003cILogger\u003cStarWarsContext\u003e\u003e _dbLogger;\n        public HumanRepositoryShould()\n        {\n            // Given\n            _dbLogger = new Mock\u003cILogger\u003cStarWarsContext\u003e\u003e();\n            _options = new DbContextOptionsBuilder\u003cStarWarsContext\u003e()\n                .UseInMemoryDatabase(databaseName: \"StarWars_HumanRepositoryShould\")\n                .Options;\n            using (var context = new StarWarsContext(_options, _dbLogger.Object))\n            {\n                context.EnsureSeedData();\n            }\n            var starWarsContext = new StarWarsContext(_options, _dbLogger.Object);\n            var repoLogger = new Mock\u003cILogger\u003cHumanRepository\u003e\u003e();\n            _humanRepository = new HumanRepository(starWarsContext, repoLogger.Object);\n        }\n\n        [Fact]\n        public async void ReturnLukeGivenIdOf1000()\n        {\n            // When\n            var luke = await _humanRepository.Get(1000);\n\n            // Then\n            Assert.NotNull(luke);\n            Assert.Equal(\"Luke Skywalker\", luke.Name);\n        }\n\n        [Fact]\n        public async void ReturnLukeFriendsAndEpisodes()\n        {\n            // When\n            var character = await _humanRepository.Get(1000, includes: new[] { \"CharacterEpisodes.Episode\", \"CharacterFriends.Friend\" });\n\n            // Then\n            Assert.NotNull(character);\n            Assert.NotNull(character.CharacterEpisodes);\n            var episodes = character.CharacterEpisodes.Select(e =\u003e e.Episode.Title);\n            Assert.Equal(new[] { \"NEWHOPE\", \"EMPIRE\", \"JEDI\" }, episodes);\n            Assert.NotNull(character.CharacterFriends);\n            var friends = character.CharacterFriends.Select(e =\u003e e.Friend.Name);\n            Assert.Equal(new[] { \"Han Solo\", \"Leia Organa\", \"C-3PO\", \"R2-D2\" }, friends);\n        }\n\n        [Fact]\n        public async void ReturnLukesHomePlanet()\n        {\n            // When\n            var luke = await _humanRepository.Get(1000, include: \"HomePlanet\");\n\n            // Then\n            Assert.NotNull(luke);\n            Assert.NotNull(luke.HomePlanet);\n            Assert.Equal(\"Tatooine\", luke.HomePlanet.Name);\n        }\n\n        [Fact]\n        public async void AddNewHuman()\n        {\n            // Given\n            var human10101 = new Human { Id = 10101, Name = \"Human10101\" };\n\n            // When\n            _humanRepository.Add(human10101);\n            var saved = await _humanRepository.SaveChangesAsync();\n\n            // Then\n            Assert.True(saved);\n            using (var db = new StarWarsContext(_options, _dbLogger.Object))\n            {\n                var human = await db.Humans.FindAsync(10101);\n                Assert.NotNull(human);\n                Assert.Equal(10101, human.Id);\n                Assert.Equal(\"Human10101\", human.Name);\n\n                // Cleanup\n                db.Humans.Remove(human);\n                await db.SaveChangesAsync();\n            }\n        }\n\n        [Fact]\n        public async void UpdateExistingHuman()\n        {\n            // Given\n            var vader = await _humanRepository.Get(1001);\n            vader.Name = \"Human1001\";\n\n            // When\n            _humanRepository.Update(vader);\n            var saved = await _humanRepository.SaveChangesAsync();\n\n            // Then\n            Assert.True(saved);\n            using (var db = new StarWarsContext(_options, _dbLogger.Object))\n            {\n                var human = await db.Humans.FindAsync(1001);\n                Assert.NotNull(human);\n                Assert.Equal(1001, human.Id);\n                Assert.Equal(\"Human1001\", human.Name);\n\n                // Cleanup\n                human.Name = \"Darth Vader\";\n                db.Humans.Update(human);\n                await db.SaveChangesAsync();\n            }\n        }\n\n        [Fact]\n        public async void DeleteExistingHuman()\n        {\n            // Given\n            using (var db = new StarWarsContext(_options, _dbLogger.Object))\n            {\n                var human10102 = new Human { Id = 10102, Name = \"Human10102\" };\n                await db.Humans.AddAsync(human10102);\n                await db.SaveChangesAsync();\n            }\n\n            // When\n            _humanRepository.Delete(10102);\n            var saved = await _humanRepository.SaveChangesAsync();\n\n            // Then\n            Assert.True(saved);\n            using (var db = new StarWarsContext(_options, _dbLogger.Object))\n            {\n                var deletedHuman = await db.Humans.FindAsync(10101);\n                Assert.Null(deletedHuman);\n            }\n        }\n    }\n}\n```\n\n* Check test results\n![test-explorer-repositories-crud-tests](https://cloud.githubusercontent.com/assets/8171434/23831006/02b658dc-0718-11e7-8951-3bce49501aca.png)\n![code-coverage-repositories-crud-tests](https://cloud.githubusercontent.com/assets/8171434/23831024/af110302-0718-11e7-8c07-e079a11119b6.png)\n\n#### GraphQL queries\n\n* TDD (Test First) integration tests for queries at [GraphQL Specification by Facebook](https://github.com/facebook/graphql#query-syntax)\n```csharp\nnamespace StarWars.Tests.Integration.Api.Controllers\n{\n    public class GraphQLControllerShould\n    {\n        // ...\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteHeroNameQuery()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\": \n                    \"\"query HeroNameQuery {\n                        hero {\n                            name\n                        }\n                    }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(\"R2-D2\", (string)jobj[\"data\"][\"hero\"][\"name\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteHeroNameAndFriendsQuery()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query HeroNameAndFriendsQuery {\n                        hero {\n                            id\n                            name\n                            friends {\n                                id\n                                name\n                            }\n                        }\n                    }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(3, ((JArray)jobj[\"data\"][\"hero\"][\"friends\"]).Count);\n            Assert.Equal(\"Luke Skywalker\", (string)jobj[\"data\"][\"hero\"][\"friends\"][0][\"name\"]);\n            Assert.Equal(\"Han Solo\", (string)jobj[\"data\"][\"hero\"][\"friends\"][1][\"name\"]);\n            Assert.Equal(\"Leia Organa\", (string)jobj[\"data\"][\"hero\"][\"friends\"][2][\"name\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteNestedQuery()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query NestedQuery {\n                        hero {\n                            name\n                            friends {\n                                name\n                                appearsIn\n                                friends {\n                                    name\n                                }\n                            }\n                        }\n                    }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            var luke = jobj[\"data\"][\"hero\"][\"friends\"][0];\n            var episodes = ((JArray) luke[\"appearsIn\"]).Select(e =\u003e (string)e).ToArray();\n            Assert.Equal(new[] { \"NEWHOPE\", \"EMPIRE\", \"JEDI\" }, episodes);\n            Assert.Equal(4, ((JArray)luke[\"friends\"]).Count);\n            Assert.Equal(\"Han Solo\", (string)luke[\"friends\"][0][\"name\"]);\n            Assert.Equal(\"Leia Organa\", (string)luke[\"friends\"][1][\"name\"]);\n            Assert.Equal(\"C-3PO\", (string)luke[\"friends\"][2][\"name\"]);\n            Assert.Equal(\"R2-D2\", (string)luke[\"friends\"][3][\"name\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteFetchLukeQuery()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query FetchLukeQuery {\n                        human(id: \"\"1000\"\") {\n                            name\n                        }\n                    }\n                }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(\"Luke Skywalker\", (string)jobj[\"data\"][\"human\"][\"name\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteFetchLukeAliased()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query FetchLukeAliased {\n                        luke: human(id: \"\"1000\"\") {\n                            name\n                        }\n                    }\n                }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(\"Luke Skywalker\", (string)jobj[\"data\"][\"human\"][\"name\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteFetchLukeAndLeiaAliased()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query FetchLukeAliased {\n                        luke: human(id: \"\"1000\"\") {\n                            name\n                        }\n                        leia: human(id: \"\"1003\"\") {\n                            name\n                        }\n                    }\n                }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(\"Luke Skywalker\", (string)jobj[\"data\"][\"luke\"][\"name\"]);\n            Assert.Equal(\"Leia Organa\", (string)jobj[\"data\"][\"leia\"][\"name\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteDuplicateFields()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query DuplicateFields {\n                        luke: human(id: \"\"1000\"\") {\n                            name\n                            homePlanet\n                        }\n                        leia: human(id: \"\"1003\"\") {\n                            name\n                            homePlanet\n                        }\n                    }\n                }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(\"Luke Skywalker\", (string)jobj[\"data\"][\"luke\"][\"name\"]);\n            Assert.Equal(\"Tatooine\", (string)jobj[\"data\"][\"luke\"][\"homePlanet\"]);\n            Assert.Equal(\"Leia Organa\", (string)jobj[\"data\"][\"leia\"][\"name\"]);\n            Assert.Equal(\"Alderaan\", (string)jobj[\"data\"][\"leia\"][\"homePlanet\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteUseFragment()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query UseFragment {\n                        luke: human(id: \"\"1000\"\") {\n                            ...HumanFragment\n                        }\n                        leia: human(id: \"\"1003\"\") {\n                            ...HumanFragment\n                        }\n                    }\n\n                    fragment HumanFragment on Human {\n                        name\n                        homePlanet\n                    }\n                }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(\"Luke Skywalker\", (string)jobj[\"data\"][\"luke\"][\"name\"]);\n            Assert.Equal(\"Tatooine\", (string)jobj[\"data\"][\"luke\"][\"homePlanet\"]);\n            Assert.Equal(\"Leia Organa\", (string)jobj[\"data\"][\"leia\"][\"name\"]);\n            Assert.Equal(\"Alderaan\", (string)jobj[\"data\"][\"leia\"][\"homePlanet\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteCheckTypeOfR2()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query CheckTypeOfR2 {\n                        hero {\n                            __typename\n                            name\n                        } \n                    }\n                }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(\"Droid\", (string)jobj[\"data\"][\"hero\"][\"__typename\"]);\n            Assert.Equal(\"R2-D2\", (string)jobj[\"data\"][\"hero\"][\"name\"]);\n        }\n\n        [Fact]\n        [Trait(\"test\", \"integration\")]\n        public async void ExecuteCheckTypeOfLuke()\n        {\n            // Given\n            const string query = @\"{\n                \"\"query\"\":\n                    \"\"query CheckTypeOfLuke {\n                       hero(episode: EMPIRE) {\n                            __typename\n                            name\n                       }\n                    }\n                }\"\"\n            }\";\n            var content = new StringContent(query, Encoding.UTF8, \"application/json\");\n\n            // When\n            var response = await _client.PostAsync(\"/graphql\", content);\n\n            // Then\n            response.EnsureSuccessStatusCode();\n            var responseString = await response.Content.ReadAsStringAsync();\n            Assert.NotNull(responseString);\n            var jobj = JObject.Parse(responseString);\n            Assert.NotNull(jobj);\n            Assert.Equal(\"Human\", (string)jobj[\"data\"][\"hero\"][\"__typename\"]);\n            Assert.Equal(\"Luke Skywalker\", (string)jobj[\"data\"][\"hero\"][\"name\"]);\n        }\n    }\n}\n```\n![facebook-spec-queries-tdd-integration-tests-failed](https://cloud.githubusercontent.com/assets/8171434/23862757/25b7c0fe-080e-11e7-8ead-1c6bc576c762.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJacekKosciesza%2FStarWars","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FJacekKosciesza%2FStarWars","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJacekKosciesza%2FStarWars/lists"}