├── src ├── section_3 │ ├── section3.4 │ │ ├── Views │ │ │ ├── _ViewStart.cshtml │ │ │ ├── _ViewImports.cshtml │ │ │ ├── Home │ │ │ │ ├── Privacy.cshtml │ │ │ │ └── Index.cshtml │ │ │ └── Shared │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ ├── Error.cshtml │ │ │ │ ├── _Layout.cshtml.css │ │ │ │ └── _Layout.cshtml │ │ ├── wwwroot │ │ │ ├── favicon.ico │ │ │ ├── js │ │ │ │ └── site.js │ │ │ ├── css │ │ │ │ └── site.css │ │ │ └── lib │ │ │ │ ├── jquery │ │ │ │ └── LICENSE.txt │ │ │ │ ├── jquery-validation │ │ │ │ └── LICENSE.md │ │ │ │ ├── bootstrap │ │ │ │ └── LICENSE │ │ │ │ └── jquery-validation-unobtrusive │ │ │ │ └── LICENSE.txt │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Models │ │ │ └── ErrorViewModel.cs │ │ ├── section3.4.csproj │ │ ├── Controllers │ │ │ └── HomeController.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ └── Program.cs │ ├── section3.3 │ │ ├── Views │ │ │ ├── _ViewStart.cshtml │ │ │ ├── Shared │ │ │ │ ├── Error.cshtml │ │ │ │ └── _Layout.cshtml │ │ │ ├── Home │ │ │ │ └── Index.cshtml │ │ │ └── Web.config │ │ ├── Global.asax │ │ ├── favicon.ico │ │ ├── Areas │ │ │ └── HelpPage │ │ │ │ ├── Views │ │ │ │ ├── Help │ │ │ │ │ ├── DisplayTemplates │ │ │ │ │ │ ├── ImageSample.cshtml │ │ │ │ │ │ ├── TextSample.cshtml │ │ │ │ │ │ ├── SimpleTypeModelDescription.cshtml │ │ │ │ │ │ ├── ComplexTypeModelDescription.cshtml │ │ │ │ │ │ ├── CollectionModelDescription.cshtml │ │ │ │ │ │ ├── InvalidSample.cshtml │ │ │ │ │ │ ├── DictionaryModelDescription.cshtml │ │ │ │ │ │ ├── KeyValuePairModelDescription.cshtml │ │ │ │ │ │ ├── EnumTypeModelDescription.cshtml │ │ │ │ │ │ ├── Samples.cshtml │ │ │ │ │ │ ├── ModelDescriptionLink.cshtml │ │ │ │ │ │ ├── ApiGroup.cshtml │ │ │ │ │ │ ├── Parameters.cshtml │ │ │ │ │ │ └── HelpPageApiModel.cshtml │ │ │ │ │ ├── ResourceModel.cshtml │ │ │ │ │ ├── Api.cshtml │ │ │ │ │ └── Index.cshtml │ │ │ │ ├── _ViewStart.cshtml │ │ │ │ ├── Shared │ │ │ │ │ └── _Layout.cshtml │ │ │ │ └── Web.config │ │ │ │ ├── ModelDescriptions │ │ │ │ ├── SimpleTypeModelDescription.cs │ │ │ │ ├── DictionaryModelDescription.cs │ │ │ │ ├── CollectionModelDescription.cs │ │ │ │ ├── ParameterAnnotation.cs │ │ │ │ ├── EnumValueDescription.cs │ │ │ │ ├── KeyValuePairModelDescription.cs │ │ │ │ ├── IModelDocumentationProvider.cs │ │ │ │ ├── ModelDescription.cs │ │ │ │ ├── ComplexTypeModelDescription.cs │ │ │ │ ├── EnumTypeModelDescription.cs │ │ │ │ ├── ModelNameAttribute.cs │ │ │ │ ├── ParameterDescription.cs │ │ │ │ └── ModelNameHelper.cs │ │ │ │ ├── SampleGeneration │ │ │ │ ├── SampleDirection.cs │ │ │ │ ├── TextSample.cs │ │ │ │ ├── InvalidSample.cs │ │ │ │ └── ImageSample.cs │ │ │ │ ├── HelpPageAreaRegistration.cs │ │ │ │ ├── ApiDescriptionExtensions.cs │ │ │ │ └── Controllers │ │ │ │ └── HelpController.cs │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── App_Start │ │ │ ├── FilterConfig.cs │ │ │ ├── RouteConfig.cs │ │ │ ├── WebApiConfig.cs │ │ │ └── BundleConfig.cs │ │ ├── Models │ │ │ ├── Employee.cs │ │ │ ├── Sale.cs │ │ │ └── SalesContext.cs │ │ ├── Controllers │ │ │ ├── HomeController.cs │ │ │ ├── ValuesController.cs │ │ │ ├── AverageController.cs │ │ │ └── TotalSalesController.cs │ │ ├── Content │ │ │ └── Site.css │ │ ├── Global.asax.cs │ │ ├── Redis.cs │ │ ├── Migrations │ │ │ ├── 202211301905089_initial.Designer.cs │ │ │ ├── 202211302024328_initial.Designer.cs │ │ │ ├── 202211301905089_initial.cs │ │ │ ├── 202211302024328_initial.cs │ │ │ └── Configuration.cs │ │ ├── README.md │ │ ├── Section3.3.sln │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Properties │ │ │ └── AssemblyInfo.cs │ ├── section3.5 │ │ ├── Views │ │ │ ├── _ViewStart.cshtml │ │ │ ├── Home │ │ │ │ ├── About.cshtml │ │ │ │ ├── Index.cshtml │ │ │ │ └── Contact.cshtml │ │ │ ├── Shared │ │ │ │ ├── Error.cshtml │ │ │ │ └── _Layout.cshtml │ │ │ └── Web.config │ │ ├── Global.asax │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── App_Start │ │ │ ├── FilterConfig.cs │ │ │ ├── RouteConfig.cs │ │ │ └── BundleConfig.cs │ │ ├── Global.asax.cs │ │ ├── Content │ │ │ └── Site.css │ │ ├── Controllers │ │ │ └── HomeController.cs │ │ ├── README.md │ │ ├── section3.5.sln │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ └── packages.config │ └── section3.2 │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── SalesContext.cs │ │ ├── section3.2.csproj │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── InitService.cs │ │ └── Migrations │ │ ├── 20221128165447_InitialCreate.cs │ │ └── SalesContextModelSnapshot.cs ├── section_4 │ ├── section4.3 │ │ ├── section4.3.csproj │ │ └── Program.cs │ ├── section4.2 │ │ ├── section4.2.csproj │ │ └── Program.cs │ └── section4.1 │ │ └── section4.1.csproj ├── section_5 │ ├── section5.1 │ │ ├── section5.1.csproj │ │ ├── Program.cs │ │ └── Model.cs │ ├── section5.2 │ │ ├── section5.2.csproj │ │ ├── Model.cs │ │ └── Program.cs │ ├── section5.3 │ │ ├── section5.3.csproj │ │ └── Model.cs │ ├── section5.4 │ │ ├── section5.4.csproj │ │ ├── Model.cs │ │ └── Program.cs │ └── section5.5 │ │ ├── section5.5.csproj │ │ └── Model.cs ├── section_1 │ ├── section1.2 │ │ ├── section1.2.csproj │ │ └── Program.cs │ └── section1.5 │ │ ├── section1.5.csproj │ │ └── Program.cs └── section_2 │ ├── section2.1 │ └── section2.1.csproj │ ├── section2.2 │ └── section2.2.csproj │ ├── section2.3 │ └── section2.3.csproj │ ├── section2.4 │ └── section2.4.csproj │ ├── section2.5 │ ├── section2.5.csproj │ └── Program.cs │ ├── section2.6 │ └── section2.6.csproj │ ├── section2.7 │ ├── section2.7.csproj │ └── Program.cs │ ├── section2.8 │ ├── section2.8.csproj │ └── Program.cs │ └── section2.9 │ ├── section2.9.csproj │ └── Program.cs ├── images_for_readme ├── insight_web.png ├── cloud_new_sub_1.png ├── cloud_new_sub_2.png ├── cloud_new_sub_3.png ├── cloud_new_sub_4.png ├── download_repo_zip.png └── insight_web_terms.png ├── docker-compose.yml ├── courseware ├── html │ ├── section_6 │ │ ├── ru102n_6_6_3_1_get_your_certificate.html │ │ ├── ru102n_6_6_0_course_wrap_up.html │ │ ├── ru102n_6_6_1_subscribe_youtube_twitch.html │ │ ├── ru102n_6_6_2_try_redis_cloud.html │ │ ├── ru102n_6_0_0_final_exam_introduction.html │ │ └── ru102n_6_6_3_2_get_your_certificate.html │ ├── section_2 │ │ ├── ru102n_2_1_0_string_operations.html │ │ ├── ru102n_2_5_0_hashes.html │ │ ├── ru102n_2_4_0_sorted_sets.html │ │ ├── ru102n_2_2_0_redis_lists.html │ │ ├── ru102n_2_3_0_redis_sets.html │ │ ├── ru102n_2_8_0_redis_transactions.html │ │ ├── ru102n_2_9_0_pubsub.html │ │ ├── ru102n_2_6_0_redis_streams.html │ │ └── ru102n_2_0_0_overview.html │ ├── section_5 │ │ ├── ru102n_5_2_0_inserting_objects.html │ │ ├── ru102n_5_4_0_updates_and_deletion.html │ │ ├── ru102n_5_0_0_unit_overview.html │ │ └── ru102n_5_5_0_aggregations.html │ ├── section_1 │ │ ├── ru102n_1_4_0_advanced_connections.html │ │ ├── ru102n_1_2_0_getting_started_with_stack_exhange_redis.html │ │ ├── ru102n_1_3_0_interfaces_of_stack_exchange_redis.html │ │ ├── ru102n_1_3_1_i_connection_multiplexer.html │ │ ├── ru102n_1_2_2_add_stack_exchange_redis.html │ │ ├── ru102n_1_3_3_i_server.html │ │ ├── ru102n_1_4_3_connect_to_redis_sentinel.html │ │ ├── ru102n_1_3_4_i_subscriber.html │ │ ├── ru102n_1_2_4_coding_challenge_solution.html │ │ ├── ru102n_1_0_0_unit_overview.html │ │ ├── ru102n_1_4_2_connect_to_redis_cluster.html │ │ ├── ru102n_1_3_2_i_database.html │ │ ├── ru102n_1_3_5_i_transaction.html │ │ └── ru102n_1_5_0_pipelining.html │ ├── README.md │ ├── section_4 │ │ ├── ru102n_4_1_0_ad_hoc_api.html │ │ ├── ru102n_4_3_0_graph.html │ │ ├── ru102n_4_2_0_timeseries_dotnet.html │ │ └── ru102n_4_0_0_overview.html │ ├── section_3 │ │ ├── ru102n_3_0_0_overview.html │ │ ├── ru102n_3_1_0_derived_clients.html │ │ ├── ru102n_3_2_0_caching_aspnet_core.html │ │ ├── ru102n_3_2_1_employee_aggregation_example_intro.html │ │ ├── ru102n_3_4_0_distributed_sessions_aspnet_core.html │ │ ├── ru102n_3_5_0_session_state_with_aspnet.html │ │ └── ru102n_3_3_0_caching_asp_net_classic.html │ └── section_0 │ │ ├── ru102n_0_1_1_about_redis.html │ │ ├── ru102n_0_1_0_introduce_yourself.html │ │ └── ru102n_0_3_0_course_setup.html └── transcripts │ └── README.md └── LICENSE /src/section_3/section3.4/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /images_for_readme/insight_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/images_for_readme/insight_web.png -------------------------------------------------------------------------------- /src/section_3/section3.3/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="section3._3.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /src/section_3/section3.3/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.3/favicon.ico -------------------------------------------------------------------------------- /src/section_3/section3.5/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="section3._5.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /src/section_3/section3.5/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.5/favicon.ico -------------------------------------------------------------------------------- /images_for_readme/cloud_new_sub_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/images_for_readme/cloud_new_sub_1.png -------------------------------------------------------------------------------- /images_for_readme/cloud_new_sub_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/images_for_readme/cloud_new_sub_2.png -------------------------------------------------------------------------------- /images_for_readme/cloud_new_sub_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/images_for_readme/cloud_new_sub_3.png -------------------------------------------------------------------------------- /images_for_readme/cloud_new_sub_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/images_for_readme/cloud_new_sub_4.png -------------------------------------------------------------------------------- /images_for_readme/download_repo_zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/images_for_readme/download_repo_zip.png -------------------------------------------------------------------------------- /images_for_readme/insight_web_terms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/images_for_readme/insight_web_terms.png -------------------------------------------------------------------------------- /src/section_3/section3.4/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.4/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/section_3/section3.4/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._4 2 | @using section3._4.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage 2 | @model ImageSample 3 | 4 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage 2 | @model TextSample 3 | 4 |
5 | @Model.Text
6 | 
-------------------------------------------------------------------------------- /src/section_3/section3.3/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.3/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/section_3/section3.3/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.3/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/section_3/section3.3/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.3/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/section_3/section3.5/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.5/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/section_3/section3.5/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.5/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/section_3/section3.5/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.5/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | redis: 4 | container_name: redisu-ru102n 5 | image: redis/redis-stack:latest 6 | ports: 7 | - "6379:6379" 8 | - "8001:8001" -------------------------------------------------------------------------------- /src/section_3/section3.3/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.3/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/section_3/section3.5/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redislabs-training/ru-dev-path-net/HEAD/src/section_3/section3.5/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/section_3/section3.2/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/section_3/section3.4/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/section_3/section3.4/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage.ModelDescriptions 2 | @model SimpleTypeModelDescription 3 | @Model.Documentation -------------------------------------------------------------------------------- /src/section_3/section3.5/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "About"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |

Use this area to provide additional information.

8 | -------------------------------------------------------------------------------- /src/section_3/section3.2/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /src/section_3/section3.4/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | // Change the Layout path below to blend the look and feel of the help page with your existing web pages 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class SimpleTypeModelDescription : ModelDescription 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage.ModelDescriptions 2 | @model ComplexTypeModelDescription 3 | @Html.DisplayFor(m => m.Properties, "Parameters") -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class DictionaryModelDescription : KeyValuePairModelDescription 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/section_3/section3.4/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/section_3/section3.4/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace section3._4.Models; 2 | 3 | public class ErrorViewModel 4 | { 5 | public string? RequestId { get; set; } 6 | 7 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 8 | } 9 | -------------------------------------------------------------------------------- /src/section_3/section3.4/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class CollectionModelDescription : ModelDescription 4 | { 5 | public ModelDescription ElementDescription { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/section_3/section3.5/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Home Page"; 3 | } 4 | 5 |
6 |

ASP.NET Distributed Session State with Redis

7 |

Our Session Says the meaning of life is: @Session["the meaning of life"].

8 |
9 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage.ModelDescriptions 2 | @model CollectionModelDescription 3 | @if (Model.ElementDescription is ComplexTypeModelDescription) 4 | { 5 | @Html.DisplayFor(m => m.ElementDescription) 6 | } -------------------------------------------------------------------------------- /courseware/html/section_6/ru102n_6_6_3_1_get_your_certificate.html: -------------------------------------------------------------------------------- 1 |

