{"action":"created","comment":{"url":"https://api.github.com/repos/dotnet/training-tutorials/pulls/comments/99998763","pull_request_review_id":20689904,"id":99998763,"diff_hunk":"@@ -6,15 +6,190 @@ Download a ZIP containing this tutorial's sample files:\n - [Initial Version] - Use this as a starting point when following along with the tutorial yourself\n - [Completed Version] - Includes the completed versions of all samples\n \n-See the [Issue](https://github.com/dotnet/training-tutorials/issues/53) to claim this lesson and/or view what should be included in it.\n+## Introduction\n \n-## First Header\n+Your ASP.NET Core app will likely depend on a number of services. These services perform important functionality for the app. Almost anything your app does could be encapsulated in a service, but the most important operations to isolate are those that work with infrastructure outside the scope of your app. For instance, if your app stores data in a database, or reads data in from an online API, these operations would benefit from being encapsulated in services rather than being performed directly from the app. ASP.NET Core is built on the premise of modularity, and includes built-in functionality for registering services your app requires and making these services available to classes within your app. You register services your app will need in the `ConfigureServices` method in *Startup.cs*. Your classes that depend on these services then request them by declaring parameters in their constructors. Writing classes such that they explicitly request their dependencies is said to follow the [Explicit Dependencies Principle](http://deviq.com/explicit-dependencies-principle/), which is closely related to the [Dependency Inversion Principle](http://deviq.com/dependency-inversion-principle/).\n \n-Start the lesson here.\n+The current sample app you've seen uses a static `QuotationStore` to access quotes from a fixed collection. Working with this static class tightly couples your app to this implementation. If you want to easily test your code, or swap in a different implementation for how quotes are accessed, you would need to edit all of the code that directly references `QuotationStore` (this violates the [Open/Closed Principle](http://deviq.com/open-closed-principle/)). To improve this design, making it more loosely coupled and closer to how ASP.NET Core expects apps to be written, you can [refactor](http://deviq.com/refactoring/) the `QuotationStore` into a non-static service that implements an interface.\n \n-...\n+## Creating a Service\n \n+The current `Configure` method works directly with `QuotationStore` to set its contents from [configuration](configuration.md) and use these contents in [middleware](middleware-basic.md):\n+\n+```c#\n+    public void Configure(IApplicationBuilder app, \n+        IOptions<List<Quotation>> quoteOptions,\n+        ILoggerFactory loggerFactory)\n+    {\n+        loggerFactory.AddConsole(Configuration.GetSection(\"Logging\"));\n+        app.UseStatusCodePagesWithRedirects(\"~/{0}.html\");\n+        app.UseDeveloperExceptionPage();\n+        app.UseStaticFiles();\n+\n+        var quotes = quoteOptions.Value;\n+        if(quotes != null) \n+        {\n+            QuotationStore.Quotations = quotes;\n+        }\n+\n+        app.Map(\"/quote\", builder => builder.Run(async context =>\n+        {\n+            var id = int.Parse(context.Request.Path.ToString().Split('/')[1]);\n+            var quote = QuotationStore.Quotations[id];\n+            await context.Response.WriteAsync(quote.ToString());\n+        }));\n+\n+        app.Map(\"/all\", builder => builder.Run(async context =>\n+        {\n+            foreach(var quote in QuotationStore.Quotations)\n+                {\n+                    await context.Response.WriteAsync(\"<div>\");\n+                    await context.Response.WriteAsync(quote.ToString());\n+                    await context.Response.WriteAsync(\"</div>\");\n+                }\n+        }));\n+\n+        app.Map(\"/random\", builder => builder.Run(async context =>\n+        {\n+            await context.Response.WriteAsync(QuotationStore.RandomQuotation().ToString());\n+        }));\n+    }\n+```\n+\n+Now is a good time to clean up this code a bit. Initializing the quotation store is something that could be left to the store itself. Other than that, the middleware needs access to either a list of quotations, or a random quotation. Thus, you can represent this using a simple interface:\n+\n+```c#\n+    public interface IQuotationStore\n+    {\n+        IEnumerable<Quotation> List();\n+        Quotation RandomQuotation();\n+    }\n+```\n+\n+With an interface extracted, you can refactor the static `QuotationStore` to implement the interface. You can pull the initialization logic into `QuotationStore`'s constructor.\n+\n+```c#\n+    public class QuotationStore : IQuotationStore\n+    {\n+        private static List<Quotation> _quotations {get; set;}\n+\n+        public QuotationStore(IOptions<List<Quotation>> quoteOptions)\n+        {\n+            _quotations = quoteOptions.Value;\n+            if(_quotations == null)\n+            {\n+                // if nothing configured, use default in code\n+                _quotations = DefaultQuotations();\n+            }\n+        }\n+\n+        private List<Quotation> DefaultQuotations()\n+        {\n+            return new List<Quotation>()\n+            {\n+                new Quotation() { \n+                    Quote=\"Measuring programming progress by lines of code is like measuring aircraft building progress by weight.\", \n+                    Author=\"Bill Gates\"},\n+                new Quotation() { \n+                    Quote=\"Be kind whenever possible. It is always possible.\", \n+                    Author=\"Dalai Lama\"},\n+                new Quotation() { \n+                    Quote=\"Before software can be reusable it first has to be usable.\", \n+                    Author=\"Ralph Johnson\"}\n+            };\n+        }\n+\n+        public IEnumerable<Quotation> List()\n+        {\n+            return _quotations.AsEnumerable();\n+        }\n+\n+        public Quotation RandomQuotation()\n+        {\n+            Random rnd = new Random(DateTime.Now.Millisecond);\n+            return _quotations[rnd.Next(0,_quotations.Count)];\n+        }\n+    }\n+```\n+\n+## Using the Service\n+\n+After making this change, you can modify the `Configure` method to request an instance of `IQuotationStore` as a parameter. ASP.NET Core will provide the instance when the method is executed (if it's been registered, which you'll do in a moment). The refactored `Configure` method no longer requires `quoteOptions`, since the initialization code for the quotes has been encapsulated in the service itself. The slightly simplified `Configure` method that now uses `IQuotationStore` is shown below:\n+\n+```c#\n+    public void Configure(IApplicationBuilder app, \n+        IQuotationStore quotationStore,\n+        ILoggerFactory loggerFactory)\n+    {\n+        loggerFactory.AddConsole(Configuration.GetSection(\"Logging\"));\n+        //app.UseStatusCodePages(\"text/plain\",\"HTTP Status Code: {0}\");","path":"content/asp.net/getting-started/app-services.md","position":124,"original_position":124,"commit_id":"4728d90b0a6fdaa90c067d40317d5fd5ba3ce163","original_commit_id":"4728d90b0a6fdaa90c067d40317d5fd5ba3ce163","user":{"login":"MisterJames","id":1197383,"avatar_url":"https://avatars.githubusercontent.com/u/1197383?v=3","gravatar_id":"","url":"https://api.github.com/users/MisterJames","html_url":"https://github.com/MisterJames","followers_url":"https://api.github.com/users/MisterJames/followers","following_url":"https://api.github.com/users/MisterJames/following{/other_user}","gists_url":"https://api.github.com/users/MisterJames/gists{/gist_id}","starred_url":"https://api.github.com/users/MisterJames/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MisterJames/subscriptions","organizations_url":"https://api.github.com/users/MisterJames/orgs","repos_url":"https://api.github.com/users/MisterJames/repos","events_url":"https://api.github.com/users/MisterJames/events{/privacy}","received_events_url":"https://api.github.com/users/MisterJames/received_events","type":"User","site_admin":false},"body":"Leaving comment in?","created_at":"2017-02-08T06:22:40Z","updated_at":"2017-02-08T06:28:56Z","html_url":"https://github.com/dotnet/training-tutorials/pull/191#discussion_r99998763","pull_request_url":"https://api.github.com/repos/dotnet/training-tutorials/pulls/191","_links":{"self":{"href":"https://api.github.com/repos/dotnet/training-tutorials/pulls/comments/99998763"},"html":{"href":"https://github.com/dotnet/training-tutorials/pull/191#discussion_r99998763"},"pull_request":{"href":"https://api.github.com/repos/dotnet/training-tutorials/pulls/191"}}},"pull_request":{"url":"https://api.github.com/repos/dotnet/training-tutorials/pulls/191","id":104714776,"html_url":"https://github.com/dotnet/training-tutorials/pull/191","diff_url":"https://github.com/dotnet/training-tutorials/pull/191.diff","patch_url":"https://github.com/dotnet/training-tutorials/pull/191.patch","issue_url":"https://api.github.com/repos/dotnet/training-tutorials/issues/191","number":191,"state":"open","locked":false,"title":"Dependency Injection","user":{"login":"ardalis","id":782127,"avatar_url":"https://avatars.githubusercontent.com/u/782127?v=3","gravatar_id":"","url":"https://api.github.com/users/ardalis","html_url":"https://github.com/ardalis","followers_url":"https://api.github.com/users/ardalis/followers","following_url":"https://api.github.com/users/ardalis/following{/other_user}","gists_url":"https://api.github.com/users/ardalis/gists{/gist_id}","starred_url":"https://api.github.com/users/ardalis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ardalis/subscriptions","organizations_url":"https://api.github.com/users/ardalis/orgs","repos_url":"https://api.github.com/users/ardalis/repos","events_url":"https://api.github.com/users/ardalis/events{/privacy}","received_events_url":"https://api.github.com/users/ardalis/received_events","type":"User","site_admin":false},"body":"Fixes #53 ","created_at":"2017-02-05T20:52:56Z","updated_at":"2017-02-08T06:28:56Z","closed_at":null,"merged_at":null,"merge_commit_sha":"84b0ba08e85ec63113d3af8289d49fd0fdd16b1b","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/dotnet/training-tutorials/pulls/191/commits","review_comments_url":"https://api.github.com/repos/dotnet/training-tutorials/pulls/191/comments","review_comment_url":"https://api.github.com/repos/dotnet/training-tutorials/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dotnet/training-tutorials/issues/191/comments","statuses_url":"https://api.github.com/repos/dotnet/training-tutorials/statuses/4728d90b0a6fdaa90c067d40317d5fd5ba3ce163","head":{"label":"dotnet:ardalis/di","ref":"ardalis/di","sha":"4728d90b0a6fdaa90c067d40317d5fd5ba3ce163","user":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"repo":{"id":59598094,"name":"training-tutorials","full_name":"dotnet/training-tutorials","owner":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dotnet/training-tutorials","description":"Getting started tutorials for C# and ASP.NET","fork":false,"url":"https://api.github.com/repos/dotnet/training-tutorials","forks_url":"https://api.github.com/repos/dotnet/training-tutorials/forks","keys_url":"https://api.github.com/repos/dotnet/training-tutorials/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dotnet/training-tutorials/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dotnet/training-tutorials/teams","hooks_url":"https://api.github.com/repos/dotnet/training-tutorials/hooks","issue_events_url":"https://api.github.com/repos/dotnet/training-tutorials/issues/events{/number}","events_url":"https://api.github.com/repos/dotnet/training-tutorials/events","assignees_url":"https://api.github.com/repos/dotnet/training-tutorials/assignees{/user}","branches_url":"https://api.github.com/repos/dotnet/training-tutorials/branches{/branch}","tags_url":"https://api.github.com/repos/dotnet/training-tutorials/tags","blobs_url":"https://api.github.com/repos/dotnet/training-tutorials/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dotnet/training-tutorials/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dotnet/training-tutorials/git/refs{/sha}","trees_url":"https://api.github.com/repos/dotnet/training-tutorials/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dotnet/training-tutorials/statuses/{sha}","languages_url":"https://api.github.com/repos/dotnet/training-tutorials/languages","stargazers_url":"https://api.github.com/repos/dotnet/training-tutorials/stargazers","contributors_url":"https://api.github.com/repos/dotnet/training-tutorials/contributors","subscribers_url":"https://api.github.com/repos/dotnet/training-tutorials/subscribers","subscription_url":"https://api.github.com/repos/dotnet/training-tutorials/subscription","commits_url":"https://api.github.com/repos/dotnet/training-tutorials/commits{/sha}","git_commits_url":"https://api.github.com/repos/dotnet/training-tutorials/git/commits{/sha}","comments_url":"https://api.github.com/repos/dotnet/training-tutorials/comments{/number}","issue_comment_url":"https://api.github.com/repos/dotnet/training-tutorials/issues/comments{/number}","contents_url":"https://api.github.com/repos/dotnet/training-tutorials/contents/{+path}","compare_url":"https://api.github.com/repos/dotnet/training-tutorials/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dotnet/training-tutorials/merges","archive_url":"https://api.github.com/repos/dotnet/training-tutorials/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dotnet/training-tutorials/downloads","issues_url":"https://api.github.com/repos/dotnet/training-tutorials/issues{/number}","pulls_url":"https://api.github.com/repos/dotnet/training-tutorials/pulls{/number}","milestones_url":"https://api.github.com/repos/dotnet/training-tutorials/milestones{/number}","notifications_url":"https://api.github.com/repos/dotnet/training-tutorials/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dotnet/training-tutorials/labels{/name}","releases_url":"https://api.github.com/repos/dotnet/training-tutorials/releases{/id}","deployments_url":"https://api.github.com/repos/dotnet/training-tutorials/deployments","created_at":"2016-05-24T18:25:52Z","updated_at":"2017-02-07T09:50:52Z","pushed_at":"2017-02-07T22:41:26Z","git_url":"git://github.com/dotnet/training-tutorials.git","ssh_url":"git@github.com:dotnet/training-tutorials.git","clone_url":"https://github.com/dotnet/training-tutorials.git","svn_url":"https://github.com/dotnet/training-tutorials","homepage":"http://dot.net/","size":1214,"stargazers_count":55,"watchers_count":55,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":20,"mirror_url":null,"open_issues_count":17,"forks":20,"open_issues":17,"watchers":55,"default_branch":"master"}},"base":{"label":"dotnet:master","ref":"master","sha":"480af14dbf621b5442f1d62fd9b040a2454eb60d","user":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"repo":{"id":59598094,"name":"training-tutorials","full_name":"dotnet/training-tutorials","owner":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dotnet/training-tutorials","description":"Getting started tutorials for C# and ASP.NET","fork":false,"url":"https://api.github.com/repos/dotnet/training-tutorials","forks_url":"https://api.github.com/repos/dotnet/training-tutorials/forks","keys_url":"https://api.github.com/repos/dotnet/training-tutorials/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dotnet/training-tutorials/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dotnet/training-tutorials/teams","hooks_url":"https://api.github.com/repos/dotnet/training-tutorials/hooks","issue_events_url":"https://api.github.com/repos/dotnet/training-tutorials/issues/events{/number}","events_url":"https://api.github.com/repos/dotnet/training-tutorials/events","assignees_url":"https://api.github.com/repos/dotnet/training-tutorials/assignees{/user}","branches_url":"https://api.github.com/repos/dotnet/training-tutorials/branches{/branch}","tags_url":"https://api.github.com/repos/dotnet/training-tutorials/tags","blobs_url":"https://api.github.com/repos/dotnet/training-tutorials/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dotnet/training-tutorials/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dotnet/training-tutorials/git/refs{/sha}","trees_url":"https://api.github.com/repos/dotnet/training-tutorials/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dotnet/training-tutorials/statuses/{sha}","languages_url":"https://api.github.com/repos/dotnet/training-tutorials/languages","stargazers_url":"https://api.github.com/repos/dotnet/training-tutorials/stargazers","contributors_url":"https://api.github.com/repos/dotnet/training-tutorials/contributors","subscribers_url":"https://api.github.com/repos/dotnet/training-tutorials/subscribers","subscription_url":"https://api.github.com/repos/dotnet/training-tutorials/subscription","commits_url":"https://api.github.com/repos/dotnet/training-tutorials/commits{/sha}","git_commits_url":"https://api.github.com/repos/dotnet/training-tutorials/git/commits{/sha}","comments_url":"https://api.github.com/repos/dotnet/training-tutorials/comments{/number}","issue_comment_url":"https://api.github.com/repos/dotnet/training-tutorials/issues/comments{/number}","contents_url":"https://api.github.com/repos/dotnet/training-tutorials/contents/{+path}","compare_url":"https://api.github.com/repos/dotnet/training-tutorials/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dotnet/training-tutorials/merges","archive_url":"https://api.github.com/repos/dotnet/training-tutorials/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dotnet/training-tutorials/downloads","issues_url":"https://api.github.com/repos/dotnet/training-tutorials/issues{/number}","pulls_url":"https://api.github.com/repos/dotnet/training-tutorials/pulls{/number}","milestones_url":"https://api.github.com/repos/dotnet/training-tutorials/milestones{/number}","notifications_url":"https://api.github.com/repos/dotnet/training-tutorials/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dotnet/training-tutorials/labels{/name}","releases_url":"https://api.github.com/repos/dotnet/training-tutorials/releases{/id}","deployments_url":"https://api.github.com/repos/dotnet/training-tutorials/deployments","created_at":"2016-05-24T18:25:52Z","updated_at":"2017-02-07T09:50:52Z","pushed_at":"2017-02-07T22:41:26Z","git_url":"git://github.com/dotnet/training-tutorials.git","ssh_url":"git@github.com:dotnet/training-tutorials.git","clone_url":"https://github.com/dotnet/training-tutorials.git","svn_url":"https://github.com/dotnet/training-tutorials","homepage":"http://dot.net/","size":1214,"stargazers_count":55,"watchers_count":55,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":20,"mirror_url":null,"open_issues_count":17,"forks":20,"open_issues":17,"watchers":55,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dotnet/training-tutorials/pulls/191"},"html":{"href":"https://github.com/dotnet/training-tutorials/pull/191"},"issue":{"href":"https://api.github.com/repos/dotnet/training-tutorials/issues/191"},"comments":{"href":"https://api.github.com/repos/dotnet/training-tutorials/issues/191/comments"},"review_comments":{"href":"https://api.github.com/repos/dotnet/training-tutorials/pulls/191/comments"},"review_comment":{"href":"https://api.github.com/repos/dotnet/training-tutorials/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dotnet/training-tutorials/pulls/191/commits"},"statuses":{"href":"https://api.github.com/repos/dotnet/training-tutorials/statuses/4728d90b0a6fdaa90c067d40317d5fd5ba3ce163"}}}}
{"actor":{"display_login":"MisterJames"}}