2 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/SampleGeneration/SampleDirection.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Areas.HelpPage 2 | { 3 | /// 4 | /// Indicates whether the sample is used for request or response 5 | /// 6 | public enum SampleDirection 7 | { 8 | Request = 0, 9 | Response 10 | } 11 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace section3._3.Areas.HelpPage.ModelDescriptions 4 | { 5 | public class ParameterAnnotation 6 | { 7 | public Attribute AnnotationAttribute { get; set; } 8 | 9 | public string Documentation { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_1_0_string_operations.html: -------------------------------------------------------------------------------- 1 |

String Operations

2 | 3 |

4 | Redis Strings are the simplest of the Redis Data Structures. They map a single Redis 5 | Key to a single Redis Value. In spite of their simplicity they have a variety of 6 | capabilities that make them useful across a number of use cases. 7 |

-------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class EnumValueDescription 4 | { 5 | public string Documentation { get; set; } 6 | 7 | public string Name { get; set; } 8 | 9 | public string Value { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Error 6 | 7 | 8 |
9 |

Error.

10 |

An error occurred while processing your request.

11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Error 6 | 7 | 8 |
9 |

Error.

10 |

An error occurred while processing your request.

11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /courseware/html/section_5/ru102n_5_2_0_inserting_objects.html: -------------------------------------------------------------------------------- 1 |

Inserting Objects with Redis OM

2 | 3 |

4 | Redis OM enables you to insert your modeled objects into Redis. As we'll find out in this section, doing so is very straightforward, 5 | but there are some considerations you'll want to keep in mind as you are inserting your objects into Redis. 6 |

-------------------------------------------------------------------------------- /src/section_3/section3.3/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace section3._3 5 | { 6 | public class FilterConfig 7 | { 8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 9 | { 10 | filters.Add(new HandleErrorAttribute()); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewBag.Title 7 | @RenderSection("scripts", required: false) 8 | 9 | 10 | @RenderBody() 11 | 12 | -------------------------------------------------------------------------------- /src/section_3/section3.5/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace section3._5 5 | { 6 | public class FilterConfig 7 | { 8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 9 | { 10 | filters.Add(new HandleErrorAttribute()); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage 2 | @model InvalidSample 3 | 4 | @if (HttpContext.Current.IsDebuggingEnabled) 5 | { 6 |
7 |

@Model.ErrorMessage

8 |
9 | } 10 | else 11 | { 12 |

Sample not available.

13 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class KeyValuePairModelDescription : ModelDescription 4 | { 5 | public ModelDescription KeyModelDescription { get; set; } 6 | 7 | public ModelDescription ValueModelDescription { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace section3._3.Areas.HelpPage.ModelDescriptions 5 | { 6 | public interface IModelDocumentationProvider 7 | { 8 | string GetDocumentation(MemberInfo member); 9 | 10 | string GetDocumentation(Type type); 11 | } 12 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Models/Employee.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace section3._3.Models 7 | { 8 | public class Employee 9 | { 10 | public int EmployeeId { get; set; } 11 | public string Name { get; set; } 12 | public List Sales { get; } = new List(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/section_3/section3.4/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 |
6 |

Redis Distributed Session State ASP.NET Core Basic Demo

7 |
8 | 9 |
10 |

Our Session tells us that the meaning of life is: @Context.Session.GetString("meaning of life")

11 |
12 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Models/Sale.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace section3._3.Models 7 | { 8 | public class Sale 9 | { 10 | public int SaleId { get; set; } 11 | public int Total { get; set; } 12 | 13 | public int EmployeeId { get; set; } 14 | public Employee Employee { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_5_0_hashes.html: -------------------------------------------------------------------------------- 1 |

Redis Hashes with StackExchange.Redis

2 | 3 |

4 | Hashes are a simple, but very powerful data structure available within Redis. Hashes are similar to dictionaries in that they allow you to 5 | organize a set of key-value pairs at a particular key within Redis. 6 |

7 |

8 | In this section we'll look at how to use hashes using the StackExchange.Redis 9 | library. 10 |

-------------------------------------------------------------------------------- /src/section_3/section3.3/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | 7 | namespace section3._3.Controllers 8 | { 9 | public class HomeController : Controller 10 | { 11 | public ActionResult Index() 12 | { 13 | ViewBag.Title = "Home Page"; 14 | 15 | return View(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/ModelDescription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace section3._3.Areas.HelpPage.ModelDescriptions 4 | { 5 | /// 6 | /// Describes a type model. 7 | /// 8 | public abstract class ModelDescription 9 | { 10 | public string Documentation { get; set; } 11 | 12 | public Type ModelType { get; set; } 13 | 14 | public string Name { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/section_3/section3.4/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 14px; 3 | } 4 | 5 | @media (min-width: 768px) { 6 | html { 7 | font-size: 16px; 8 | } 9 | } 10 | 11 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 12 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 13 | } 14 | 15 | html { 16 | position: relative; 17 | min-height: 100%; 18 | } 19 | 20 | body { 21 | margin-bottom: 60px; 22 | } -------------------------------------------------------------------------------- /src/section_4/section4.3/section4.3.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section4._3 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_5/section5.1/section5.1.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section5._1 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_5/section5.2/section5.2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section5._2 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_5/section5.3/section5.3.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section5._3 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_5/section5.4/section5.4.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section5._4 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_5/section5.5/section5.5.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section5._5 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_3/section3.4/section3.4.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | section3._4 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/section_4/section4.2/section4.2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section4._2 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_4_0_advanced_connections.html: -------------------------------------------------------------------------------- 1 |

Advanced Connections

2 | 3 |

4 | In this section we'll explore connecting the Multiplexer to instances of Redis 5 | that are beyond the unencrypted single server variety. We'll explore: 6 |

7 | 8 | 13 | -------------------------------------------------------------------------------- /src/section_1/section1.2/section1.2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section1._2 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_1/section1.5/section1.5.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section1._5 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.1/section2.1.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._1 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.2/section2.2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._2 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.3/section2.3.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._3 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.4/section2.4.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._4 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.5/section2.5.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._5 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.6/section2.6.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._6 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.7/section2.7.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._7 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.8/section2.8.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._8 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_2/section2.9/section2.9.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section2._9 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage.ModelDescriptions 2 | @model DictionaryModelDescription 3 | Dictionary of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] 4 | and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage.ModelDescriptions 2 | @model KeyValuePairModelDescription 3 | Pair of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] 4 | and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace section3._3.Areas.HelpPage.ModelDescriptions 4 | { 5 | public class ComplexTypeModelDescription : ModelDescription 6 | { 7 | public ComplexTypeModelDescription() 8 | { 9 | Properties = new Collection(); 10 | } 11 | 12 | public Collection Properties { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace section3._3.Areas.HelpPage.ModelDescriptions 5 | { 6 | public class EnumTypeModelDescription : ModelDescription 7 | { 8 | public EnumTypeModelDescription() 9 | { 10 | Values = new Collection(); 11 | } 12 | 13 | public Collection Values { get; private set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/section_3/section3.5/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Contact"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P: 11 | 425.555.0100 12 |
13 | 14 |
15 | Support: Support@example.com
16 | Marketing: Marketing@example.com 17 |
-------------------------------------------------------------------------------- /courseware/html/README.md: -------------------------------------------------------------------------------- 1 | # RU102N: Redis for .NET Developers: HTML Documents 2 | 3 | This folder contains the HTML documents used for the RU102N: Redis for .NET Developers course from Redis University. 4 | 5 | * [Sign up for the course here](https://university.redis.com/courses/ru102n/). 6 | * If you spot errors, or feel you can improve the materials, please [open an issue](https://github.com/redislabs-training/ru102n/issues) or [submit a pull request](https://github.com/redislabs-training/ru102n/pulls). 7 | 8 | Thanks, 9 | 10 | The Redis Developer Relations team. -------------------------------------------------------------------------------- /courseware/html/section_6/ru102n_6_6_0_course_wrap_up.html: -------------------------------------------------------------------------------- 1 |

Congratulations, you're at the end of the course! We hope that you enjoyed it, and that you were successful with the final exam. If so, please click Next for instructions showing how to add your certificate of completion to your LinkedIn profile.

2 |

Thanks for spending your time with us, and we hope to see you on another Redis University course soon.

3 |

Steve Lorello on behalf of the Redis University team.

-------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_4_0_sorted_sets.html: -------------------------------------------------------------------------------- 1 |

Sorted Sets

2 | 3 |

4 | Sorted Sets are similar conceptually to sets, however unlike regular sets, sorted sets 5 | store and retrieve members in order using the member's score. In this section we'll learn 6 | how to use Sorted Sets using the StackExchange.Redis library. We'll learn how to add to, 7 | combine, move between, and enumerate members with sorted sets. Read more about Sorted Sets on redis.io. 8 |

-------------------------------------------------------------------------------- /src/section_5/section5.1/Program.cs: -------------------------------------------------------------------------------- 1 | using Redis.OM; 2 | using section5._1; 3 | 4 | // TODO for Coding Challenge Start here on starting-point branch 5 | var provider = new RedisConnectionProvider("redis://localhost:6379"); 6 | 7 | provider.Connection.DropIndexAndAssociatedRecords(typeof(Sale)); 8 | provider.Connection.DropIndexAndAssociatedRecords(typeof(Employee)); 9 | 10 | await provider.Connection.CreateIndexAsync(typeof(Sale)); 11 | await provider.Connection.CreateIndexAsync(typeof(Employee)); 12 | 13 | Console.WriteLine("Created indexes."); 14 | // end coding challenge 15 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Set width on the form input elements since they're 100% wide by default */ 13 | input, 14 | select, 15 | textarea { 16 | max-width: 280px; 17 | } 18 | 19 | .navbar-inverse .navbar-toggle:hover, 20 | .navbar-inverse .navbar-toggle:focus { 21 | background-color: #777; 22 | border-color: #fff 23 | } 24 | -------------------------------------------------------------------------------- /courseware/html/section_5/ru102n_5_4_0_updates_and_deletion.html: -------------------------------------------------------------------------------- 1 |

Updating and Deleting Documents with Redis OM

2 | 3 |

4 | In this section we'll look at how Redis OM performs the "U" and "D" of CRUD: Updates, and Deletes. 5 |

6 |

7 | Unlike a traditional 8 | relational database Redis Stack does not have the the ability to natively query, update, and delete records in isolation on the Redis 9 | Server, so we need to use the client to do so. Fortunately, Redis OM makes these tasks pretty straightforward as we'll be exploring in 10 | this section. 11 |

-------------------------------------------------------------------------------- /src/section_1/section1.2/Program.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | 3 | // TODO for Coding Challenge Start here on starting-point branch 4 | var options = new ConfigurationOptions 5 | { 6 | // add and update parameters as needed 7 | EndPoints = {"localhost:6379"} 8 | }; 9 | 10 | // initalize a multiplexer with ConnectionMultiplexer.Connect() 11 | var muxer = ConnectionMultiplexer.Connect(options); 12 | 13 | // get an IDatabase here with GetDatabase 14 | var db = muxer.GetDatabase(); 15 | 16 | // add ping here 17 | Console.WriteLine($"ping: {db.Ping().TotalMilliseconds} ms"); 18 | // end programming challenge -------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_2_0_redis_lists.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Working with Redis Lists

6 | 7 |

8 | Redis Lists are doubly linked lists that allow you to push and pop from the front and 9 | tail. This allows you to build what are in essence queues and stacks of Redis Strings. 10 | The StackExchange.Redis library provides an intuitive interface for working with Lists 11 | in the IDatabase that we will go over in this section. 12 |

-------------------------------------------------------------------------------- /courseware/transcripts/README.md: -------------------------------------------------------------------------------- 1 | # RU102N: Redis for .NET Developers: Video Transcripts 2 | 3 | This folder contains the video transcripts used for the RU102N: Redis for .NET Developers course from Redis University. These files are in [SubRip](https://en.wikipedia.org/wiki/SubRip) format. 4 | 5 | * [Sign up for the course here](https://university.redis.com/courses/ru102n/). 6 | * If you spot errors, or feel you can improve the materials, please [open an issue](https://github.com/redislabs-training/ru102n/issues) or [submit a pull request](https://github.com/redislabs-training/ru102n/pulls). 7 | 8 | Thanks, 9 | 10 | The Redis Developer Relations team. -------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_3_0_redis_sets.html: -------------------------------------------------------------------------------- 1 |

Redis Sets

2 | 3 |

4 | Redis Sets are an implementation of a mathematical set. 5 | Like mathematical sets they have a number of key properties: 6 |

7 | 8 |
    9 |
  • They are unordered.
  • 10 |
  • They do not allow duplication, so there are no repeated members.
  • 11 |
  • They can be combined together to create new sets.
  • 12 |
13 | 14 |

15 | In this section we'll learn about how to leverage these properties using Redis Sets in .NET. 16 |

-------------------------------------------------------------------------------- /src/section_4/section4.1/section4.1.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | section4._1 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Always 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /courseware/html/section_4/ru102n_4_1_0_ad_hoc_api.html: -------------------------------------------------------------------------------- 1 |

The Ad-Hoc API

2 | 3 |

4 | The Ad-Hoc API exposed by StackExchange.Redis is a powerful tool that allows you to execute Arbitrary commands against Redis. 5 |

6 |

7 | Each of the 8 | Redis Stack libraries we'll be looking at in the forthcoming sections use this Ad-Hoc API to execute commands against Redis and to parse 9 | their results. In this section we'll look at how to run ad-hoc commands against Redis and parse their results. 10 |

11 |

12 | We'll then use these ad-hoc 13 | commands to work with some of the Probabilistic Data Structures in Redis. 14 |

-------------------------------------------------------------------------------- /courseware/html/section_5/ru102n_5_0_0_unit_overview.html: -------------------------------------------------------------------------------- 1 |

Unit Overview

2 | 3 |

4 | Welcome to Unit 5 of RU102N. In this unit, we'll be covering the last two primary use cases of Redis Stack: 5 |

6 | 7 |
    8 |
  • Storage and Retrieval of serialized JSON documents
  • 9 |
  • Indexing and Querying JSON documents stored in Redis
  • 10 |
11 | 12 |

13 | We'll be learning how to do all of this using Redis OM .NET, an Object Mapping library that 14 | facilities the storage and retrieval of your classes in Redis using LINQ. 15 |

-------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_2_0_getting_started_with_stack_exhange_redis.html: -------------------------------------------------------------------------------- 1 | 4 |

5 | Ok, so let's get started with StackExchange.Redis. In this bit, we'll look at some of 6 | the fundamental architectural choices of StackExchange.Redis. We'll discuss how to add it 7 | to your project, and then we'll go over how to connect to Redis from StackExchange.Redis 8 | and send the most basic of Redis Commands: PING. 9 |

-------------------------------------------------------------------------------- /src/section_3/section3.3/Models/SalesContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity; 3 | using System.Data.Entity.ModelConfiguration.Conventions; 4 | 5 | namespace section3._3.Models 6 | { 7 | public class SalesContext : DbContext 8 | { 9 | public DbSet Employees { get; set; } 10 | 11 | public DbSet Sales { get; set; } 12 | 13 | public SalesContext() : base("SalesContext") 14 | { 15 | } 16 | 17 | protected override void OnModelCreating(DbModelBuilder modelBuilder) 18 | { 19 | modelBuilder.Conventions.Remove(); 20 | } 21 | 22 | } 23 | } -------------------------------------------------------------------------------- /src/section_3/section3.5/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Optimization; 7 | using System.Web.Routing; 8 | 9 | namespace section3._5 10 | { 11 | public class MvcApplication : System.Web.HttpApplication 12 | { 13 | protected void Application_Start() 14 | { 15 | AreaRegistration.RegisterAllAreas(); 16 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 17 | RouteConfig.RegisterRoutes(RouteTable.Routes); 18 | BundleConfig.RegisterBundles(BundleTable.Bundles); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace section3._3.Areas.HelpPage.ModelDescriptions 4 | { 5 | /// 6 | /// Use this attribute to change the name of the generated for a type. 7 | /// 8 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = false, Inherited = false)] 9 | public sealed class ModelNameAttribute : Attribute 10 | { 11 | public ModelNameAttribute(string name) 12 | { 13 | Name = name; 14 | } 15 | 16 | public string Name { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/ParameterDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace section3._3.Areas.HelpPage.ModelDescriptions 5 | { 6 | public class ParameterDescription 7 | { 8 | public ParameterDescription() 9 | { 10 | Annotations = new Collection(); 11 | } 12 | 13 | public Collection Annotations { get; private set; } 14 | 15 | public string Documentation { get; set; } 16 | 17 | public string Name { get; set; } 18 | 19 | public ModelDescription TypeDescription { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace section3._3 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | 16 | routes.MapRoute( 17 | name: "Default", 18 | url: "{controller}/{action}/{id}", 19 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 20 | ); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/section_3/section3.5/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace section3._5 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | 16 | routes.MapRoute( 17 | name: "Default", 18 | url: "{controller}/{action}/{id}", 19 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 20 | ); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/ResourceModel.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using section3._3.Areas.HelpPage.ModelDescriptions 3 | @model ModelDescription 4 | 5 | 6 |
7 | 14 |

@Model.Name

15 |

@Model.Documentation

16 |
17 | @Html.DisplayFor(m => Model) 18 |
19 |
20 | -------------------------------------------------------------------------------- /src/section_3/section3.3/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http; 5 | 6 | namespace section3._3 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | 14 | // Web API routes 15 | config.MapHttpAttributeRoutes(); 16 | 17 | config.Routes.MapHttpRoute( 18 | name: "DefaultApi", 19 | routeTemplate: "api/{controller}/{id}", 20 | defaults: new { id = RouteParameter.Optional } 21 | ); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /courseware/html/section_3/ru102n_3_0_0_overview.html: -------------------------------------------------------------------------------- 1 |

Redis the ASP.NET Way

2 | 3 |

4 | We'd be remiss if we went through an entire course about Redis, and did not mention ASP.NET or ASP.NET Core. In this unit we'll be looking 5 | at two of the more popular integrations of Redis in the ASP.NET ecosystem. 6 |

7 | 8 |
    9 |
  • Caching
  • 10 |
  • Session State Management
  • 11 |
12 | 13 |

14 | We'll be looking at how to do both of these in both ASP.NET and ASP.NET Core. We'll prioritize core as that's definitely the future of 15 | the ecosystem, but there are always a distinct possibility that you might be working on an older ASP.NET project that could use an integration. 16 |

-------------------------------------------------------------------------------- /courseware/html/section_4/ru102n_4_3_0_graph.html: -------------------------------------------------------------------------------- 1 |

Working with Graph Data in .NET

2 | 3 |

4 | Another feature of Redis Stack is the ability to work with Graph Databases. There are a few ways that 5 | you can interact with Graph Databases in Redis Stack: 6 |

7 | 8 |
    9 |
  • NRedisGraph: another extension library based on StackExchange.Redis wrapping the Redis Graph Commands
  • 10 |
  • You can use the Ad-Hoc API to send Graph Commands Directly
  • 11 |
  • NRedisStack: A newer library based on StackExchange.Redis which provides access to all the commands in Stack
  • 12 |
13 | 14 |

15 | In this section, we'll work with NRedisGraph to run our commands and use the results of some basic Graph Queries. 16 |

-------------------------------------------------------------------------------- /courseware/html/section_4/ru102n_4_2_0_timeseries_dotnet.html: -------------------------------------------------------------------------------- 1 |

Time Series With .NET

2 | 3 |

4 | Redis Stack supports a Time Series Data Structure, which allows your application to ingest and utilize time tagged numeric data in a series. 5 | There are three different modes that you can use to interact with the Time Series data structure in Redis. 6 |

7 | 8 |
    9 |
  • NRedisTimeSeries: A specialized library based on StackExchange.Redis
  • 10 |
  • The Ad-Hoc API
  • 11 |
  • NRedisStack: A newer library based on StackExchange.Redis which provides access to all the commands in stack
  • 12 |
13 | 14 |

15 | In this section we'll be looking at how to use NRedisTimeSeries to interact with the Time Series Data Structure in Redis. 16 |

-------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/Api.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using section3._3.Areas.HelpPage.Models 3 | @model HelpPageApiModel 4 | 5 | @{ 6 | var description = Model.ApiDescription; 7 | ViewBag.Title = description.HttpMethod.Method + " " + description.RelativePath; 8 | } 9 | 10 | 11 |
12 | 19 |
20 | @Html.DisplayForModel() 21 |
22 |
23 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Http; 6 | using System.Web.Mvc; 7 | using System.Web.Optimization; 8 | using System.Web.Routing; 9 | 10 | namespace section3._3 11 | { 12 | public class WebApiApplication : System.Web.HttpApplication 13 | { 14 | protected void Application_Start() 15 | { 16 | AreaRegistration.RegisterAllAreas(); 17 | GlobalConfiguration.Configure(WebApiConfig.Register); 18 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 19 | RouteConfig.RegisterRoutes(RouteTable.Routes); 20 | BundleConfig.RegisterBundles(BundleTable.Bundles); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Override the default bootstrap behavior where horizontal description lists 13 | will truncate terms that are too long to fit in the left column 14 | */ 15 | .dl-horizontal dt { 16 | white-space: normal; 17 | } 18 | 19 | /* Set width on the form input elements since they're 100% wide by default */ 20 | input, 21 | select, 22 | textarea { 23 | max-width: 280px; 24 | } 25 | 26 | .navbar-inverse .navbar-toggle:hover, 27 | .navbar-inverse .navbar-toggle:focus { 28 | background-color: #777; 29 | border-color: #fff 30 | } 31 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | 7 | namespace section3._5.Controllers 8 | { 9 | public class HomeController : Controller 10 | { 11 | public ActionResult Index() 12 | { 13 | Session["the meaning of life"] = 42; 14 | return View(); 15 | } 16 | 17 | public ActionResult About() 18 | { 19 | ViewBag.Message = "Your application description page."; 20 | 21 | return View(); 22 | } 23 | 24 | public ActionResult Contact() 25 | { 26 | ViewBag.Message = "Your contact page."; 27 | 28 | return View(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Redis.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Web; 6 | 7 | namespace section3._3 8 | { 9 | public class Redis 10 | { 11 | private static readonly Lazy LazyMuxer; 12 | 13 | static Redis() 14 | { 15 | var options = new ConfigurationOptions 16 | { 17 | EndPoints = { "localhost:6379" }, 18 | Password = "" 19 | }; 20 | 21 | LazyMuxer = new Lazy(() => ConnectionMultiplexer.Connect(options)); 22 | } 23 | 24 | public static ConnectionMultiplexer Muxer => LazyMuxer.Value; 25 | public static IDatabase Database => Muxer.GetDatabase(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/HelpPageAreaRegistration.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using System.Web.Mvc; 3 | 4 | namespace section3._3.Areas.HelpPage 5 | { 6 | public class HelpPageAreaRegistration : AreaRegistration 7 | { 8 | public override string AreaName 9 | { 10 | get 11 | { 12 | return "HelpPage"; 13 | } 14 | } 15 | 16 | public override void RegisterArea(AreaRegistrationContext context) 17 | { 18 | context.MapRoute( 19 | "HelpPage_Default", 20 | "Help/{action}/{apiId}", 21 | new { controller = "Help", action = "Index", apiId = UrlParameter.Optional }); 22 | 23 | HelpPageConfig.Register(GlobalConfiguration.Configuration); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage.ModelDescriptions 2 | @model EnumTypeModelDescription 3 | 4 |

Possible enumeration values:

5 | 6 | 7 | 8 | 9 | 10 | 11 | @foreach (EnumValueDescription value in Model.Values) 12 | { 13 | 14 | 15 | 18 | 21 | 22 | } 23 | 24 |
NameValueDescription
@value.Name 16 |

@value.Value

17 |
19 |

@value.Documentation

20 |
-------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_3_0_interfaces_of_stack_exchange_redis.html: -------------------------------------------------------------------------------- 1 | 4 |

The Interfaces of StackExchange.Redis

5 |

6 | The public API of StackExchange.Redis is broken up across several critical 7 | interfaces. We'll briefly go over each of them in this section. You've 8 | actually already touched two of them IConnectionMultiplexer 9 | and IDatabase. 10 |

11 | 12 |

The Interfaces

13 | 14 |
    15 |
  • IConnectionMultiplexer
  • 16 |
  • IDatabase
  • 17 |
  • IServer
  • 18 |
  • ISubscriber
  • 19 |
  • ITransaction
  • 20 |
-------------------------------------------------------------------------------- /courseware/html/section_5/ru102n_5_5_0_aggregations.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Aggregations with Redis OM

6 | 7 |

8 | In this section we'll look at another really useful feature of Redis Stack and Redis OM, the ability to aggregate data within your 9 | documents together. Aggregations allow you to build pipelines for your documents to perform tasks such as: 10 |

11 | 12 |
    13 |
  • Query your Documents the same way you would with a IRedisCollection.
  • 14 |
  • Apply a variety of mathematical and string functions to your data.
  • 15 |
  • Group like documents together.
  • 16 |
  • Run Reductions against groups of documents.
  • 17 |
  • Further filter documents based off the results in your pipeline.
  • 18 |
19 | 20 | -------------------------------------------------------------------------------- /courseware/html/section_0/ru102n_0_1_1_about_redis.html: -------------------------------------------------------------------------------- 1 |

Redis, Inc is the home of Redis. We built Redis University, we sponsor the development of OSS Redis, and we're the creators of Redis Enterprise and Redis Cloud (for AWS, GCP, and Azure), the most sophisticated Redis deployments on the planet.

2 |

Need a cloud-based Redis now? We welcome you to sign up for a free Redis Cloud Essentials instance.

-------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_3_1_i_connection_multiplexer.html: -------------------------------------------------------------------------------- 1 | 4 |

IConnectionMultiplexer

5 | 6 |

7 | The IConnectionMultiplexer is responsible for maintaining all of the 8 | connections to Redis. As I described in the previous section. It routes 9 | all the commands to Redis through a single connection for interactive commands, 10 | and a separate connection for subscription, which we'll discuss more in 11 | depth later. 12 |

13 | 14 |

15 | The IConnectionMultiplexer is responsible for exposing a simple interface 16 | to get other critical interfaces of the library. Including the IDatabase, 17 | ISubscriber, and IServer. 18 |

-------------------------------------------------------------------------------- /courseware/html/section_0/ru102n_0_1_0_introduce_yourself.html: -------------------------------------------------------------------------------- 1 |

We're so excited that you're taking this course and welcome you to Redis University!

2 |

Did you know that hundreds of people from around the world are also taking this course at the same time as you? They are! I encourage you to stop by our Discord chat and introduce yourself.

3 |

Once you've signed up, you'll find a channel for introductions at #introduce-yourself.

4 |

And every course also has its own dedicated channel where you can talk to other people taking the same course.

5 |

So come on by and say hello to your peers and the Redis Developer Relations team -- we look forward to meeting you.

6 |

PS: Research suggests that learning with a group of people will boost your learning!

-------------------------------------------------------------------------------- /src/section_3/section3.5/README.md: -------------------------------------------------------------------------------- 1 | # Section 3.5 Special Instructions 2 | 3 | In section 3.5 we explore caching using Redis in ASP.NET, this section is completely optional as ASP.NET is now (somewhat) legacy having been largely supplanted by ASP.NET Core. None of the materials here will be included in the exam required to pass this course so if this is of no interest to you, feel free to skip. That said it should still be instructive for those of you still interested in ASP.NET to learn how to use Redis in this context, so please bear in mind that there are some additional requirements for this section. 4 | 5 | ## Additional Prerequisites 6 | 7 | * Because this uses ASP.NET and the .NET Framework, this needs to run on a Windows (preferably Windows 10+) machine or VM. 8 | * You must have the [.NET Framework 4.8.1 SDK](https://dotnet.microsoft.com/en-us/download/visual-studio-sdks) installed 9 | * It's highly recommended that you use either Visual Studio or Rider for this section. -------------------------------------------------------------------------------- /courseware/html/section_0/ru102n_0_3_0_course_setup.html: -------------------------------------------------------------------------------- 1 |

To take this course successfully, you will need:

2 |
    3 |
  • The .NET 7 SDK.
  • 4 |
  • Docker installed and running. We'll be using this at a minimum for more advanced Redis Configurations in the connections section of this course.
  • 5 |
  • A Redis Stack instance. This can either be done in Docker or in the cloud.
  • 6 |
7 | 8 |

Throughout the course, I've provided example code written in C# that you can run.

9 | 10 |

If you don't have one or more of the prerequisites, you can follow the setup instructions in the course repo's README.

11 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Migrations/202211301905089_initial.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace section3._3.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.4.4")] 10 | public sealed partial class initial : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(initial)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "202211301905089_initial"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Migrations/202211302024328_initial.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace section3._3.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.4.4")] 10 | public sealed partial class initial : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(initial)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "202211302024328_initial"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Controllers/ValuesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Web.Http; 7 | 8 | namespace section3._3.Controllers 9 | { 10 | public class ValuesController : ApiController 11 | { 12 | // GET api/values 13 | public IEnumerable Get() 14 | { 15 | return new string[] { "value1", "value2" }; 16 | } 17 | 18 | // GET api/values/5 19 | public string Get(int id) 20 | { 21 | return "value"; 22 | } 23 | 24 | // POST api/values 25 | public void Post([FromBody] string value) 26 | { 27 | } 28 | 29 | // PUT api/values/5 30 | public void Put(int id, [FromBody] string value) 31 | { 32 | } 33 | 34 | // DELETE api/values/5 35 | public void Delete(int id) 36 | { 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/section_3/section3.4/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Microsoft.AspNetCore.Mvc; 3 | using section3._4.Models; 4 | 5 | namespace section3._4.Controllers; 6 | 7 | public class HomeController : Controller 8 | { 9 | private readonly ILogger _logger; 10 | 11 | public HomeController(ILogger logger) 12 | { 13 | _logger = logger; 14 | } 15 | 16 | public IActionResult Index() 17 | { 18 | var fourtyTwo = 42; 19 | HttpContext.Session.SetString("meaning of life", fourtyTwo.ToString()); 20 | return View(); 21 | } 22 | 23 | public IActionResult Privacy() 24 | { 25 | return View(); 26 | } 27 | 28 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 29 | public IActionResult Error() 30 | { 31 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/section_3/section3.4/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /courseware/html/section_3/ru102n_3_1_0_derived_clients.html: -------------------------------------------------------------------------------- 1 |

The Derived Client Ecosystem

2 | 3 |

4 | In the .NET Redis Ecosystem, there are a variety of clients that are derived clients of StackExchange.Redis. In this unit we'll be looking 5 | at a few of them that are specifically built around ASP.NET and ASP.NET Core. 6 |

7 | 8 |

9 | In general the derived clients of StackExchange.Redis take many of the same parameters as StackExchange.Redis does. However there are 10 | some important exceptions which we'll explore later in this unit. 11 |

12 |

13 | In general these clients are configured along with the rest of your application, 14 | in the case of ASP.NET Core, they are typically best configured at application startup when configuring your application services. 15 |

16 |

17 | Both of 18 | the cases we are going to explore here are heavily integrated into the ASP.NET stack, and you'll be surprised with how simple it is to get 19 | up and running with them. 20 |

-------------------------------------------------------------------------------- /courseware/html/section_4/ru102n_4_0_0_overview.html: -------------------------------------------------------------------------------- 1 |

Redis Stack and the Ad-Hoc API

2 | 3 |

4 | Welcome to Unit 4 of RU102N. In this section we'll be taking a deeper look at Redis Stack. 5 |

6 | 7 |

8 | Redis Stack enhances Redis with additional data structures and capabilities. These include: 9 |

10 | 11 |
    12 |
  • Probabilistic Data Structures
  • 13 |
  • The ability to ingest and query Time Series Data
  • 14 |
  • The ability to create and Query Graph data models with Cypher query language
  • 15 |
  • The ability to store and retrieve serialized JSON documents directly
  • 16 |
  • The ability to Query across hashes and JSON Documents
  • 17 |
18 | 19 |

20 | Unit 4 will focus on the first three, while Unit 5 will focus exclusively on the last two. We'll explore the client ecosystem 21 | surrounding Redis Stack, and how you can leverage the ad-hoc API provided by StackExchange.Redis to execute any command you need to 22 | against Redis. 23 |

24 | -------------------------------------------------------------------------------- /courseware/html/section_6/ru102n_6_6_1_subscribe_youtube_twitch.html: -------------------------------------------------------------------------------- 1 |

Watch on YouTube: Redis Explained

2 |

Redis Explained is a series of short, fun videos. Each episode covers a different Redis data type or topic. We'd love you to check out our YouTube channel and subscribe for regular updates!

3 |
4 |

5 |

Twitch.tv Live Streams

6 |

We also stream regularly on our Twitch.tv channel - follow us to be notified when we're live.

7 |

Check out our complete event schedule on the Redis Developer site.

-------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_2_2_add_stack_exchange_redis.html: -------------------------------------------------------------------------------- 1 | 4 |

5 | There are of course a variety of ways to Add StackExchange.Redis to your project. 6 | From the base directory of the github project, first checkout the starting-point tag, 7 | this will leave you with a skeleton of the projects we are going to build in this course. 8 | Move to the src/section_1/section1.2 directory with cd src/section_1/section1.2. 9 |

10 |

11 | This directory contains section1.2.csproj, which is a more or less empty csproj 12 | file. To Add StackExchange.Redis to this project, follow any of the methods outlined in 13 | NuGet, or just run: 14 | dotnet add package StackExchange.Redis 15 |

-------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Net.Http.Headers 2 | @model Dictionary 3 | 4 | @{ 5 | // Group the samples into a single tab if they are the same. 6 | Dictionary samples = Model.GroupBy(pair => pair.Value).ToDictionary( 7 | pair => String.Join(", ", pair.Select(m => m.Key.ToString()).ToArray()), 8 | pair => pair.Key); 9 | var mediaTypes = samples.Keys; 10 | } 11 |
12 | @foreach (var mediaType in mediaTypes) 13 | { 14 |

@mediaType

15 |
16 | Sample: 17 | @{ 18 | var sample = samples[mediaType]; 19 | if (sample == null) 20 | { 21 |

Sample not available.

22 | } 23 | else 24 | { 25 | @Html.DisplayFor(s => sample); 26 | } 27 | } 28 |
29 | } 30 |
-------------------------------------------------------------------------------- /src/section_3/section3.2/SalesContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace section3._2; 4 | 5 | public class SalesContext : DbContext 6 | { 7 | public DbSet Employees { get; set; } 8 | public DbSet Sales { get; set; } 9 | public string DbPath { get; set; } 10 | 11 | public SalesContext() 12 | { 13 | var folder = Environment.SpecialFolder.LocalApplicationData; 14 | var path = Environment.GetFolderPath(folder); 15 | DbPath = System.IO.Path.Join(path, "Sales.db"); 16 | } 17 | 18 | protected override void OnConfiguring(DbContextOptionsBuilder options) => 19 | options.UseSqlite($"Data Source={DbPath}"); 20 | } 21 | 22 | public class Employee 23 | { 24 | public int EmployeeId { get; set; } 25 | public string Name { get; set; } 26 | public List Sales { get; } = new(); 27 | } 28 | 29 | public class Sale 30 | { 31 | public int SaleId { get; set; } 32 | public int Total { get; set; } 33 | 34 | public int EmployeeId { get; set; } 35 | public Employee Employee { get; set; } 36 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/SampleGeneration/TextSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace section3._3.Areas.HelpPage 4 | { 5 | /// 6 | /// This represents a preformatted text sample on the help page. There's a display template named TextSample associated with this class. 7 | /// 8 | public class TextSample 9 | { 10 | public TextSample(string text) 11 | { 12 | if (text == null) 13 | { 14 | throw new ArgumentNullException("text"); 15 | } 16 | Text = text; 17 | } 18 | 19 | public string Text { get; private set; } 20 | 21 | public override bool Equals(object obj) 22 | { 23 | TextSample other = obj as TextSample; 24 | return other != null && Text == other.Text; 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return Text.GetHashCode(); 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return Text; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.cshtml: -------------------------------------------------------------------------------- 1 | @using section3._3.Areas.HelpPage.ModelDescriptions 2 | @model Type 3 | @{ 4 | ModelDescription modelDescription = ViewBag.modelDescription; 5 | if (modelDescription is ComplexTypeModelDescription || modelDescription is EnumTypeModelDescription) 6 | { 7 | if (Model == typeof(Object)) 8 | { 9 | @:Object 10 | } 11 | else 12 | { 13 | @Html.ActionLink(modelDescription.Name, "ResourceModel", "Help", new { modelName = modelDescription.Name }, null) 14 | } 15 | } 16 | else if (modelDescription is CollectionModelDescription) 17 | { 18 | var collectionDescription = modelDescription as CollectionModelDescription; 19 | var elementDescription = collectionDescription.ElementDescription; 20 | @:Collection of @Html.DisplayFor(m => elementDescription.ModelType, "ModelDescriptionLink", new { modelDescription = elementDescription }) 21 | } 22 | else 23 | { 24 | @Html.DisplayFor(m => modelDescription) 25 | } 26 | } -------------------------------------------------------------------------------- /src/section_3/section3.4/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:64745", 7 | "sslPort": 44380 8 | } 9 | }, 10 | "profiles": { 11 | "http": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "applicationUrl": "http://localhost:5097", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "https": { 21 | "commandName": "Project", 22 | "dotnetRunMessages": true, 23 | "launchBrowser": true, 24 | "applicationUrl": "https://localhost:7084;http://localhost:5097", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | }, 29 | "IIS Express": { 30 | "commandName": "IISExpress", 31 | "launchBrowser": true, 32 | "environmentVariables": { 33 | "ASPNETCORE_ENVIRONMENT": "Development" 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_8_0_redis_transactions.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Redis Transactions

6 | 7 |

8 | Transactions in Redis allow you to apply multiple commands in in isolation to Redis 9 | sequentially. StackExchange.Redis exposes the ITransaction which operationally is similar to the IBatch 10 | in that it allows you to dispatch a series of tasks, which you can then await after calling the Execute method 11 | to run the transaction. 12 |

13 |

14 | Unlike IBatch however, all commands sent through a transaction are guaranteed to be executed 15 | sequentially. And you can apply some limited conditions to the transaction to allow it to self terminate if a watched 16 | key changes. In this section we'll learn how to work with Transactions in StackExchange.Redis. 17 |

-------------------------------------------------------------------------------- /src/section_3/section3.4/Views/Shared/_Layout.cshtml.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | a { 11 | color: #0077cc; 12 | } 13 | 14 | .btn-primary { 15 | color: #fff; 16 | background-color: #1b6ec2; 17 | border-color: #1861ac; 18 | } 19 | 20 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 21 | color: #fff; 22 | background-color: #1b6ec2; 23 | border-color: #1861ac; 24 | } 25 | 26 | .border-top { 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | .border-bottom { 30 | border-bottom: 1px solid #e5e5e5; 31 | } 32 | 33 | .box-shadow { 34 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 35 | } 36 | 37 | button.accept-policy { 38 | font-size: 1rem; 39 | line-height: inherit; 40 | } 41 | 42 | .footer { 43 | position: absolute; 44 | bottom: 0; 45 | width: 100%; 46 | white-space: nowrap; 47 | line-height: 60px; 48 | } 49 | -------------------------------------------------------------------------------- /courseware/html/section_6/ru102n_6_6_2_try_redis_cloud.html: -------------------------------------------------------------------------------- 1 | 15 |

Try Redis Enterprise in the Cloud for Free!

16 |

Thanks for completing this Redis University Course. We hope you enjoyed learning more about the world's most loved database. Here at Redis, we're excited to offer you the opportunity to experience Redis Enterprise, our best Redis experience.

17 |

Redis Enterprise Cloud Essentials is a managed service providing a secure, scalable, and highly available Redis environment in the cloud. Sign up for our free tier to create your own private Redis Enterprise instance.

18 | 19 |
20 | Start Here 21 |
22 |
-------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Redis Training 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/section_2/section2.7/Program.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | 3 | var muxer = ConnectionMultiplexer.Connect("localhost"); 4 | var db = muxer.GetDatabase(); 5 | 6 | // TODO for Coding Challenge Start here on starting-point branch 7 | var scriptText = @" 8 | local id = redis.call('incr', @id_key) 9 | local key = 'key:' .. id 10 | redis.call('set', key, @value) 11 | return key 12 | "; 13 | 14 | var script = LuaScript.Prepare(scriptText); 15 | 16 | var key1 = db.ScriptEvaluate(script, new {id_key=(RedisKey)"autoIncrement", value="A String Value"}); 17 | var key2 = db.ScriptEvaluate(script, new {id_key=(RedisKey)"autoIncrement", value="Another String Value"}); 18 | 19 | Console.WriteLine($"Key 1: {key1}"); 20 | Console.WriteLine($"Key 2: {key2}"); 21 | 22 | var nonPreparedScript = @" 23 | local id = redis.call('incr', KEYS[1]) 24 | local key = 'key:' .. id 25 | redis.call('set', key, ARGV[1]) 26 | return key 27 | "; 28 | 29 | var key3 = db.ScriptEvaluate(nonPreparedScript, new RedisKey[] { "autoIncrement" }, new RedisValue[] { "Yet another string value" }); 30 | Console.WriteLine($"Key 3: {key3}"); 31 | //end coding challenge 32 | -------------------------------------------------------------------------------- /courseware/html/section_3/ru102n_3_2_0_caching_aspnet_core.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Caching in ASP.NET Core

6 | 7 |

8 | Using Redis as a Cache in ASP.NET Core is as simple as using the Distributed Cache API that's available for any manner of cache that you 9 | might use in ASP.NET Core. The only change that occurs when you add Redis to your caches in ASP.NET Core is that ASP.NET Core will 10 | use Redis as its cache provider. The remainder of the operations are abstracted away from us so we don't have to think about them. That 11 | means that fundamentally, the feature set provided by the Redis Cache Provider is limited to the IDistributedCache API. 12 |

13 | 14 |

15 | In this section we'll be encountering our Employee Aggregation System for the first time, where we'll use a simple model, and running some 16 | aggregations against a SQLite database with Entity Framework to see how one might use a caching framework to remove the need to repeat 17 | major computation. 18 |

-------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_3_3_i_server.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

The IServer

6 | 7 |

8 | The IServer is an abstraction to a single instance of a Redis Server. You can grab an instance of an IServer 9 | by using the IConnectionMultiplexer.GetServer command, passing in the exact endpoint information you want to 10 | retrieve. 11 |

12 |

13 | IServer has a fundamentally different role than IDatabase as you're going to use it to handle the server 14 | level commands. That means that in general, data modeling commands are not appropriate to be used on a server. Rather 15 | operations like checking the server's info (the basic info of Redis), it's configuration, updating it's configuration, 16 | checking memory statistics, and the like are IServer operations. Even scanning for the keys of a Redis server should be 17 | done at the server level. 18 |

-------------------------------------------------------------------------------- /src/section_3/section3.2/section3.2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | section3._2 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/SampleGeneration/InvalidSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace section3._3.Areas.HelpPage 4 | { 5 | /// 6 | /// This represents an invalid sample on the help page. There's a display template named InvalidSample associated with this class. 7 | /// 8 | public class InvalidSample 9 | { 10 | public InvalidSample(string errorMessage) 11 | { 12 | if (errorMessage == null) 13 | { 14 | throw new ArgumentNullException("errorMessage"); 15 | } 16 | ErrorMessage = errorMessage; 17 | } 18 | 19 | public string ErrorMessage { get; private set; } 20 | 21 | public override bool Equals(object obj) 22 | { 23 | InvalidSample other = obj as InvalidSample; 24 | return other != null && ErrorMessage == other.ErrorMessage; 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return ErrorMessage.GetHashCode(); 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return ErrorMessage; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/README.md: -------------------------------------------------------------------------------- 1 | # Section 3.3 Special Instructions 2 | 3 | In section 3.3 we explore caching using Redis in ASP.NET, this section is completely optional as ASP.NET is now (somewhat) legacy having been largely supplanted by ASP.NET Core. None of the materials here will be included in the exam required to pass this course so if this is of no interest to you, feel free to skip. That said it should still be instructive for those of you still interested in ASP.NET to learn how to use Redis in this context, so please bear in mind that there are some additional requirements for this section. 4 | 5 | ## Additional Prerequisites 6 | 7 | * Because this uses ASP.NET and the .NET Framework, this needs to run on a Windows (preferably Windows 10+) machine or VM. 8 | * You must have the [.NET Framework 4.8.1 SDK](https://dotnet.microsoft.com/en-us/download/visual-studio-sdks) installed 9 | * This example makes use of MS SQL Server Express's [Local DB](https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-express-localdb?view=sql-server-ver16) it should ship along side IIS Express. 10 | * It's highly recommended that you use either Visual Studio or Rider for this section. -------------------------------------------------------------------------------- /src/section_3/section3.3/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Optimization; 3 | 4 | namespace section3._3 5 | { 6 | public class BundleConfig 7 | { 8 | // For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862 9 | public static void RegisterBundles(BundleCollection bundles) 10 | { 11 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 12 | "~/Scripts/jquery-{version}.js")); 13 | 14 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 15 | // ready for production, use the build tool at https://modernizr.com to pick only the tests you need. 16 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 17 | "~/Scripts/modernizr-*")); 18 | 19 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 20 | "~/Scripts/bootstrap.js")); 21 | 22 | bundles.Add(new StyleBundle("~/Content/css").Include( 23 | "~/Content/bootstrap.css", 24 | "~/Content/site.css")); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/section_3/section3.4/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright OpenJS Foundation and other contributors, https://openjsf.org/ 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_4_3_connect_to_redis_sentinel.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Connect to Redis Sentinel

6 | 7 |

8 | Connecting to Redis Sentinel 9 | is a bit different than connecting to other instances of Redis. 10 |

11 |

12 | The key 13 | difference is that rather than connecting to the master server, you connect 14 | to one of the 'sentinels' - the instances of Redis responsible for monitoring 15 | your master and replicas, detecting fail-overs, and promoting new masters. 16 | 17 | Additionally, you must specify the ServiceName, which corresponds 18 | to the master name that you tell the sentinels to monitor when configuring them. 19 |

20 | 21 |

22 |

23 |     var options = new ConfigurationOptions
24 |     {
25 |         EndPoints = {"sentinel-1:26379"},
26 |         ServiceName = "sentinel"
27 |     };
28 | 
29 |     var muxer = ConnectionMultiplexer.Connect(options);
30 | 
31 |

-------------------------------------------------------------------------------- /src/section_3/section3.3/Section3.3.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32922.545 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "section3.3", "section3.3.csproj", "{B12C0F53-AA6D-4132-891A-CD6C7D3007BF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B12C0F53-AA6D-4132-891A-CD6C7D3007BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {B12C0F53-AA6D-4132-891A-CD6C7D3007BF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {B12C0F53-AA6D-4132-891A-CD6C7D3007BF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {B12C0F53-AA6D-4132-891A-CD6C7D3007BF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {6B4FFDDF-7895-4E6C-8A06-EF5BFCA4AF6D} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /src/section_3/section3.4/Program.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | 3 | var builder = WebApplication.CreateBuilder(args); 4 | 5 | // Add services to the container. 6 | builder.Services.AddControllersWithViews(); 7 | 8 | // TODO Section 3.4 Step 1 9 | // Add Redis Cache here. 10 | builder.Services.AddStackExchangeRedisCache(options => options.ConfigurationOptions = new ConfigurationOptions{ 11 | EndPoints = { "localhost:6379" }, 12 | Password = "" 13 | }); 14 | // end Section 3.4 Step 1 15 | 16 | // add session service 17 | builder.Services.AddSession(); 18 | var app = builder.Build(); 19 | 20 | // Configure the HTTP request pipeline. 21 | if (!app.Environment.IsDevelopment()) 22 | { 23 | app.UseExceptionHandler("/Home/Error"); 24 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 25 | app.UseHsts(); 26 | } 27 | 28 | app.UseHttpsRedirection(); 29 | app.UseStaticFiles(); 30 | 31 | app.UseRouting(); 32 | app.UseAuthorization(); 33 | 34 | // tell app to use session. 35 | app.UseSession(); 36 | 37 | app.MapControllerRoute( 38 | name: "default", 39 | pattern: "{controller=Home}/{action=Index}/{id?}"); 40 | 41 | app.Run(); 42 | -------------------------------------------------------------------------------- /src/section_3/section3.5/section3.5.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32922.545 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "section3.5", "section3.5.csproj", "{7604CC12-E54E-42CE-9CE0-4C83CD42EC8D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {7604CC12-E54E-42CE-9CE0-4C83CD42EC8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {7604CC12-E54E-42CE-9CE0-4C83CD42EC8D}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {7604CC12-E54E-42CE-9CE0-4C83CD42EC8D}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {7604CC12-E54E-42CE-9CE0-4C83CD42EC8D}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {DED42B8C-7233-470C-8DB8-D84C147DADC2} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_3_4_i_subscriber.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

ISubscriber

6 | 7 |

8 | The ISubscriber is the interface responsible for maintaining subscriptions 9 | to Redis in the pub/sub interface. Unlike the other interfaces we've looked 10 | at thus far, the subscriber does not leverage the interactive connection. 11 |

12 | 13 |

14 | The Multiplexer explicitly opens a separate connection for subscriptions 15 | because when you subscribe to any channel on a client in Redis, the client 16 | connection converts to subscription mode. This limits the connection to only use commands that implement subscriber functionality. 17 |

18 | 19 |

20 | True to it's name however, the Multiplexer continues to maintain a single 21 | connection per server, and all subscriptions are handled on that single 22 | connection. 23 |

24 | 25 |

26 | You you can get an instance of an ISubscriber by calling the 27 | IConnectionMultiplexer.GetSubscriber() method. 28 |

-------------------------------------------------------------------------------- /src/section_3/section3.4/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/section_3/section3.2/Program.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using section3._2; 3 | using StackExchange.Redis; 4 | 5 | var builder = WebApplication.CreateBuilder(args); 6 | 7 | // Add services to the container. 8 | 9 | builder.Services.AddControllers().AddNewtonsoftJson(x=>x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore); 10 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 11 | builder.Services.AddEndpointsApiExplorer(); 12 | builder.Services.AddSwaggerGen(); 13 | builder.Services.AddDbContext(); 14 | builder.Services.AddHostedService(); 15 | 16 | // TODO Section 3.2 Step 1 17 | // call AddStackExchangeRedisCache here. 18 | builder.Services.AddStackExchangeRedisCache(x => x.ConfigurationOptions = new ConfigurationOptions 19 | { 20 | EndPoints = { "localhost:6379" }, 21 | Password = "" 22 | }); 23 | // End Section 3.2 Step 1 24 | var app = builder.Build(); 25 | 26 | // Configure the HTTP request pipeline. 27 | if (app.Environment.IsDevelopment()) 28 | { 29 | app.UseSwagger(); 30 | app.UseSwaggerUI(); 31 | } 32 | 33 | app.UseHttpsRedirection(); 34 | 35 | app.UseAuthorization(); 36 | 37 | app.MapControllers(); 38 | 39 | app.Run(); 40 | -------------------------------------------------------------------------------- /src/section_3/section3.4/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2021 Twitter, Inc. 4 | Copyright (c) 2011-2021 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/section_3/section3.4/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) .NET Foundation and Contributors 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_9_0_pubsub.html: -------------------------------------------------------------------------------- 1 |

Redis Pub/Sub

2 | 3 |

4 | Pub/Sub allows for a subscriber to listen for events on channels in Redis using the 5 | Publisher/Subscriber messaging pattern. 6 |

7 |

8 | Pub/Sub is unique in StackExchange.Redis as it is the only thing the library does that is not 9 | on the main interactive connection. Earlier in the course I mentioned that StackExchange.Redis opens two connection to Redis at a time. 10 |

11 |

12 | The first of these is the interactive connection that you use to send all of your interactive commands to Redis on. The other is the subscriber 13 | connection. There's a really good reason for this, after subscribing to a channel in Redis, Redis flips the connection over to Subscriber 14 | mode which does not allow any non pub/sub related commands across, allowing it to send messages back to the client unhindered by other 15 | traffic on the channel. 16 |

17 | 18 |

19 | In this section, we'll be learning how to use the pub/sub API in StackExchange.Redis. How to subscribe to channels, and handle messages 20 | that are dispatched from Redis to the client. 21 |

-------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_2_4_coding_challenge_solution.html: -------------------------------------------------------------------------------- 1 | 4 |

5 | The following is what my code looked like after the previous exercise, yours might 6 | be slightly different, and that's ok! but fundamentally, you should have: 7 |

8 | 9 |
    10 |
  • Connected to the ConnectionMultiplexer
  • 11 |
  • Grabbed an IDatabase from the Multiplexer
  • 12 |
  • Called Ping on the IDatabase
  • 13 |
  • Printed the results from that ping.
  • 14 |
15 | 16 |

17 |

18 |         // Start Programming Challenge
19 |         using StackExchange.Redis;
20 |         Console.WriteLine("Hello Redis!");
21 | 
22 |         var muxer = ConnectionMultiplexer.Connect(new ConfigurationOptions
23 |         {
24 |             EndPoints = {"localhost:6379"}
25 |         });
26 | 
27 |         var db = muxer.GetDatabase();
28 |         var res = db.Ping();
29 |         Console.WriteLine($"The ping took: {res.TotalMilliseconds} ms");
30 |         //End Programming Challenge 
31 |     
32 |

33 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/SampleGeneration/ImageSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace section3._3.Areas.HelpPage 4 | { 5 | /// 6 | /// This represents an image sample on the help page. There's a display template named ImageSample associated with this class. 7 | /// 8 | public class ImageSample 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The URL of an image. 14 | public ImageSample(string src) 15 | { 16 | if (src == null) 17 | { 18 | throw new ArgumentNullException("src"); 19 | } 20 | Src = src; 21 | } 22 | 23 | public string Src { get; private set; } 24 | 25 | public override bool Equals(object obj) 26 | { 27 | ImageSample other = obj as ImageSample; 28 | return other != null && Src == other.Src; 29 | } 30 | 31 | public override int GetHashCode() 32 | { 33 | return Src.GetHashCode(); 34 | } 35 | 36 | public override string ToString() 37 | { 38 | return Src; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/section_5/section5.1/Model.cs: -------------------------------------------------------------------------------- 1 | using Redis.OM.Modeling; 2 | 3 | namespace section5._1; 4 | 5 | [Document(StorageType = StorageType.Json, Prefixes = new []{"Employee"}, IndexName = "employees")] 6 | public class Employee 7 | { 8 | [RedisIdField] 9 | [Indexed] 10 | public string? Id { get; set; } 11 | [Indexed] 12 | public List? Sales { get; set; } 13 | [Indexed(JsonPath = "$.Location")] 14 | [Indexed(JsonPath = "$.PostalCode")] 15 | public Address? Address { get; set; } 16 | [Indexed] 17 | public string? Name { get; set; } 18 | } 19 | 20 | [Document(StorageType = StorageType.Json)] 21 | public class Sale 22 | { 23 | [RedisIdField] 24 | [Indexed] 25 | public string? Id { get; set; } 26 | [Indexed] 27 | public string? EmployeeId { get; set; } 28 | [Indexed] 29 | public int Total { get; set; } 30 | [Indexed(CascadeDepth = 2)] 31 | public Address? Address { get; set; } 32 | } 33 | 34 | public class Address 35 | { 36 | [Searchable] 37 | public string? StreetAddress { get; set; } 38 | [Indexed] 39 | public string? PostalCode { get; set; } 40 | [Indexed] 41 | public GeoLoc Location { get; set; } 42 | [Indexed(CascadeDepth = 1)] 43 | public Address? ForwardingAddress { get; set; } 44 | } -------------------------------------------------------------------------------- /src/section_5/section5.2/Model.cs: -------------------------------------------------------------------------------- 1 | using Redis.OM.Modeling; 2 | 3 | namespace section5._2; 4 | 5 | [Document(StorageType = StorageType.Json, Prefixes = new []{"Employee"}, IndexName = "employees")] 6 | public class Employee 7 | { 8 | [RedisIdField] 9 | [Indexed] 10 | public string? Id { get; set; } 11 | [Indexed] 12 | public List? Sales { get; set; } 13 | [Indexed(JsonPath = "$.Location")] 14 | [Indexed(JsonPath = "$.PostalCode")] 15 | public Address? Address { get; set; } 16 | [Indexed] 17 | public string? Name { get; set; } 18 | } 19 | 20 | [Document(StorageType = StorageType.Json)] 21 | public class Sale 22 | { 23 | [RedisIdField] 24 | [Indexed] 25 | public string? Id { get; set; } 26 | [Indexed] 27 | public string? EmployeeId { get; set; } 28 | [Indexed] 29 | public int Total { get; set; } 30 | [Indexed(CascadeDepth = 2)] 31 | public Address? Address { get; set; } 32 | } 33 | 34 | public class Address 35 | { 36 | [Searchable] 37 | public string? StreetAddress { get; set; } 38 | [Indexed] 39 | public string? PostalCode { get; set; } 40 | [Indexed] 41 | public GeoLoc Location { get; set; } 42 | [Indexed(CascadeDepth = 1)] 43 | public Address? ForwardingAddress { get; set; } 44 | } -------------------------------------------------------------------------------- /courseware/html/section_3/ru102n_3_2_1_employee_aggregation_example_intro.html: -------------------------------------------------------------------------------- 1 |

Introducing our Employee Example

2 | 3 |

4 | For our employee aggregations example, we'll keep it simple, we'll have two components of our model, Employees and Sales. They'll 5 | look like this: 6 |

7 | 8 |

9 |

    
10 |     public class Employee
11 |     {
12 |         public int EmployeeId { get; set; }
13 |         public string Name { get; set; }
14 |         public List<Sale> Sales { get; } = new();
15 |     }
16 | 
17 |     public class Sale
18 |     {
19 |         public int SaleId { get; set; }
20 |         public int Total { get; set; }
21 | 
22 |         public int EmployeeId { get; set; }
23 |         public Employee Employee { get; set; }
24 |     }
25 | 
26 |

27 | 28 |

29 | We'll use Entity Framework and a local SQLite database for our example. If you are using a remote Redis Database, keep in mind that you'll 30 | incur some latency over the network when going to Redis vs going to our SQLite database, so if you want to truly compare apples to apples 31 | you can either run Redis locally (see the course setup instructions), or you can use a remote SQL Database by updating the adapter we're using in our 32 | example. 33 |

-------------------------------------------------------------------------------- /src/section_3/section3.2/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:62837", 8 | "sslPort": 44314 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "http://localhost:5053", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "applicationUrl": "https://localhost:7234;http://localhost:5053", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "swagger", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using System.Web.Http.Controllers 3 | @using System.Web.Http.Description 4 | @using System.Collections.ObjectModel 5 | @using section3._3.Areas.HelpPage.Models 6 | @model Collection 7 | 8 | @{ 9 | ViewBag.Title = "ASP.NET Web API Help Page"; 10 | 11 | // Group APIs by controller 12 | ILookup apiGroups = Model.ToLookup(api => api.ActionDescriptor.ControllerDescriptor); 13 | } 14 | 15 | 16 |
17 |
18 |
19 |

@ViewBag.Title

20 |
21 |
22 |
23 |
24 | 32 |
33 | @foreach (var group in apiGroups) 34 | { 35 | @Html.DisplayFor(m => group, "ApiGroup") 36 | } 37 |
38 |
39 | -------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_0_0_unit_overview.html: -------------------------------------------------------------------------------- 1 |

2 | In this unit we'll be introducing the StackExchange.Redis Client as the primary 3 | interactive Redis client for the .NET ecosystem. In this unit we'll explore: 4 |

5 | 6 |
    7 |
  • The origins of StackExchange.Redis.
  • 8 |
  • The ecosystem that has grown up around the project.
  • 9 |
  • How to get and add StackExchange.Redis to your project, and connect to Redis.
  • 10 |
  • The architecture of StackExchange.Redis, along with the benefits and drawbacks of it.
  • 11 |
  • How Connection Multiplexing works.
  • 12 |
  • The core interfaces of StackExchange.Redis.
  • 13 |
  • How to connect to different types of Redis Instances.
  • 14 |
  • How to handle pipelining in StackExchange.Redis.
  • 15 |
16 | 17 |

18 | Throughout this unit, I'll have Hands-On materials for you to work through 19 | and code to actually let you get your hands dirty. These bits aren't graded, 20 | but you should ought to work through them all, what better way to learn is there than 21 | actually writing some code and seeing it work? 22 |

23 |

24 | By the end of this unit, you'll 25 | have a firm grasp as to the fundamentals of the StackExchange.Redis library, 26 | and how to use it in your applications. 27 |

-------------------------------------------------------------------------------- /src/section_3/section3.5/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Optimization; 3 | 4 | namespace section3._5 5 | { 6 | public class BundleConfig 7 | { 8 | // For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862 9 | public static void RegisterBundles(BundleCollection bundles) 10 | { 11 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 12 | "~/Scripts/jquery-{version}.js")); 13 | 14 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 15 | "~/Scripts/jquery.validate*")); 16 | 17 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 18 | // ready for production, use the build tool at https://modernizr.com to pick only the tests you need. 19 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 20 | "~/Scripts/modernizr-*")); 21 | 22 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 23 | "~/Scripts/bootstrap.js")); 24 | 25 | bundles.Add(new StyleBundle("~/Content/css").Include( 26 | "~/Content/bootstrap.css", 27 | "~/Content/site.css")); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /courseware/html/section_6/ru102n_6_0_0_final_exam_introduction.html: -------------------------------------------------------------------------------- 1 |

You've now completed all of the course materials. You're almost to the finishing line!

2 |

Now, it's time for the final exam. Here are a few things to know before you get started:

3 |
    4 |
  • The final exam is a series of multiple choice questions and we encourage you to refer to the course materials and Redis documentation - it's open book!
  • 5 |
  • The final exam counts for 100% of your grade - there are no other graded activities as part of this course.
  • 6 |
  • You have until the end of the course to complete the exam, so take your time.
  • 7 |
8 |

After you've submitted your answers, you'll be graded automatically. If you receive a final grade of 65% or more, then you'll earn a certificate of completion.

9 |

Instructions are provided at the end of this section to generate and view your certificate, along with how to add the certificate to your LinkedIn profile.

10 |

And don't forget that the course Discord channel is still available if you have any questions or need assistance.

11 |

Thanks for sticking with it so far, and good luck with the exam!

12 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/section_5/section5.3/Model.cs: -------------------------------------------------------------------------------- 1 | using Redis.OM.Modeling; 2 | 3 | namespace section5._3; 4 | 5 | [Document(StorageType = StorageType.Json, Prefixes = new []{"Employee"}, IndexName = "employees")] 6 | public class Employee 7 | { 8 | [RedisIdField] 9 | [Indexed] 10 | public string? Id { get; set; } 11 | [Indexed] 12 | public List? Sales { get; set; } 13 | [Indexed(JsonPath = "$.Location")] 14 | [Indexed(JsonPath = "$.PostalCode")] 15 | public Address? Address { get; set; } 16 | [Indexed(Sortable = true)] 17 | public string? Name { get; set; } 18 | [Indexed(Sortable = true)] 19 | public int Age { get; set; } 20 | } 21 | 22 | [Document(StorageType = StorageType.Json)] 23 | public class Sale 24 | { 25 | [RedisIdField] 26 | [Indexed] 27 | public string? Id { get; set; } 28 | [Indexed] 29 | public string? EmployeeId { get; set; } 30 | [Indexed] 31 | public int Total { get; set; } 32 | [Indexed(CascadeDepth = 2)] 33 | public Address? Address { get; set; } 34 | } 35 | 36 | public class Address 37 | { 38 | [Searchable] 39 | public string? StreetAddress { get; set; } 40 | [Indexed] 41 | public string? PostalCode { get; set; } 42 | [Indexed(Sortable = true)] 43 | public GeoLoc Location { get; set; } 44 | [Indexed(CascadeDepth = 1)] 45 | public Address? ForwardingAddress { get; set; } 46 | } -------------------------------------------------------------------------------- /src/section_5/section5.4/Model.cs: -------------------------------------------------------------------------------- 1 | using Redis.OM.Modeling; 2 | 3 | namespace section5._4; 4 | 5 | [Document(StorageType = StorageType.Json, Prefixes = new []{"Employee"}, IndexName = "employees")] 6 | public class Employee 7 | { 8 | [RedisIdField] 9 | [Indexed] 10 | public string? Id { get; set; } 11 | [Indexed] 12 | public List? Sales { get; set; } 13 | [Indexed(JsonPath = "$.Location")] 14 | [Indexed(JsonPath = "$.PostalCode")] 15 | public Address? Address { get; set; } 16 | [Indexed(Sortable = true)] 17 | public string? Name { get; set; } 18 | [Indexed(Sortable = true)] 19 | public int Age { get; set; } 20 | } 21 | 22 | [Document(StorageType = StorageType.Json)] 23 | public class Sale 24 | { 25 | [RedisIdField] 26 | [Indexed] 27 | public string? Id { get; set; } 28 | [Indexed] 29 | public string? EmployeeId { get; set; } 30 | [Indexed] 31 | public int Total { get; set; } 32 | [Indexed(CascadeDepth = 2)] 33 | public Address? Address { get; set; } 34 | } 35 | 36 | public class Address 37 | { 38 | [Searchable] 39 | public string? StreetAddress { get; set; } 40 | [Indexed] 41 | public string? PostalCode { get; set; } 42 | [Indexed(Sortable = true)] 43 | public GeoLoc Location { get; set; } 44 | [Indexed(CascadeDepth = 1)] 45 | public Address? ForwardingAddress { get; set; } 46 | } -------------------------------------------------------------------------------- /src/section_2/section2.5/Program.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | 3 | var muxer = ConnectionMultiplexer.Connect("localhost"); 4 | var db = muxer.GetDatabase(); 5 | 6 | // TODO for Coding Challenge Start here on starting-point branch 7 | var person1 = "person:1"; 8 | var person2 = "person:2"; 9 | var person3 = "person:3"; 10 | 11 | db.KeyDelete(new RedisKey[]{person1, person2, person3}); 12 | 13 | db.HashSet(person1, new HashEntry[] 14 | { 15 | new("name","Alice"), 16 | new("age", 33), 17 | new("email","alice@example.com") 18 | }); 19 | 20 | db.HashSet(person2, new HashEntry[] 21 | { 22 | new("name","Bob"), 23 | new("age", 27), 24 | new("email","robert@example.com") 25 | }); 26 | 27 | db.HashSet(person3, new HashEntry[] 28 | { 29 | new("name","Charlie"), 30 | new("age", 50), 31 | new("email","chuck@example.com") 32 | }); 33 | 34 | var newAge = db.HashIncrement(person3, "age"); 35 | Console.WriteLine($"person:3 new age: {newAge}"); 36 | 37 | var person1Name = db.HashGet(person1, "name"); 38 | Console.WriteLine($"person:1 name: {person1Name}"); 39 | 40 | // HashGetAll 41 | var person2Fields = db.HashGetAll(person2); 42 | Console.WriteLine($"person:2 fields: {string.Join(", ", person2Fields)}"); 43 | 44 | // HashScan 45 | var person3Fields = db.HashScan(person3); 46 | Console.WriteLine($"person:3 fields: {string.Join(", ", person3Fields)}"); 47 | // end coding challenge -------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_4_2_connect_to_redis_cluster.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Connecting to a Redis Cluster

6 | 7 |

8 | Of the "advanced" Redis Connections, cluster is probably the easiest to 9 | understand. Connecting to an OSS Cluster instance is the same as connecting 10 | to a standalone Redis Instance. The only exception is that when you are 11 | listing endpoints, you'll want to use more than one. This is so that if 12 | the endpoint you are trying to reach has failed over, the Multiplexer 13 | still has a chance to connect to the other master instances. 14 |

15 | 16 |

17 | An example ConfigurationOptions for a cluster instance might look something along the lines of this, notice 18 | how the Endpoints collection takes multiple endpoints which the ConnectionMultiplexer can use as backups if 19 | one of the endpoints should fail to respond (possibly because it's failed over). 20 |

21 | 22 |

23 |

24 |     var options = new ConfigurationOptions
25 |     {
26 |         // add and update parameters as needed
27 |         EndPoints = {"redis-1:6379", "redis-2:6379", "redis-3:6379"}
28 |     };
29 | 
30 |

-------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_3_2_i_database.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

IDatabase

6 | 7 |

8 | The IDatabase can be thought of as the primary interactive interface to Redis. 9 | It provides a single interface for your entire Redis Instance, and is the 10 | preferred interface when you are executing single commands that manipulate 11 | your application's data to Redis. 12 |

13 | 14 |

15 | The IDatabase, unlike the IServer, abstracts the particulars of your Redis 16 | deployments away. Consequentially, if you are running in a cluster and are 17 | preforming a write, the IDatabase does not require you to know which server 18 | in particular you need to write to. Also, if you have many replicas per master 19 | shard in your Cluster 20 | or Sentinel Redis 21 | deployments, the IDatabase will leverage the ConnectionMultiplexer to 22 | automatically distribute your reads across your deployment. 23 |

-------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using System.Web.Http.Controllers 3 | @using System.Web.Http.Description 4 | @using section3._3.Areas.HelpPage 5 | @using section3._3.Areas.HelpPage.Models 6 | @model IGrouping 7 | 8 | @{ 9 | var controllerDocumentation = ViewBag.DocumentationProvider != null ? 10 | ViewBag.DocumentationProvider.GetDocumentation(Model.Key) : 11 | null; 12 | } 13 | 14 |

@Model.Key.ControllerName

15 | @if (!String.IsNullOrEmpty(controllerDocumentation)) 16 | { 17 |

@controllerDocumentation

18 | } 19 | 20 | 21 | 22 | 23 | 24 | @foreach (var api in Model) 25 | { 26 | 27 | 28 | 38 | 39 | } 40 | 41 |
APIDescription
@api.HttpMethod.Method @api.RelativePath 29 | @if (api.Documentation != null) 30 | { 31 |

@api.Documentation

32 | } 33 | else 34 | { 35 |

No documentation available.

36 | } 37 |
-------------------------------------------------------------------------------- /src/section_3/section3.3/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_3_5_i_transaction.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

ITransaction

6 | 7 |

8 | The ITransaction provides an interface for Redis Transactions. 9 | Transactions in Redis differ from transactions in other databases, for a full 10 | description of transactions check out the transaction section of 11 | RU101. 12 |

13 | 14 |

15 | The ITransaction interface is fundamentally an async interface. It exposes 16 | a very similar command set to the IDatabase, but it will only expose async 17 | versions of each command. That is because each command in ITransaction is 18 | async, as they will not be competed until after Execute is called. 19 | Only after the Execute is called can the underpinning tasks for the 20 | Transaction be awaited. 21 |

22 | 23 |

24 | You can get an instance of an ITransaction by calling 25 | IDatabase.GetTransaction() on your IDatabase object. 26 |

-------------------------------------------------------------------------------- /src/section_2/section2.8/Program.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | 3 | var muxer = ConnectionMultiplexer.Connect("localhost"); 4 | var db = muxer.GetDatabase(); 5 | 6 | // TODO for Coding Challenge Start here on starting-point branch 7 | var transaction = db.CreateTransaction(); 8 | 9 | transaction.HashSetAsync("person:1", new HashEntry[] 10 | { 11 | new ("name", "Steve"), 12 | new ("age", 32), 13 | new ("postal_code", "32999") 14 | }); 15 | transaction.SortedSetAddAsync("person:name:Steve", "person:1", 0); 16 | transaction.SortedSetAddAsync("person:postal_code:32999", "person:1", 0); 17 | transaction.SortedSetAddAsync("person:age", "person:1", 32); 18 | 19 | var success = transaction.Execute(); 20 | Console.WriteLine($"Transaction Successful: {success}"); 21 | 22 | // add condition that age == 32 23 | 24 | transaction.AddCondition(Condition.HashEqual("person:1", "age", 32)); 25 | transaction.HashIncrementAsync("person:1", "age"); 26 | transaction.SortedSetIncrementAsync("person:age", "person:1", 1); 27 | 28 | success = transaction.Execute(); 29 | Console.WriteLine($"Transaction Successful: {success}"); 30 | 31 | // Add a condition that will fail (e.g. age == 31) 32 | 33 | transaction.AddCondition(Condition.HashEqual("person:1", "age", 31)); 34 | transaction.HashIncrementAsync("person:1", "age"); 35 | transaction.SortedSetIncrementAsync("person:age", "person:1", 1); 36 | success = transaction.Execute(); 37 | 38 | Console.WriteLine($"Transaction Successful: {success}"); 39 | // end coding challenge 40 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Migrations/202211301905089_initial.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class initial : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.Employee", 12 | c => new 13 | { 14 | EmployeeId = c.Int(nullable: false, identity: true), 15 | Name = c.String(), 16 | }) 17 | .PrimaryKey(t => t.EmployeeId); 18 | 19 | CreateTable( 20 | "dbo.Sale", 21 | c => new 22 | { 23 | SaleId = c.Int(nullable: false, identity: true), 24 | Total = c.Int(nullable: false), 25 | EmployeeId = c.Int(nullable: false), 26 | }) 27 | .PrimaryKey(t => t.SaleId) 28 | .ForeignKey("dbo.Employee", t => t.EmployeeId, cascadeDelete: true) 29 | .Index(t => t.EmployeeId); 30 | 31 | } 32 | 33 | public override void Down() 34 | { 35 | DropForeignKey("dbo.Sale", "EmployeeId", "dbo.Employee"); 36 | DropIndex("dbo.Sale", new[] { "EmployeeId" }); 37 | DropTable("dbo.Sale"); 38 | DropTable("dbo.Employee"); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Migrations/202211302024328_initial.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class initial : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.Employee", 12 | c => new 13 | { 14 | EmployeeId = c.Int(nullable: false, identity: true), 15 | Name = c.String(), 16 | }) 17 | .PrimaryKey(t => t.EmployeeId); 18 | 19 | CreateTable( 20 | "dbo.Sale", 21 | c => new 22 | { 23 | SaleId = c.Int(nullable: false, identity: true), 24 | Total = c.Int(nullable: false), 25 | EmployeeId = c.Int(nullable: false), 26 | }) 27 | .PrimaryKey(t => t.SaleId) 28 | .ForeignKey("dbo.Employee", t => t.EmployeeId, cascadeDelete: true) 29 | .Index(t => t.EmployeeId); 30 | 31 | } 32 | 33 | public override void Down() 34 | { 35 | DropForeignKey("dbo.Sale", "EmployeeId", "dbo.Employee"); 36 | DropIndex("dbo.Sale", new[] { "EmployeeId" }); 37 | DropTable("dbo.Sale"); 38 | DropTable("dbo.Employee"); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("section3._3")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("section3._3")] 13 | [assembly: AssemblyCopyright("Copyright © 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("083a157a-db65-4f53-8019-89b988df7552")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("section3._5")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("section3._5")] 13 | [assembly: AssemblyCopyright("Copyright © 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("52e068cf-7ff5-45bf-9a71-cb60f50f4ca9")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /src/section_5/section5.2/Program.cs: -------------------------------------------------------------------------------- 1 | using Redis.OM; 2 | using Redis.OM.Modeling; 3 | using section5._2; 4 | 5 | var provider = new RedisConnectionProvider("redis://localhost:6379"); 6 | 7 | provider.Connection.DropIndexAndAssociatedRecords(typeof(Sale)); 8 | provider.Connection.DropIndexAndAssociatedRecords(typeof(Employee)); 9 | 10 | await provider.Connection.CreateIndexAsync(typeof(Sale)); 11 | await provider.Connection.CreateIndexAsync(typeof(Employee)); 12 | 13 | // TODO for Coding Challenge Start here on starting-point branch 14 | var employees = provider.RedisCollection(); 15 | 16 | var employee = new Employee 17 | { 18 | Name = "Steve", 19 | Address = new Address 20 | { 21 | StreetAddress = "Main Street", 22 | PostalCode = "34739", 23 | Location = new GeoLoc(-81.006, 27.872) 24 | }, 25 | Sales = new List() 26 | }; 27 | var key = employees.Insert(employee); 28 | 29 | Console.WriteLine($"Employee Id: {employee.Id}"); 30 | Console.WriteLine($"Key Name: {key}"); 31 | 32 | var sale = new Sale 33 | { 34 | Id = Guid.NewGuid().ToString(), 35 | Address = new Address 36 | { 37 | StreetAddress = "Pinewood Ave", 38 | PostalCode = "10001", 39 | Location = new GeoLoc( -73.991, 40.753) 40 | }, 41 | EmployeeId = employee.Id, 42 | Total = 5000, 43 | }; 44 | 45 | key = provider.Connection.Set(sale, TimeSpan.FromMinutes(5)); 46 | Console.WriteLine($"Sale Id: {sale.Id}"); 47 | Console.WriteLine($"Key Name: {key}"); 48 | // end coding challenge -------------------------------------------------------------------------------- /src/section_3/section3.3/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 |
2 |

ASP.NET

3 |

ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS, and JavaScript.

4 |

Learn more »

5 |
6 |
7 |
8 |

Getting started

9 |

ASP.NET Web API is a framework that makes it easy to build HTTP services that reach 10 | a broad range of clients, including browsers and mobile devices. ASP.NET Web API 11 | is an ideal platform for building RESTful applications on the .NET Framework.

12 |

Learn more »

13 |
14 |
15 |

Get more libraries

16 |

NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.

17 |

Learn more »

18 |
19 |
20 |

Web Hosting

21 |

You can easily find a web hosting company that offers the right mix of features and price for your applications.

22 |

Learn more »

23 |
24 |
25 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Migrations/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace section3._3.Migrations 2 | { 3 | using section3._3.Models; 4 | using System; 5 | using System.Data.Entity; 6 | using System.Data.Entity.Migrations; 7 | using System.Data.SQLite.EF6.Migrations; 8 | using System.Linq; 9 | 10 | internal sealed class Configuration : DbMigrationsConfiguration 11 | { 12 | public Configuration() 13 | { 14 | AutomaticMigrationsEnabled = false; 15 | AutomaticMigrationDataLossAllowed = true; 16 | } 17 | 18 | protected override async void Seed(section3._3.Models.SalesContext context) 19 | { 20 | Console.WriteLine("seeding. . ."); 21 | var names = new[] { "Alice", "Bob", "Carlos", "Dan", "Yves" }; 22 | var random = new Random(); 23 | 24 | foreach(var name in names) 25 | { 26 | var employee = new Employee { Name = name }; 27 | context.Employees.Add(employee); 28 | } 29 | 30 | Console.WriteLine("Saving Employees"); 31 | context.SaveChanges(); 32 | 33 | foreach(var name in names) 34 | { 35 | var employee = context.Employees.First(x => x.Name == name); 36 | for(var i = 0; i < 10000; i++) 37 | { 38 | employee.Sales.Add(new Sale { Total = random.Next(1000, 30000) }); 39 | } 40 | 41 | context.SaveChanges(); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ModelDescriptions/ModelNameHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace section3._3.Areas.HelpPage.ModelDescriptions 7 | { 8 | internal static class ModelNameHelper 9 | { 10 | // Modify this to provide custom model name mapping. 11 | public static string GetModelName(Type type) 12 | { 13 | ModelNameAttribute modelNameAttribute = type.GetCustomAttribute(); 14 | if (modelNameAttribute != null && !String.IsNullOrEmpty(modelNameAttribute.Name)) 15 | { 16 | return modelNameAttribute.Name; 17 | } 18 | 19 | string modelName = type.Name; 20 | if (type.IsGenericType) 21 | { 22 | // Format the generic type name to something like: GenericOfAgurment1AndArgument2 23 | Type genericType = type.GetGenericTypeDefinition(); 24 | Type[] genericArguments = type.GetGenericArguments(); 25 | string genericTypeName = genericType.Name; 26 | 27 | // Trim the generic parameter counts from the name 28 | genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); 29 | string[] argumentTypeNames = genericArguments.Select(t => GetModelName(t)).ToArray(); 30 | modelName = String.Format(CultureInfo.InvariantCulture, "{0}Of{1}", genericTypeName, String.Join("And", argumentTypeNames)); 31 | } 32 | 33 | return modelName; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_6_0_redis_streams.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Redis Streams

6 | 7 |

8 | Redis Streams are an append-only log like data structure that allows you to enqueue messages from producers to be consumed by consumers in 9 | your application. They are a powerful data structure with a rich feature set, and for a full explanation as to how and why to use Redis Streams, 10 | you can checkout RU202: Redis Streams. 11 |

12 | 13 |

14 | In this section, we'll explore using Redis Streams in .NET using the StackExchange.Redis library. We'll learn how to: 15 |

16 | 17 |
    18 |
  • Add Messages to a Stream.
  • 19 |
  • Read Messages from a Stream.
  • 20 |
  • Read Messages from a Stream in a Consumer Group.
  • 21 |
22 | 23 |

Stream Limitations in StackExchange.Redis

24 | 25 |

26 | Due to the multiplexed nature of StackExchange.Redis, it's important to note at the top that there is no mechanism for using the blocking 27 | paradigms available within the stream reading operations. 28 |

29 |

30 | This means that the Stream Read operations, StreamRead & StreamReadGroup, will 31 | not be able to use the XREAD and XREADGROUP block timer or the special $ id to read only new messages. 32 |

-------------------------------------------------------------------------------- /src/section_5/section5.5/Model.cs: -------------------------------------------------------------------------------- 1 | using Redis.OM.Modeling; 2 | 3 | namespace section5._5; 4 | 5 | [Document(StorageType = StorageType.Json, Prefixes = new []{"Employee"}, IndexName = "employees")] 6 | public class Employee 7 | { 8 | [RedisIdField] 9 | [Indexed] 10 | public string? Id { get; set; } 11 | [Indexed] 12 | public List? Sales { get; set; } 13 | [Indexed(JsonPath = "$.Location", Aggregatable = true)] 14 | [Indexed(JsonPath = "$.PostalCode")] 15 | public Address? Address { get; set; } 16 | [Indexed(Sortable = true)] 17 | public string? Name { get; set; } 18 | [Indexed(Sortable = true)] 19 | public int Age { get; set; } 20 | 21 | [Indexed(Aggregatable = true)] 22 | public long TotalSales { get; set; } 23 | 24 | [Indexed(Aggregatable = true)] 25 | public double SalesAdjustment { get; set; } 26 | } 27 | 28 | [Document(StorageType = StorageType.Json)] 29 | public class Sale 30 | { 31 | [RedisIdField] 32 | [Indexed] 33 | public string? Id { get; set; } 34 | [Indexed(Aggregatable = true)] 35 | public string? EmployeeId { get; set; } 36 | [Indexed(Aggregatable = true)] 37 | public int Total { get; set; } 38 | [Indexed(CascadeDepth = 2)] 39 | public Address? Address { get; set; } 40 | } 41 | 42 | public class Address 43 | { 44 | [Searchable] 45 | public string? StreetAddress { get; set; } 46 | [Indexed] 47 | public string? PostalCode { get; set; } 48 | [Indexed(Sortable = true)] 49 | public GeoLoc Location { get; set; } 50 | [Indexed(CascadeDepth = 1)] 51 | public Address? ForwardingAddress { get; set; } 52 | } -------------------------------------------------------------------------------- /courseware/html/section_3/ru102n_3_4_0_distributed_sessions_aspnet_core.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Distributed Session State in ASP.NET Core

6 | 7 |

8 | In this Hands-On, we'll be exploring how to use Session State Management in ASP.NET Core. 9 | You'll likely want to open up the csproj file and check out the starting-point 10 | branch before proceeding, unless of course you'd prefer to build everything from scratch. 11 |

12 |

13 | The csproj file is located at /src/section_3/section3.4/section3.4.csproj. 14 |

15 | 16 |

17 | Session state is the state maintained between the client's browser and a web server. However as we live in a world of increasingly larger 18 | distributed systems, we run into a problem. Do we really want the client to be dependent on a single, theoretically stateless application 19 | server? 20 |

21 |

22 | The answer is no, yet this session-state can be absolutely critical for the functionality of some applications. Hence it's often 23 | really helpful to have a intermediate database act as a central holder for this session state. This pattern is so popular in fact that it's 24 | heavily integrated into both ASP.NET and ASP.NET Core. 25 |

26 |

27 | Since Redis is so often tapped as the database acting as the distributed session 28 | state store, there are very straightforward integrations to add Redis as a native integration which we'll be exploring throughout the next 29 | couple of sections. 30 |

31 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/ApiDescriptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Web; 4 | using System.Web.Http.Description; 5 | 6 | namespace section3._3.Areas.HelpPage 7 | { 8 | public static class ApiDescriptionExtensions 9 | { 10 | /// 11 | /// Generates an URI-friendly ID for the . E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}" 12 | /// 13 | /// The . 14 | /// The ID as a string. 15 | public static string GetFriendlyId(this ApiDescription description) 16 | { 17 | string path = description.RelativePath; 18 | string[] urlParts = path.Split('?'); 19 | string localPath = urlParts[0]; 20 | string queryKeyString = null; 21 | if (urlParts.Length > 1) 22 | { 23 | string query = urlParts[1]; 24 | string[] queryKeys = HttpUtility.ParseQueryString(query).AllKeys; 25 | queryKeyString = String.Join("_", queryKeys); 26 | } 27 | 28 | StringBuilder friendlyPath = new StringBuilder(); 29 | friendlyPath.AppendFormat("{0}-{1}", 30 | description.HttpMethod.Method, 31 | localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty)); 32 | if (queryKeyString != null) 33 | { 34 | friendlyPath.AppendFormat("_{0}", queryKeyString.Replace('.', '-')); 35 | } 36 | return friendlyPath.ToString(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/section_3/section3.3/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewBag.Title - My ASP.NET Application 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 11 | 29 |
30 | @RenderBody() 31 |
32 |
33 |

© @DateTime.Now.Year - My ASP.NET Application

34 |
35 |
36 | 37 | @Scripts.Render("~/bundles/jquery") 38 | @Scripts.Render("~/bundles/bootstrap") 39 | @RenderSection("scripts", required: false) 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewBag.Title - My ASP.NET Application 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 11 | 30 |
31 | @RenderBody() 32 |
33 |
34 |

© @DateTime.Now.Year - My ASP.NET Application

35 |
36 |
37 | 38 | @Scripts.Render("~/bundles/jquery") 39 | @Scripts.Render("~/bundles/bootstrap") 40 | @RenderSection("scripts", required: false) 41 | 42 | 43 | -------------------------------------------------------------------------------- /courseware/html/section_3/ru102n_3_5_0_session_state_with_aspnet.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Distributed Session State with ASP.NET

6 | 7 |

8 | ASP.NET has a well established Distributed Session State Provider for Redis called Microsoft.Web.RedisSessionStateProvider. 9 | It allows for a proper Integration of Session State with Redis, and similar to the ASP.NET Core case, is quite easy to get up and running with. 10 |

11 | 12 |

13 | In this section, we'll be walking through a very simple application that leverages Session State, and see how to add Redis as a Provider of 14 | distributed session state in ASP.NET. 15 |

16 | 17 |

18 | Because this is ASP.NET, this section is considered optional. None of the questions on the final exam will be derived from the material from this 19 | section. However, if you still work on non-core ASP.NET apps, this can still be quite useful, so if it's useful to you, I'd encourage you 20 | to walk through this tutorial. 21 |

22 | 23 |

Added Prerequisites for this Section

24 | 25 |
    26 |
  • 27 | Because this is an ASP.NET section it necessary for you to have a Windows Machine (preferably Windows 10+) to work on. The reality 28 | of the .NET Framework is that it only runs on Windows, so you'll need Windows. 29 |
  • 30 |
  • 31 | You must have the .NET Framework 4.8.1 SDK installed 32 |
  • 33 |
  • 34 | It's highly recommended that you use either Visual Studio or Rider for this section. 35 |
  • 36 |
37 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Collections.Generic 2 | @using System.Collections.ObjectModel 3 | @using System.Web.Http.Description 4 | @using System.Threading 5 | @using section3._3.Areas.HelpPage.ModelDescriptions 6 | @model IList 7 | 8 | @if (Model.Count > 0) 9 | { 10 | 11 | 12 | 13 | 14 | 15 | @foreach (ParameterDescription parameter in Model) 16 | { 17 | ModelDescription modelDescription = parameter.TypeDescription; 18 | 19 | 20 | 23 | 26 | 39 | 40 | } 41 | 42 |
NameDescriptionTypeAdditional information
@parameter.Name 21 |

@parameter.Documentation

22 |
24 | @Html.DisplayFor(m => modelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = modelDescription }) 25 | 27 | @if (parameter.Annotations.Count > 0) 28 | { 29 | foreach (var annotation in parameter.Annotations) 30 | { 31 |

@annotation.Documentation

32 | } 33 | } 34 | else 35 | { 36 |

None.

37 | } 38 |
43 | } 44 | else 45 | { 46 |

None.

47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Controllers/AverageController.cs: -------------------------------------------------------------------------------- 1 | using section3._3.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using System.Web.Http; 9 | using System.Web.Http.Description; 10 | using System.Diagnostics; 11 | using System.Data.Entity; 12 | 13 | namespace section3._3.Controllers 14 | { 15 | public class AverageController : ApiController 16 | { 17 | private SalesContext _salesContext = new SalesContext(); 18 | 19 | 20 | [ResponseType(typeof(Dictionary))] 21 | public async Task GetAverage(int id) 22 | { 23 | var stopwatch = Stopwatch.StartNew(); 24 | // TODO Section 3.3 step 1 25 | // add cache check here 26 | var db = Redis.Database; 27 | var avg = (double?)await db.StringGetSetExpiryAsync($"average:{id}", TimeSpan.FromHours(1)); 28 | if(avg != null) 29 | { 30 | stopwatch.Stop(); 31 | return Ok(new Dictionary 32 | { 33 | {"average", avg.Value }, 34 | {"elapsed", stopwatch.ElapsedMilliseconds } 35 | }); 36 | } 37 | // End Section 3.3 step 1 38 | 39 | avg = await _salesContext.Employees.Include("Sales").Where(x => x.EmployeeId == id).Select(x => x.Sales.Average(s => s.Total)).FirstAsync(); 40 | 41 | // TODO Section 3.3 step 2 42 | // add cache set here 43 | await db.StringSetAsync($"average:{id}", avg, TimeSpan.FromHours(1)); 44 | // End Section 3.3 step 2 45 | 46 | return Ok(new Dictionary 47 | { 48 | {"average", avg.Value }, 49 | {"elapsed", stopwatch.ElapsedMilliseconds } 50 | }); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/section_2/section2.9/Program.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | 3 | var muxer = ConnectionMultiplexer.Connect("localhost"); 4 | var db = muxer.GetDatabase(); 5 | 6 | // TODO for Coding Challenge Start here on starting-point branch 7 | var subscriber = muxer.GetSubscriber(); 8 | var cancellationTokenSource = new CancellationTokenSource(); 9 | var token = cancellationTokenSource.Token; 10 | 11 | var channel = await subscriber.SubscribeAsync("test-channel"); 12 | 13 | channel.OnMessage(msg => 14 | { 15 | Console.WriteLine($"Sequentially received: {msg.Message} on channel: {msg.Channel}"); 16 | }); 17 | 18 | await subscriber.SubscribeAsync("test-channel", (channel, value) => 19 | { 20 | Console.WriteLine($"Received: {value} on channel: {channel}"); 21 | }); 22 | 23 | var basicSendTask = Task.Run(async () => 24 | { 25 | var i = 0; 26 | while (!token.IsCancellationRequested) 27 | { 28 | await db.PublishAsync("test-channel", i++); 29 | await Task.Delay(1000); 30 | } 31 | }); 32 | 33 | await subscriber.SubscribeAsync("pattern:*", (channel, value) => 34 | { 35 | Console.WriteLine($"Received: {value} on channel: {channel}"); 36 | }); 37 | 38 | 39 | var patternSendTask = Task.Run(async () => 40 | { 41 | var i = 0; 42 | while (!token.IsCancellationRequested) 43 | { 44 | await db.PublishAsync($"pattern:{Guid.NewGuid()}", i++); 45 | await Task.Delay(1000); 46 | } 47 | }); 48 | 49 | // put all other producer/subscriber stuff above here. 50 | Console.ReadKey(); 51 | // put cancellation & unsubscribe down here. 52 | 53 | Console.WriteLine("Unsubscribing to a single channel"); 54 | await channel.UnsubscribeAsync(); 55 | Console.ReadKey(); 56 | 57 | Console.WriteLine("Unsubscribing whole subscriber from test-channel"); 58 | await subscriber.UnsubscribeAsync("test-channel"); 59 | Console.ReadKey(); 60 | 61 | Console.WriteLine("Unsubscribing from all"); 62 | await subscriber.UnsubscribeAllAsync(); 63 | Console.ReadKey(); 64 | // end coding challenge 65 | -------------------------------------------------------------------------------- /src/section_4/section4.2/Program.cs: -------------------------------------------------------------------------------- 1 | 2 | using NRedisTimeSeries; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.DataTypes; 5 | using StackExchange.Redis; 6 | 7 | 8 | var muxer = ConnectionMultiplexer.Connect("localhost"); 9 | var db = muxer.GetDatabase(); 10 | 11 | // TODO for Coding Challenge Start here on starting-point branch 12 | // Delete keys. 13 | db.KeyDelete(new RedisKey[]{"sensor", "sensor:Max", "sensor:Avg", "sensor:Min"}); 14 | 15 | // Create Time Series and Rules. 16 | await db.TimeSeriesCreateAsync("sensor", 60000, new List{new TimeSeriesLabel("id", "sensor-1")}); 17 | 18 | var aggregations = new TsAggregation[]{TsAggregation.Avg, TsAggregation.Min, TsAggregation.Max}; 19 | foreach(var agg in aggregations) 20 | { 21 | await db.TimeSeriesCreateAsync($"sensor:{agg}", 60000, new List{new ("type", agg.ToString()), new("aggregation-for", "sensor-1")}); 22 | await(db.TimeSeriesCreateRuleAsync("sensor", new TimeSeriesRule($"sensor:{agg}", 5000, agg))); 23 | } 24 | 25 | var producerTask = Task.Run(async()=>{ 26 | while(true) 27 | { 28 | await db.TimeSeriesAddAsync("sensor", "*", Random.Shared.Next(50)); 29 | await Task.Delay(1000); 30 | } 31 | }); 32 | 33 | var consumerTask = Task.Run(async()=>{ 34 | while(true) 35 | { 36 | await Task.Delay(1000); 37 | var result = await db.TimeSeriesGetAsync("sensor"); 38 | Console.WriteLine($"{result.Time.Value}: {result.Val}"); 39 | } 40 | }); 41 | 42 | var aggregationConsumerTask = Task.Run(async()=> 43 | { 44 | while(true) 45 | { 46 | await Task.Delay(5000); 47 | var results = await db.TimeSeriesMGetAsync(new List(){"aggregation-for=sensor-1"}, true); 48 | foreach(var result in results) 49 | { 50 | Console.WriteLine($"{result.labels.First(x=>x.Key == "type").Value}: {result.value.Val}"); 51 | } 52 | 53 | } 54 | }); 55 | 56 | Console.ReadKey(); 57 | // end coding challenge -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Controllers/TotalSalesController.cs: -------------------------------------------------------------------------------- 1 | using section3._3.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using System.Web.Http; 9 | using System.Web.Http.Description; 10 | using System.Diagnostics; 11 | using System.Data.Entity; 12 | 13 | namespace section3._3.Controllers 14 | { 15 | public class TotalSalesController : ApiController 16 | { 17 | private SalesContext _salesContext = new SalesContext(); 18 | 19 | [ResponseType(typeof(Dictionary))] 20 | public async Task GetTotalSales() 21 | { 22 | var stopwatch = Stopwatch.StartNew(); 23 | 24 | // TODO Section 3.3 step 5 25 | // add cache check logic here 26 | var db = Redis.Database; 27 | long? totalSales = (long?) await db.StringGetAsync("totalSales"); 28 | if(totalSales != null) 29 | { 30 | return Ok(new Dictionary() 31 | { 32 | { "Total Sales", totalSales.Value }, 33 | { "elapsed", stopwatch.ElapsedMilliseconds } 34 | }); 35 | } 36 | // end Section 3.3 step 5 37 | 38 | totalSales = await _salesContext.Sales.SumAsync(x => x.Total); 39 | 40 | // TODO Section 3.3 step 6 41 | // add cache set logic here 42 | var timeTillMidnight = DateTime.Today.AddDays(1) - DateTime.Now; 43 | await db.StringSetAsync("totalSales", totalSales, timeTillMidnight); 44 | // end section 3.3 step 6 45 | 46 | stopwatch.Stop(); 47 | 48 | return Ok(new Dictionary() 49 | { 50 | { "Total Sales", totalSales.Value }, 51 | { "elapsed", stopwatch.ElapsedMilliseconds } 52 | }); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Views/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/section_3/section3.5/Views/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using System.Web.Http.Description 3 | @using section3._3.Areas.HelpPage.Models 4 | @using section3._3.Areas.HelpPage.ModelDescriptions 5 | @model HelpPageApiModel 6 | 7 | @{ 8 | ApiDescription description = Model.ApiDescription; 9 | } 10 |

@description.HttpMethod.Method @description.RelativePath

11 |
12 |

@description.Documentation

13 | 14 |

Request Information

15 | 16 |

URI Parameters

17 | @Html.DisplayFor(m => m.UriParameters, "Parameters") 18 | 19 |

Body Parameters

20 | 21 |

@Model.RequestDocumentation

22 | 23 | @if (Model.RequestModelDescription != null) 24 | { 25 | @Html.DisplayFor(m => m.RequestModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.RequestModelDescription }) 26 | if (Model.RequestBodyParameters != null) 27 | { 28 | @Html.DisplayFor(m => m.RequestBodyParameters, "Parameters") 29 | } 30 | } 31 | else 32 | { 33 |

None.

34 | } 35 | 36 | @if (Model.SampleRequests.Count > 0) 37 | { 38 |

Request Formats

39 | @Html.DisplayFor(m => m.SampleRequests, "Samples") 40 | } 41 | 42 |

Response Information

43 | 44 |

Resource Description

45 | 46 |

@description.ResponseDescription.Documentation

47 | 48 | @if (Model.ResourceDescription != null) 49 | { 50 | @Html.DisplayFor(m => m.ResourceDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ResourceDescription }) 51 | if (Model.ResourceProperties != null) 52 | { 53 | @Html.DisplayFor(m => m.ResourceProperties, "Parameters") 54 | } 55 | } 56 | else 57 | { 58 |

None.

59 | } 60 | 61 | @if (Model.SampleResponses.Count > 0) 62 | { 63 |

Response Formats

64 | @Html.DisplayFor(m => m.SampleResponses, "Samples") 65 | } 66 | 67 |
-------------------------------------------------------------------------------- /courseware/html/section_2/ru102n_2_0_0_overview.html: -------------------------------------------------------------------------------- 1 |

Unit 2 Overview

2 | 3 |

4 | Welcome to Unit 2 of RU102N: Redis for .NET Developers. In the previous unit 5 | we covered the basics of how to connect and use the StackExchange.Redis library. In 6 | this unit we will get into the nitty gritty of how to use various Redis Data 7 | structures and features from StackExchange.Redis. In this unit we will cover: 8 |

9 | 10 | 21 | 22 |

23 | We've linked to each of the relevant articles for each concept on redis.io. 24 | If you want a full explanation of each of these, and you haven't taken 25 | RU101: Introduction to Redis Data Structures, 26 | that course has full explanations for each of these concepts, and can be a great resource. 27 |

28 |

29 | In this unit we will briefly introduce the concepts and demonstrate the mechanics of 30 | using them using the StackExchange.Redis library. 31 |

-------------------------------------------------------------------------------- /src/section_1/section1.5/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using StackExchange.Redis; 3 | 4 | var options = new ConfigurationOptions 5 | { 6 | EndPoints = { "localhost:6379" } 7 | }; 8 | 9 | var muxer = ConnectionMultiplexer.Connect(options); 10 | var db = muxer.GetDatabase(); 11 | 12 | var stopwatch = Stopwatch.StartNew(); 13 | // TODO for Coding Challenge Start here on starting-point branch 14 | // un-pipelined commands incur the added cost of an extra network round trip 15 | for (var i = 0; i < 1000; i++) 16 | { 17 | await db.PingAsync(); 18 | } 19 | 20 | Console.WriteLine($"1000 un-pipelined commands took: {stopwatch.ElapsedMilliseconds}ms to execute"); 21 | 22 | // If we run out async tasks to StackExchange.Redis concurrently, the library 23 | // will automatically manage pipelining of these commands to Redis, making 24 | // them significantly more performant as we remove most of the network round trips to Redis. 25 | var pingTasks = new List>(); 26 | 27 | // restart stopwatch 28 | stopwatch.Restart(); 29 | 30 | for (var i = 0; i < 1000; i++) 31 | { 32 | pingTasks.Add(db.PingAsync()); 33 | } 34 | 35 | await Task.WhenAll(pingTasks); 36 | 37 | Console.WriteLine($"1000 automatically pipelined tasks took: {stopwatch.ElapsedMilliseconds}ms to execute, first result: {pingTasks[0].Result}"); 38 | 39 | // clear our ping tasks list. 40 | pingTasks.Clear(); 41 | 42 | // Batches allow you to more intentionally group together the commands that you want to send to Redis. 43 | // If you use a batch, all commands in the batch will be sent to Redis in one contiguous block, with no 44 | // other commands from the client interleaved. Of course, if there are other clients sending commands to Redis, 45 | // commands from those other clients may be interleaved with your batched commands. 46 | var batch = db.CreateBatch(); 47 | 48 | // restart stopwatch 49 | stopwatch.Restart(); 50 | 51 | for (var i = 0; i < 1000; i++) 52 | { 53 | pingTasks.Add(batch.PingAsync()); 54 | } 55 | 56 | batch.Execute(); 57 | await Task.WhenAll(pingTasks); 58 | Console.WriteLine($"1000 batched commands took: {stopwatch.ElapsedMilliseconds}ms to execute, first result: {pingTasks[0].Result}"); 59 | // end Challenge -------------------------------------------------------------------------------- /src/section_3/section3.3/Areas/HelpPage/Controllers/HelpController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | using System.Web.Mvc; 4 | using section3._3.Areas.HelpPage.ModelDescriptions; 5 | using section3._3.Areas.HelpPage.Models; 6 | 7 | namespace section3._3.Areas.HelpPage.Controllers 8 | { 9 | /// 10 | /// The controller that will handle requests for the help page. 11 | /// 12 | public class HelpController : Controller 13 | { 14 | private const string ErrorViewName = "Error"; 15 | 16 | public HelpController() 17 | : this(GlobalConfiguration.Configuration) 18 | { 19 | } 20 | 21 | public HelpController(HttpConfiguration config) 22 | { 23 | Configuration = config; 24 | } 25 | 26 | public HttpConfiguration Configuration { get; private set; } 27 | 28 | public ActionResult Index() 29 | { 30 | ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider(); 31 | return View(Configuration.Services.GetApiExplorer().ApiDescriptions); 32 | } 33 | 34 | public ActionResult Api(string apiId) 35 | { 36 | if (!String.IsNullOrEmpty(apiId)) 37 | { 38 | HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId); 39 | if (apiModel != null) 40 | { 41 | return View(apiModel); 42 | } 43 | } 44 | 45 | return View(ErrorViewName); 46 | } 47 | 48 | public ActionResult ResourceModel(string modelName) 49 | { 50 | if (!String.IsNullOrEmpty(modelName)) 51 | { 52 | ModelDescriptionGenerator modelDescriptionGenerator = Configuration.GetModelDescriptionGenerator(); 53 | ModelDescription modelDescription; 54 | if (modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, out modelDescription)) 55 | { 56 | return View(modelDescription); 57 | } 58 | } 59 | 60 | return View(ErrorViewName); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /courseware/html/section_3/ru102n_3_3_0_caching_asp_net_classic.html: -------------------------------------------------------------------------------- 1 |

Caching in ASP.NET

2 | 3 |

4 | Before we get started here, just a quick note... the sections on ASP.NET are completely optional, they are here because there are many 5 | developers still using ASP.NET, so this can be quite useful to them. 6 |

7 |

8 | But the reality is that ASP.NET is rapidly becoming a largely 9 | legacy framework with the emergence of ASP.NET Core. So if you don't think the sections on ASP.NET will be useful to you, feel free to skip them. 10 |

11 |

12 | None of the questions on the exam will involve this material. 13 |

14 | 15 |

16 | Doing simple caching in ASP.NET is the only one of the use cases we'll discuss in this unit that does not involve the use of some derived 17 | library. It's a simple enough way to use Redis that one isn't particularly needed (though when you compare it to ASP.NET Core you'll see 18 | why a framework integration can be really nice). 19 |

20 | 21 |

22 | In this section, we'll walk through an application with the same model as the ASP.NET Core example, the major differences are that instead 23 | of using EntityFramework Core, we use EntityFramework, and instead of using ASP.NET Core WebAPI we are using the ASP.NET API scaffolding, 24 | consequentially the structure of the projects are quite different. 25 |

26 | 27 |

Additional Prerequisites for this section

28 | 29 |
    30 |
  • 31 | Because this is an ASP.NET section it necessary for you to have a Windows Machine (preferably Windows 10+) to work on. The reality 32 | of the .NET Framework is that it only runs on Windows, so you'll need Windows. 33 |
  • 34 |
  • 35 | You must have the .NET Framework 4.8.1 SDK installed 36 |
  • 37 |
  • 38 | The example here uses MS SQL Server Express's LocalDB. 39 |
  • 40 |
  • 41 | It's highly recommended that you use either Visual Studio or Rider for this section. 42 |
  • 43 |
44 | -------------------------------------------------------------------------------- /src/section_4/section4.3/Program.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | 3 | using NRedisGraph; 4 | using StackExchange.Redis; 5 | 6 | var muxer = ConnectionMultiplexer.Connect("localhost"); 7 | var db = muxer.GetDatabase(); 8 | db.KeyDelete("pets"); 9 | 10 | // TODO for Coding Challenge Start here on starting-point branch 11 | var graph = new RedisGraph(db); 12 | 13 | 14 | var createBobResult = await graph.QueryAsync("pets", "CREATE(:human{name:'Bob',age:32})"); 15 | await graph.QueryAsync("pets", "CREATE(:human{name:'Alice',age:30})"); 16 | 17 | Console.WriteLine($"Nodes Created:{createBobResult.Statistics.NodesCreated}"); 18 | Console.WriteLine($"Properties Set:{createBobResult.Statistics.PropertiesSet}"); 19 | Console.WriteLine($"Labels Created:{createBobResult.Statistics.LabelsAdded}"); 20 | Console.WriteLine($"Operation took:{createBobResult.Statistics.QueryInternalExecutionTime}"); 21 | 22 | await graph.QueryAsync("pets", "CREATE(:pet{name:'Honey',age:5,species:'canine',breed:'Greyhound'})"); 23 | 24 | await graph.QueryAsync("pets", 25 | "MATCH(a:human),(p:pet) WHERE(a.name='Bob' and p.name='Honey') CREATE (a)-[:OWNS]->(p)"); 26 | 27 | await graph.QueryAsync("pets", 28 | "MATCH(a:human),(p:pet) WHERE(a.name='Alice' and p.name='Honey') CREATE (a)-[:WALKS]->(p)"); 29 | await graph.QueryAsync("pets", 30 | "MATCH(a:human),(p:pet) WHERE(a.name='Bob' and p.name='Honey') CREATE (a)-[:WALKS]->(p)"); 31 | 32 | var matches = await graph.QueryAsync("pets", "MATCH(a:human),(p:pet) where (a)-[:OWNS]->(p) and p.name='Honey' return a"); 33 | 34 | var record = matches.First(); 35 | 36 | Console.WriteLine($"Honey's owner nodes: {record}"); 37 | 38 | matches = await graph.QueryAsync("pets", "MATCH(a:human),(p:pet) where (a)-[:WALKS]->(p) and p.name='Honey' return a"); 39 | 40 | foreach (var rec in matches) 41 | { 42 | var node = (Node)rec.Values.First(); 43 | Console.WriteLine($"{node.PropertyMap["name"].Value} walks honey"); 44 | } 45 | 46 | matches = await graph.QueryAsync("pets", "MATCH(a:human),(p:pet) where (a)-[:OWNS]->(p) and p.species='canine' and a.name='Bob' return p"); 47 | 48 | foreach (var rec in matches) 49 | { 50 | var dogs = rec.Values.Select(x=>(Node)x).Select(x=>x.PropertyMap["name"].Value.ToString()); 51 | Console.WriteLine($"Bob's dogs are: {string.Join(", ", dogs)}"); 52 | } 53 | 54 | // end coding challenge -------------------------------------------------------------------------------- /src/section_3/section3.2/InitService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.Caching.Distributed; 3 | 4 | namespace section3._2; 5 | 6 | public class InitService : IHostedService 7 | { 8 | private readonly IServiceScopeFactory _scopeFactory; 9 | 10 | public InitService(IServiceScopeFactory scopeFactory) 11 | { 12 | _scopeFactory = scopeFactory; 13 | } 14 | 15 | public async Task StartAsync(CancellationToken cancellationToken) 16 | { 17 | using var scope = _scopeFactory.CreateScope(); 18 | var salesDb = scope.ServiceProvider.GetRequiredService(); 19 | 20 | // TODO Section 3.2 Step 2 21 | // add cache invalidation logic here. 22 | var cache = scope.ServiceProvider.GetRequiredService(); 23 | // End Section 3.2 Step 2 24 | 25 | var cachePipe = new List 26 | { 27 | cache.RemoveAsync("top:sales", cancellationToken), 28 | cache.RemoveAsync("top:name", cancellationToken), 29 | cache.RemoveAsync("totalSales", cancellationToken) 30 | }; 31 | cachePipe.AddRange(salesDb.Employees.Select(employee => cache.RemoveAsync($"employee:{employee.EmployeeId}:avg", cancellationToken))); 32 | 33 | await Task.WhenAll(cachePipe); 34 | // end cache invalidation logic 35 | 36 | await salesDb.Database.ExecuteSqlRawAsync("DELETE FROM Employees", cancellationToken); 37 | await salesDb.Database.ExecuteSqlRawAsync("DELETE FROM Sales",cancellationToken); 38 | 39 | var names = new[] { "Alice", "Bob", "Carlos", "Dan", "Yves" }; 40 | var random = new Random(); 41 | foreach (var name in names) 42 | { 43 | var employee = new Employee { Name = name }; 44 | salesDb.Employees.Add(employee); 45 | } 46 | 47 | await salesDb.SaveChangesAsync(cancellationToken); 48 | 49 | foreach (var name in names) 50 | { 51 | var employee = salesDb.Employees.First(x => x.Name == name); 52 | for (var i = 0; i < 10000; i++) 53 | { 54 | employee.Sales.Add(new Sale(){Total = random.Next(1000,30000)}); 55 | } 56 | } 57 | await salesDb.SaveChangesAsync(cancellationToken); 58 | } 59 | 60 | public Task StopAsync(CancellationToken cancellationToken) 61 | { 62 | return Task.CompletedTask; 63 | } 64 | } -------------------------------------------------------------------------------- /src/section_3/section3.5/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /courseware/html/section_1/ru102n_1_5_0_pipelining.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Pipelining

6 | 7 |

8 | Pipelining is a critically important concept for maximizing throughput to 9 | Redis. When you need to execute multiple commands against Redis, and the 10 | intermediate results can be temporarily ignored, pipelining can drastically 11 | reduce the number of round trips required to Redis, which can drastically 12 | increase performance, as many operations are hundreds of times faster than 13 | the Round Trip Time (RTT). 14 |

15 | 16 |

17 | With StackExchange.Redis, there are two ways to pipeline commands, 18 | either implicitly with the Async API, and explicitly with the IBatch API. 19 |

20 | 21 |

Implicit Pipelining with Async API

22 | 23 |

24 | If you use the async version of a command, the command will be automatically 25 | pipelined to Redis. If you use await to await the result of a 26 | task dispatched by one of these async commands, it will wait until the command 27 | is complete before returning control. However, if you group a set of tasks dispatched 28 | by the async methods together and await them all in one shot, the ConnectionMultiplexer 29 | will automatically find an efficient way to pipeline the commands to Redis, so that 30 | you can cut down the number of round trips significantly. 31 |

32 | 33 |

Explicit Pipelining with IBatch

34 | 35 |

36 | You can also be much more explicit about pipelining commands. The IBatch 37 | API only provides the async interface for commands. You can set up however many commands 38 | you want to pipeline with those async methods, preserving the tasks as they will provide 39 | you the results. 40 |

41 |

42 | When you have all the commands that you want pipelined dispatched, 43 | you can call Execute to run them all. This will pipeline all of your commands 44 | in one contiguous block to Redis. Using this method, no other commands will be interleaved 45 | with your batched commands from the client. However, if there are other clients sending 46 | commands to Redis, it's possible that their commands will be interleaved with the batched 47 | commands. 48 |

-------------------------------------------------------------------------------- /src/section_3/section3.2/Migrations/20221128165447_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace section3._2.Migrations 6 | { 7 | /// 8 | public partial class InitialCreate : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.CreateTable( 14 | name: "Employees", 15 | columns: table => new 16 | { 17 | EmployeeId = table.Column(type: "INTEGER", nullable: false) 18 | .Annotation("Sqlite:Autoincrement", true), 19 | Name = table.Column(type: "TEXT", nullable: false) 20 | }, 21 | constraints: table => 22 | { 23 | table.PrimaryKey("PK_Employees", x => x.EmployeeId); 24 | }); 25 | 26 | migrationBuilder.CreateTable( 27 | name: "Sales", 28 | columns: table => new 29 | { 30 | SaleId = table.Column(type: "INTEGER", nullable: false) 31 | .Annotation("Sqlite:Autoincrement", true), 32 | Total = table.Column(type: "INTEGER", nullable: false), 33 | EmployeeId = table.Column(type: "INTEGER", nullable: false) 34 | }, 35 | constraints: table => 36 | { 37 | table.PrimaryKey("PK_Sales", x => x.SaleId); 38 | table.ForeignKey( 39 | name: "FK_Sales_Employees_EmployeeId", 40 | column: x => x.EmployeeId, 41 | principalTable: "Employees", 42 | principalColumn: "EmployeeId", 43 | onDelete: ReferentialAction.Cascade); 44 | }); 45 | 46 | migrationBuilder.CreateIndex( 47 | name: "IX_Sales_EmployeeId", 48 | table: "Sales", 49 | column: "EmployeeId"); 50 | } 51 | 52 | /// 53 | protected override void Down(MigrationBuilder migrationBuilder) 54 | { 55 | migrationBuilder.DropTable( 56 | name: "Sales"); 57 | 58 | migrationBuilder.DropTable( 59 | name: "Employees"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /courseware/html/section_6/ru102n_6_6_3_2_get_your_certificate.html: -------------------------------------------------------------------------------- 1 |

How do I get my certificate?

2 |

Once you have completed your final exam, if your combined grade for homework and the final exam is 65% or more, you will be eligible for a Certificate of Completion.

3 |

Here are the steps to obtain your certificate:

4 |

1. Navigate to the Progress Tab

5 |

Navigate to the Progress Tab. Once here, if you have achieved 65% or more, then the Request Certificate button will be enabled.

6 |

7 |

2. View the Certificate

8 |

Click on the View Certificate button.

9 |

10 |

3. Download & Print your certificate

11 |

You can now download and print your Certificate.

12 |

13 |

Can I add my Certificate to my LinkedIn profile?

14 |

Yes! Its simple to add your certificate to your LinkedIn profile. This has the added benefit that your current and future employers can verify the authenticity of your certificate! Follow these simple instructions to add your certificate.

15 |

1. Open your profile on LinkedIn. Click "Add profile section". Then choose "Licenses and Certifications".

16 |

17 |

2. Add Certificate details

18 |

Add the Certificate details. This information comes from the Certificate your downloaded.

19 |

20 |

3. Where to find the Certificate details

21 |

You need two items from the Certificate, indicated in the diagram below.

22 |

23 |

4. View your new Certificate

24 |

After you have added the certificate details, you can now view your new accomplishment!

25 |

-------------------------------------------------------------------------------- /src/section_3/section3.2/Migrations/SalesContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Infrastructure; 4 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 5 | using section3._2; 6 | 7 | #nullable disable 8 | 9 | namespace section3._2.Migrations 10 | { 11 | [DbContext(typeof(SalesContext))] 12 | partial class SalesContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder.HasAnnotation("ProductVersion", "7.0.0"); 18 | 19 | modelBuilder.Entity("section3._2.Employee", b => 20 | { 21 | b.Property("EmployeeId") 22 | .ValueGeneratedOnAdd() 23 | .HasColumnType("INTEGER"); 24 | 25 | b.Property("Name") 26 | .IsRequired() 27 | .HasColumnType("TEXT"); 28 | 29 | b.HasKey("EmployeeId"); 30 | 31 | b.ToTable("Employees"); 32 | }); 33 | 34 | modelBuilder.Entity("section3._2.Sale", b => 35 | { 36 | b.Property("SaleId") 37 | .ValueGeneratedOnAdd() 38 | .HasColumnType("INTEGER"); 39 | 40 | b.Property("EmployeeId") 41 | .HasColumnType("INTEGER"); 42 | 43 | b.Property("Total") 44 | .HasColumnType("INTEGER"); 45 | 46 | b.HasKey("SaleId"); 47 | 48 | b.HasIndex("EmployeeId"); 49 | 50 | b.ToTable("Sales"); 51 | }); 52 | 53 | modelBuilder.Entity("section3._2.Sale", b => 54 | { 55 | b.HasOne("section3._2.Employee", "Employee") 56 | .WithMany("Sales") 57 | .HasForeignKey("EmployeeId") 58 | .OnDelete(DeleteBehavior.Cascade) 59 | .IsRequired(); 60 | 61 | b.Navigation("Employee"); 62 | }); 63 | 64 | modelBuilder.Entity("section3._2.Employee", b => 65 | { 66 | b.Navigation("Sales"); 67 | }); 68 | #pragma warning restore 612, 618 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/section_5/section5.4/Program.cs: -------------------------------------------------------------------------------- 1 | using Redis.OM; 2 | using Redis.OM.Modeling; 3 | using section5._4; 4 | 5 | var provider = new RedisConnectionProvider("redis://localhost:6379"); 6 | 7 | provider.Connection.DropIndexAndAssociatedRecords(typeof(Sale)); 8 | provider.Connection.DropIndexAndAssociatedRecords(typeof(Employee)); 9 | 10 | await provider.Connection.CreateIndexAsync(typeof(Sale)); 11 | await provider.Connection.CreateIndexAsync(typeof(Employee)); 12 | 13 | var employees = provider.RedisCollection(); 14 | 15 | // TODO for Coding Challenge Start here on starting-point branch 16 | 17 | // Create a couple of employees. 18 | var alice = new Employee 19 | { 20 | Name = "Alice", 21 | Age = 45, 22 | Address = new Address { StreetAddress = "Elm Street", Location = new GeoLoc(-81.957, 27.058), PostalCode = "34269" }, 23 | Sales = new List() 24 | }; 25 | 26 | var bob = new Employee 27 | { 28 | Name = "Bob", 29 | Age = 60, 30 | Address = new Address() { StreetAddress = "Bleecker Street", Location = new GeoLoc(-74.003, 40.732), PostalCode = "10014" }, 31 | Sales = new List() 32 | }; 33 | 34 | var bobKeyName = await employees.InsertAsync(bob); 35 | await employees.InsertAsync(alice); 36 | 37 | var sales = provider.RedisCollection(); 38 | var saleInsertTasks = new List>(); 39 | var random = new Random(); 40 | 41 | for (var i = 0; i < 500; i++) 42 | { 43 | saleInsertTasks.Add(sales.InsertAsync(new Sale 44 | { 45 | Total = random.Next(1000, 30000), 46 | EmployeeId = bob.Id 47 | })); 48 | } 49 | 50 | await Task.WhenAll(saleInsertTasks); 51 | 52 | bob.Sales.AddRange(saleInsertTasks.Select(x=>x.Result.Split(":")[1])); 53 | 54 | await employees.UpdateAsync(bob); 55 | 56 | var bobFromDb = employees.FindById(bob.Id!); 57 | Console.WriteLine($"Bob has: {bobFromDb!.Sales!.Count} sales"); 58 | 59 | await foreach (var employee in employees.Where(x => x.Name == "Alice")) 60 | { 61 | Console.WriteLine($"Alice's old age: {employee.Age}"); 62 | employee.Age++; 63 | } 64 | 65 | employees.Save(); 66 | 67 | Console.WriteLine($"Alice's new age: {employees.First(x=>x.Name == "Alice").Age}"); 68 | 69 | await employees.DeleteAsync(alice); 70 | Console.WriteLine($"Alice's present in Redis: {await employees.AnyAsync(x=>x.Name == "Alice")}"); 71 | 72 | await provider.Connection.UnlinkAsync(bobKeyName); 73 | Console.WriteLine($"Bob's present in Redis: {await employees.AnyAsync(x=>x.Name == "Bob")}"); 74 | // end coding challenge 75 | -------------------------------------------------------------------------------- /src/section_3/section3.4/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - section3._4 7 | 8 | 9 | 10 | 11 | 12 |
13 | 32 |
33 |
34 |
35 | @RenderBody() 36 |
37 |
38 | 39 |
40 |
41 | © 2022 - section3._4 - Privacy 42 |
43 |
44 | 45 | 46 | 47 | @await RenderSectionAsync("Scripts", required: false) 48 | 49 | 50 | --------------------------------------------------------------------------------