From 4f45a805dad6ff2de7910785dd1485fd8f8f68f1 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Fri, 3 Sep 2021 15:39:05 +0200 Subject: [PATCH 01/21] conversion in progress --- .editorconfig | 2 +- Words.Web.Core/App_Data/placeholder.txt | 0 Words.Web.Core/App_Data/words.json | 190 +++++++++++++++++++ Words.Web.Core/Controllers/HomeController.cs | 37 ---- Words.Web.Core/Models/ErrorViewModel.cs | 11 -- Words.Web.Core/Startup.cs | 10 +- Words.Web.Core/Views/Shared/Error.cshtml | 27 --- Words.Web.Core/Views/_ViewImports.cshtml | 4 +- Words.Web.Core/Words.Web.Core.csproj | 12 ++ Words.Web/Controllers/AbstractController.cs | 73 +++++++ Words.Web/Controllers/HomeController.cs | 34 +++- Words.Web/Models/{Query.cs => QueryType.cs} | 0 Words.Web/Words.Web.csproj | 4 +- 13 files changed, 313 insertions(+), 91 deletions(-) create mode 100644 Words.Web.Core/App_Data/placeholder.txt create mode 100644 Words.Web.Core/App_Data/words.json delete mode 100644 Words.Web.Core/Controllers/HomeController.cs delete mode 100644 Words.Web.Core/Models/ErrorViewModel.cs delete mode 100644 Words.Web.Core/Views/Shared/Error.cshtml create mode 100644 Words.Web/Controllers/AbstractController.cs rename Words.Web/Models/{Query.cs => QueryType.cs} (100%) diff --git a/.editorconfig b/.editorconfig index a8a1bab..e04cffc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ # Default severity for all analyzer diagnostics dotnet_analyzer_diagnostic.severity = error -csharp_using_directive_placement=inside_namespace:error +csharp_using_directive_placement = inside_namespace:error # IDE0032: Use auto property dotnet_diagnostic.IDE0032.severity = none diff --git a/Words.Web.Core/App_Data/placeholder.txt b/Words.Web.Core/App_Data/placeholder.txt new file mode 100644 index 0000000..e69de29 diff --git a/Words.Web.Core/App_Data/words.json b/Words.Web.Core/App_Data/words.json new file mode 100644 index 0000000..2e836b6 --- /dev/null +++ b/Words.Web.Core/App_Data/words.json @@ -0,0 +1,190 @@ +[ + { + "Number": 1, + "Data": { + "EncodingBytes": [ + "u7tbbutWrbta6qXWqtW1bburqqmqlVtVq3bV21VlVVVVWraVVWqtWtrVVZUpKVatVVVVVWqtrba6k1Sq", + "lVVWpVVWqlVVa1Vqq0VSSJSqqqqqqqqqqqqqqqqoUqqUpVKpKVVVVSqqoSoRIoqUqqqiUSSikipApCA" + ], + "EncodingBits": 953, + "LetterBytes": [ + "AHMAdABmAHj__QBwAHYAbABkAGgAZQB1AGIAYQBtAGUAdQBpAHIAaQBjAGUAaQBnAG0AbgBuAHQAYQAA", + "AHMAZQBuAHYAcABhAGn__QBwAGUAbwBvAGIAawBuAGEAbgBhAGsAcABvAGkAbwBzADoAeABhAGsAbgAA", + "AGQAcABz__0AcgAAAHMAagBzAG8AaAB1AGEAbwB0AGwAZQBvAHIAdABpAGwAagBsAGkAbgByAGYAYQB1", + "AGUAcgAAAGEAaQBiAGUAZQBvAG4AYQBoAHIAdgBzAHQAZQBrAG8AYQByAHIAdQBiAAAAbwBuAGEAY__9", + "AHUAdQByAHgAbgBlAGUAaQBpAGoAZQBlAGwAZQBvAGUAcQBvAHQAYwBlAGEAdQAAAHMAYgBqAAAAawBr", + "AAAAaQAAAGEAYQAAAHQAaQByAGkAYwBr__0AbAB0AGsAeQB1AGQAbABjAG4AbAB0AGsAbgB0AGcAbAB6", + "AHQAYQBoAHUAcgBuAHMAYQB1AHIAaQBlAHQAbwB1AGIAAABoAAAAaQBtAHMAdAAAAHQAdQBpAGcAbAAA", + "AHQAZQBpAHAAYQBoAGEAdABpAG8AbwByAAAAbQBmAGkAaABiAGQAaQBuAHQAYQBsAGEAbQAAAHAAcgBh", + "AGQAYwBrAHgAdABwAHkAYwByAGsAdwBhAHkAbgBmAHIAbwBoAHUAZQAAAAAAbgB0AG0AdQAAAGEAAABn", + "AAAAZAAAAAAAegAAAHQAZQAAAGkAbwBnAGkAZwBvAGkAcgBuAGkAbwB0AG7__QByAGQAeABhAG4AZQBw", + "AGEAZABiAGEAbwBlAG8AdABlAG8AawBzAG___QAgAGMAc__9AG8AbABvAGsAaQBj__0AZQBlAHQAaQBu", + "AAAAAAAAAG8AdAAAAGcAYwBnAHAAZQBuAAAAaQBnAAAAawBpAAAAdABhAGEAYQAAAGkAcgBhAG8AAABv", + "AAAAZABsAAAAYQBsAGYAbwBoAG0AcABhAGkAcABqAGwAeQAAAGkAegBvAGsAcgBlAGkAcwBhAAAAAAAA", + "AGgAAABwAG4AeQAAAAAAAABhAAAAAABsAAAAbgAAAAAAawBrAAAAAABsAGkAZgAAAGEAbwAAAGcAbgBy", + "AGQAbwB0AGUAbwBsAGwAAAAAAHMAAABtAGUAAAAAAG0AAABhAAAAZQAAAGsAbgAAAAAAdABlAAD__QAA", + "AGcAAAAAAG0AYQBpAAAAaQAAAAAAAAAAAAAAZwBhAAAAawAAAAAAAABuAAAAAAAAAAAAAA" + ] + } + }, + { + "Number": 2, + "Data": { + "EncodingBytes": [ + "ttW1WuqqrVVVVVVVVVVKqqiqqSqKiA" + ], + "EncodingBits": 175, + "LetterBytes": [ + "AHT__QBzAHIAcgB0AGEAdABhAGEAbABzAGcAbgBuAHYAYQBoAHkAbABmAHIAaQBzAGcAcwBpAGwAeQBv", + "__0AaQBuAGf__QBoAGkAcwBrAHQAbgBuAGcAZQBuAGkAdABrAG8AcAB0AHQAAABuAGcAbgBh__0AcwBh", + "AGUAaQAAAAAAZwBrAGwAaQByAG8AZgAAAGUAAABkAGEAcgBhAAAAAABkAG8AZAAAAAAAYQAA" + ] + } + }, + { + "Number": 3, + "Data": { + "EncodingBytes": [ + "u3VW1VrdVWrXaqqqqrtVVVVVq1VVVVVaqqqqqqqqrSqqqqqqqiqqqqqqipVVVVUiVVVVClVVCFQg" + ], + "EncodingBits": 453, + "LetterBytes": [ + "AHQAbwBnAHYAcgBhAGUAYgBz__0AcwBlAG4AbwBhAG0AbAByAGsAawB0AG4AZQBm__0Aa__9AGoAbQBs", + "AGsAdwBlAHUAcgBhAHQAdAByAGwAcwBhAGkAawBlAHAAZAB2AG8AcgBzAGcAawBlAHL__f_9AHAAbQBu", + "AG8AZABhAHUAbABuAHMAbwBuAGEAYgB2AHMAcv_9AHAAcABsAGIAdABuAGkAbABnAGr__QBpAGkAYQB0", + "AGQAcABh__0AdAB3AGsAcABwAGEAYQBlAHMAZQBs__0AawBhAHQAZwBjAHIAAABvAGkAcgByAHIAbwBp", + "AG8AZgB0AGwAcgB1AGQAYQBu__0AaABwAGEAawBzAHYAAAAAAG4AbQBlAHIAZwBzAGUAdABhAGYAbQBy", + "AG4AawBuAGUAbwB2AGQAYwAAAAAAZQBjAGsAAAB0AHMAYQB5AGEAaQBlAGIAaQBzAGQAbABkAHUAaAAA", + "AGsAAAAAAHQAAABhAGMAcwB2AG8AZQBsAGkAaQByAGUAYgAAAAAAAABlAG0AAABrAGwAYQBuAHMAYQBs", + "AGEAAAAAAAAAaQAAAAAAAABnAGwAbgAAAAAAAABrAAA" + ] + } + }, + { + "Number": 4, + "Data": { + "EncodingBytes": [ + "u3dtu1VVW7atVatbbW1VVVVVatbVVVVVVVW1VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVKqqqq", + "qqqqqqilVVVVVVVSpFVVVVVKpAqqqVRBVKSJIA" + ], + "EncodingBits": 705, + "LetterBytes": [ + "AHMAdABvAHYAeQBvAHIAbABw__0Adf_9AHL__QBjAHAAZwBz__0AYwBuAGEAcgBzAHQAdgBlAGQAawBy", + "AGEAdABnAGEAYQBhAGsAYQBtAG4AbABhAHQAYQBwAGUAawBwAGX__QBhAG4AZQBvAG4AeABsAGUAdgBi", + "AG8AZgBr__0AdQB0AHQAYQBlAHQAdgBnAHAAcgBvAGEAcgBkAG0AYgBvAGQAZwBrAGwAcgBiAHIAbwBu", + "AGkAaABlAHQAeQBuAG8AZQByAHMAZAB0__0AZQBnAGIAbgBwAGIAYgBmAHIAcABkAGwAbwBjAHQAeQB0", + "AGYAbABhAHMAbABqAG4AdQBoAHQAcgByAGkAdABpAGsAbgByAHIAZQBvAHAAbwBhAGkAaQBvAHP__QB0", + "AGUAaQB0AHIAdABhAHMAaQBk__0AYwBwAGUAbwB0AHQAYQBtAGsAcgBsAGEAYQBsAG0AZQBtAGsAbABz", + "AHIAYQByAGwAbgBmAGEAbwBlAGcAbQBzAGUAcgBoAHAAdAByAGEAYQByAGEAeQB5AGkAbgBk__0AAABy", + "AGIAYQB0AHQAbwByAGQAZQB0AGkAcgBzAHIAaQBhAHQAbABuAG8AbABzAHIAZABuAGsAbQByAGMAZwBk", + "AGUAbgAAAAAAZABlAAAAcwBlAGEAdABlAGUAYgBrAHMAcwB0AGUAbgBzAGMAYQBkAGgAZQB0AGEAbQBp", + "AGsAAABlAHIAYQAAAHIAAAAAAGcAdAByAHL__QBvAGgAZQBpAG4AaQBsAGsAZwBlAGUAbAAAAGwAYQBz", + "AGEAAABhAAAAAAAAAAAAAABhAHMAcABhAHIAcgB0AG7__QAAAGEAcwB0AGwAAAAAAHQAAAAAAAAAAABp", + "AHYAYQBpAAAAZwBwAAAAawAAAGEAAAAAAGUAAABzAAAAcAAAAAAAAAAAAAA" + ] + } + }, + { + "Number": 5, + "Data": { + "EncodingBytes": [ + "ut1t21Vda7VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVSqqqqqlSqqqoglShQA" + ], + "EncodingBits": 415, + "LetterBytes": [ + "AGoAdQBhAHMAbgBuAGIAbwBrAHQAZwB0AGsAZQBjAGYAagB0AHIAbgByAHYAZgBpAHQAdABoAGYAYwBt", + "__0AcgBwAHkAYQBsAHAAYQBo__0AcgBvAGkAYQBvAHAAawBtAGwAaQBpAGQAcwB5AHUAbgBhAGwAdQB4", + "AGUAbABrAG8AZQBhAHYAbgBuAGQAaQB4AG4AcwBpAGkAbwBpAGIAdABsAHQAcgByAHQAZwBuAGIAIABy", + "AGsAcABiAHMAbABkAHUAagBhAGEAZgBwAGUAdABkAHUAZwBlAHQAbwBvAG8AagBhAGL__QBkAHT__QBy", + "AHIAcgBvAGsAbwBuAGIAbgB4AGwAYQBuAGIAbgBiAGkAbABhAGEAbwBrAGUAcgBvAGUAZABuAGUAAAB0", + "AGwAcwBvAHMAbAB0AHAAcwB0AHQAZQB2AHYAZQBpAHIAAABhAHQAbAAAAGEAYQBpAGEAbwB0AG4AZQBh", + "AHIAbgBhAAAAAABsAAAAAAAAAAAAcgAAAGcAcgBrAAAAZwBkAAAAAAAAAGEAYQAAAAAAAAAA" + ] + } + }, + { + "Number": 6, + "Data": { + "EncodingBytes": [ + "u21XaqrrVVa6qqqqqqqqtVVVVVVVVVVVVVVaqqqqqqqqqqqqqqpVVVVVFVVUqVVVIlVQSg" + ], + "EncodingBits": 419, + "LetterBytes": [ + "AHIAZQBiAHMAawBvAGkAaABwAHQAbABsAG4AdQBkAGsAcgBlAGkAYQBsAGQAcwB1AGkAaQBuAGkAbgBu", + "AGcAbQBzAG4AaABiAG8AdgBuAG8AbQBvAGQAaf_9AGUAav_9AGn__QBiAHMAaQBrAGwAZQBwAHAAbwBs", + "AHIAaQB0AG4AbABlAGsAbgBvAHQAbAByAGUAcgByAHMAZQBuAHQAZwBsAGwAdgBuAG0AaQB0AHYAcgBv", + "AGIAcABrAGcAbgBzAHMAcP_9AG8AcwBtAG___QBhAGcAbwBvAG8AZQBpAHQAbgBhAGsAbABoAHQAaQBs", + "AHIAdAByAGUAbABuAGwAbgBp__0AbgBsAHQAYQBwAHMAZQBkAGkAYQBuAGEAbwAAAGcAZAByAGUAaQBh", + "AG4AcgBlAHIAZQBvAG0AZABuAG0AAAAAAGEAcgBjAHIAZP_9AHIAYQBzAG4AAABlAGQAaQAAAGEAawBl", + "AGUAdgBpAG4AbQBzAAAAZQAAAAAAYQAAAGwAYQBuAHMAYQBrAAAAAAAAAAAAZwAAAHT__QAAAAAAAA" + ] + } + }, + { + "Number": 7, + "Data": { + "EncodingBytes": [ + "u3VbVVW6qqqqqrVVVVVVqqqtVVVVVVVVVVVVVVVVVVVVVVVKqqqpKqqQqUUI" + ], + "EncodingBits": 359, + "LetterBytes": [ + "AHMAeQBr__0AcwBrAHkAaABtAGwAdABhAGwAbwBvAGcAaQBkAGUAdABzAG4AbQBlAGYAbgBlAHUAZQBt", + "AHQAawB0AG8AbgByAGkAZABzAHIAawBl__0Acv_9AG0Aaf_9AG0AaQBrAHMAcgBwAHAAYQBrAG8AaABu", + "AGEAZQBlAHMAaQBsAHYAcwBmAHQAZABlAHMAbABpAGwAagBmAHQAYf_9AHAAYQBlAGkAdAB0AGkAbgBi", + "AHUAaQBpAG4AeABvAGsAbgBmAHMAZQBzAGQAeQBrAHgAcwBlAGwAZQB0AHMAaQBiAHIAdAB1AGcAZABl", + "AGsAcgBpAHMAaQBrAGUAZQBzAGkAcwBnAG8AcgAAAGEAbgBpAHMAYQByAHIAaABzAHQAYQBtAGEAAABn", + "AAAAawBwAGH__QBvAGsAcgByAAAAZAAAAAAAAABkAHYAcAAAAGkAZQAAAAAAYQBwAAAAAAAAAGEAAA" + ] + } + }, + { + "Number": 8, + "Data": { + "EncodingBytes": [ + "ut1tVVVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpVVSpUSA" + ], + "EncodingBits": 267, + "LetterBytes": [ + "AGsAYQBkAHMAbQByAGcAcABtAHQAZQBvAGkAZQBmAGUAdQByAHIAZwByAG7__QB0AGwAYf_9AGEAbABl", + "AG8AZwBzAHQAZgBu__0AaQBrAG0AZQBrAGkAZgBnAHYAYgB0AGYAbABvAHIAaQBzAGUAZQB2AG8AaQBt", + "AGUAYQBlAHIAcgBlAHIAbgBwAHMAbABsAHYAYQByAHMAZgBlAGkAaQBhAGEAbABrAGsAbAB0AHMAawB2", + "AGsAaQBhAG4AdQBlAHQAbgBnAGEAcwBuAGkAZQBuAGUAYQBpAAAAbQBkAG4AbgBzAG4AbgBmAAAAZQBn", + "AHMAAAB0AGQAdAAAAAAAYQAAAGUAAAAAAAA" + ] + } + }, + { + "Number": 9, + "Data": { + "EncodingBytes": [ + "tdttVW1VWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVVVSqqpKlKkg" + ], + "EncodingBits": 353, + "LetterBytes": [ + "AHUAdABsAHMAaQBhAG0AZQB2AHYAYQB2AGsAYQByAGUAaQBzAGsAdABsAGgAcgBp__0AbgBzAHMAdABh", + "AGn__QB1AGsAbgB0AGQAbgB0AG8AbABtAG4AdgBuAG8AdABlAGkAaQBzAHMAYQBzAHUAYQByAHYAZgBu", + "AGwAaQBwAHQAbQBkAGQAaQBpAGkAZwBzAG4AZQBmAGEAZwBzAHQAcwB4AHMAcwB0AG7__QBrAHUAYQBl", + "AGUAZQBoAGoAbwBzAHIAdABkAG4AdABtAHIAbwB1AGwAaQBzAHMAcwBwAHP__QBhAHQAawBlAG___QBv", + "AHQAYQBzAHIAZABhAGQAcgBuAG0AcgBqAHMAawBrAAAAZABvAGH__QByAGT__QBzAHkAbgAAAG0AbgBy", + "AGkAbgBuAGEAZABpAAAAcwAAAG4AaQBzAAAAZABuAAAAZwBuAHQAAABnAAAAZwAAAAAAAA" + ] + } + }, + { + "Number": 10, + "Data": { + "EncodingBytes": [ + "u1qrqraqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoqolSKA" + ], + "EncodingBits": 349, + "LetterBytes": [ + "AGYAeQBiAGcAcv_9AGwAcgBwAGEAcgBv__0AcgBrAHMAIABzAGMAbgBlAGUAcgBuAGEAdAB2AGsAcwBz", + "AHIAZQB1AG4AaQBh__3__QB0AHMAZABtAG4AbAByAHYAdgBhAG8AaQBtAGkAbABzAGUAZQBuAG4AdABl", + "AG4AIABuAHIAcgBkAHYAdgByAGcAbgBlAHMAcwBhAGH__QBwAHMAaQBkAGsAawBqAGwAcgByAGsAbwBs", + "AHIAcv_9AHMAZABlAG8ALf_9AGkAaQBtAGsAZQBzAG0AbABnAGQAZABmAGEAcgBlAG3__QBnAGEAYf_9", + "AG0AaQBuAGkAcgBuAG4AbgByAHAAbgB0AHMAYQBpAGQAZABlAGEAZwBhAHMAcgBuAGUAZQBsAG4AcwB0", + "AGkAZQBnAAAAAABzAGoAYv_9AG8AAAAAAGUAAABlAHIAbgAAAHQAAAAAAHkAZwAA" + ] + } + } +] \ No newline at end of file diff --git a/Words.Web.Core/Controllers/HomeController.cs b/Words.Web.Core/Controllers/HomeController.cs deleted file mode 100644 index e537f60..0000000 --- a/Words.Web.Core/Controllers/HomeController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Words.Web.Core.Models; - -namespace Words.Web.Core.Controllers -{ - public class HomeController : Controller - { - private readonly ILogger _logger; - - public HomeController(ILogger logger) - { - _logger = logger; - } - - public IActionResult Index() - { - return View(); - } - - public IActionResult Privacy() - { - return View(); - } - - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public IActionResult Error() - { - return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); - } - } -} diff --git a/Words.Web.Core/Models/ErrorViewModel.cs b/Words.Web.Core/Models/ErrorViewModel.cs deleted file mode 100644 index 01238a2..0000000 --- a/Words.Web.Core/Models/ErrorViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Words.Web.Core.Models -{ - public class ErrorViewModel - { - public string RequestId { get; set; } = null!; - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - } -} diff --git a/Words.Web.Core/Startup.cs b/Words.Web.Core/Startup.cs index 1b71eec..1c3cb23 100644 --- a/Words.Web.Core/Startup.cs +++ b/Words.Web.Core/Startup.cs @@ -37,21 +37,25 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); + app.UseHsts(); // ? } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); - app.UseAuthorization(); + app.UseAuthorization(); // ? - app.UseEndpoints(endpoints => + app.UseEndpoints(endpoints => // ? { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); + + AppDomain.CurrentDomain.SetData( + "DataDirectory", + Path.Combine(env.ContentRootPath, "App_Data")); } } } diff --git a/Words.Web.Core/Views/Shared/Error.cshtml b/Words.Web.Core/Views/Shared/Error.cshtml deleted file mode 100644 index bfbc079..0000000 --- a/Words.Web.Core/Views/Shared/Error.cshtml +++ /dev/null @@ -1,27 +0,0 @@ -@model ErrorViewModel -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -@if (Model?.ShowRequestId ?? false) -{ -

- Request ID: @Model.RequestId -

-} - -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that - occurred. -

-

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

diff --git a/Words.Web.Core/Views/_ViewImports.cshtml b/Words.Web.Core/Views/_ViewImports.cshtml index 011c509..bbb7b16 100644 --- a/Words.Web.Core/Views/_ViewImports.cshtml +++ b/Words.Web.Core/Views/_ViewImports.cshtml @@ -1,3 +1,3 @@ -@using Words.Web.Core -@using Words.Web.Core.Models +@* @using Words.Web.Core +@using Words.Web.Core.Models *@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/Words.Web.Core/Words.Web.Core.csproj b/Words.Web.Core/Words.Web.Core.csproj index 183a136..f470e79 100644 --- a/Words.Web.Core/Words.Web.Core.csproj +++ b/Words.Web.Core/Words.Web.Core.csproj @@ -3,5 +3,17 @@ net6.0 + + + + + + + + + + + + diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs new file mode 100644 index 0000000..2e60426 --- /dev/null +++ b/Words.Web/Controllers/AbstractController.cs @@ -0,0 +1,73 @@ +#if NET40 +#nullable enable +#endif + +namespace Words.Web +{ +#if NET + using System.Data; + using Dapper; + using Microsoft.Extensions.Caching.Memory; +#endif + + public abstract class AbstractController + { +#if NET + private readonly IMemoryCache memoryCache; + private readonly IDbConnection connection; + + protected AbstractController(IMemoryCache memoryCache, IDbConnection connection) => + (this.memoryCache, this.connection) = (memoryCache, connection); +#endif + + protected TResult? CacheGet(string cacheKey) + { +#if NET + return memoryCache.Get(cacheKey); +#else + return HttpContext.Cache.Get($"query-{id}") as TResult?; +#endif + } + + protected async Task Transact( + Func> func, + CancellationToken cancellationToken) + { +#if NET + IDbTransaction tran = connection.BeginTransaction(); + TResult result = await func.Invoke(connection, tran); + tran.Commit(); + return result; +#else + return await MvcApplication.Transact( + func, + cancellationToken); +#endif + } + + protected async Task Transact( + Func action, + CancellationToken cancellationToken) + { +#if NET + _ = await Transact(async (conn, tran) => + { + await action.Invoke(conn, tran); + return false; + }, + cancellationToken); +#else + await MvcApplication.Transact( + action, + cancellationToken); +#endif + } + +#if NET40 + private void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) + { + Log.Info("Cache item {key} removed due to {reason}: {@value}", key, reason, value); + } +#endif + } +} diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index 7dde8fd..f8de1f9 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -1,7 +1,10 @@ -#nullable enable +#if !NET +#nullable enable +#endif namespace Words.Web.Controllers { +#if NET40 using System; using System.Collections.Generic; using System.Diagnostics; @@ -18,16 +21,36 @@ namespace Words.Web.Controllers using ViewModels; using Words.Web.Entities; using Words.Web.Infrastructure; +#else + using System.Data; + using Dapper; + using Microsoft.AspNetCore.Mvc; + using Microsoft.Extensions.Caching.Memory; + using Words.Web.ViewModels; +#endif - public class HomeController : Controller + public class HomeController : AbstractController { +#if NET40 private static readonly Logger Log = LogManager.GetCurrentClassLogger(); +#else + private readonly ILogger logger; + + public HomeController( + IMemoryCache memoryCache, + ILogger logger, + IDbConnection connection) + : base(memoryCache, connection) + { + this.logger = logger; + } +#endif public async Task Index(int? id, CancellationToken cancellationToken) { if (id != null) { - string text = await MvcApplication.Transact(async (conn, tran) => + string text = await Transact(async (conn, tran) => await conn.QuerySingleAsync("select text from query where query_id = @id", new { id }), cancellationToken); if (HttpContext.Cache.Get($"query-{id}") is ResultsViewModel results) @@ -158,11 +181,6 @@ SELECT q.query_id as queryid return recentQueries; } - private void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) - { - Log.Info("Cache item {key} removed due to {reason}: {@value}", key, reason, value); - } - private record QueryId(int Id, string Text); } } diff --git a/Words.Web/Models/Query.cs b/Words.Web/Models/QueryType.cs similarity index 100% rename from Words.Web/Models/Query.cs rename to Words.Web/Models/QueryType.cs diff --git a/Words.Web/Words.Web.csproj b/Words.Web/Words.Web.csproj index 5430cad..83d2086 100644 --- a/Words.Web/Words.Web.csproj +++ b/Words.Web/Words.Web.csproj @@ -107,7 +107,7 @@ - + @@ -200,4 +200,4 @@ --> - \ No newline at end of file + From 61d33916efc7156922f52591c017f81719e4c292 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Sat, 4 Sep 2021 10:51:11 +0200 Subject: [PATCH 02/21] fix --- Words.Web/Controllers/AbstractController.cs | 2 +- Words.Web/Controllers/HomeController.cs | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs index 2e60426..49b332b 100644 --- a/Words.Web/Controllers/AbstractController.cs +++ b/Words.Web/Controllers/AbstractController.cs @@ -63,7 +63,7 @@ await MvcApplication.Transact( #endif } -#if NET40 +#if !NET private void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) { Log.Info("Cache item {key} removed due to {reason}: {@value}", key, reason, value); diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index f8de1f9..ccd015e 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -1,10 +1,17 @@ -#if !NET +#if NET #nullable enable #endif namespace Words.Web.Controllers { -#if NET40 +#if NET + using System; + using System.Data; + using Dapper; + using Microsoft.AspNetCore.Mvc; + using Microsoft.Extensions.Caching.Memory; + using Words.Web.ViewModels; +#else using System; using System.Collections.Generic; using System.Diagnostics; @@ -21,12 +28,6 @@ namespace Words.Web.Controllers using ViewModels; using Words.Web.Entities; using Words.Web.Infrastructure; -#else - using System.Data; - using Dapper; - using Microsoft.AspNetCore.Mvc; - using Microsoft.Extensions.Caching.Memory; - using Words.Web.ViewModels; #endif public class HomeController : AbstractController From 223152e7eb29ea1652a883d4cd8be8f02ba59fb5 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Sat, 4 Sep 2021 10:53:28 +0200 Subject: [PATCH 03/21] fix --- Words.Web/Controllers/AbstractController.cs | 4 ++-- Words.Web/Controllers/HomeController.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs index 49b332b..0f3867d 100644 --- a/Words.Web/Controllers/AbstractController.cs +++ b/Words.Web/Controllers/AbstractController.cs @@ -20,10 +20,10 @@ protected AbstractController(IMemoryCache memoryCache, IDbConnection connection) (this.memoryCache, this.connection) = (memoryCache, connection); #endif - protected TResult? CacheGet(string cacheKey) + protected object? CacheGet(string cacheKey) { #if NET - return memoryCache.Get(cacheKey); + return memoryCache.Get(cacheKey); #else return HttpContext.Cache.Get($"query-{id}") as TResult?; #endif diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index ccd015e..1f39e62 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -54,7 +54,7 @@ public async Task Index(int? id, CancellationToken cancellationTok string text = await Transact(async (conn, tran) => await conn.QuerySingleAsync("select text from query where query_id = @id", new { id }), cancellationToken); - if (HttpContext.Cache.Get($"query-{id}") is ResultsViewModel results) + if (CacheGet($"query-{id}") is ResultsViewModel results) { QueryViewModel cachedModel = new() { Text = text, Results = results }; if (results.Count == 0) From 181ad69503f940f7adf5379a57a939cfb77291c1 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Sat, 4 Sep 2021 11:02:16 +0200 Subject: [PATCH 04/21] fix --- Words.Web/Controllers/AbstractController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs index 0f3867d..f15f469 100644 --- a/Words.Web/Controllers/AbstractController.cs +++ b/Words.Web/Controllers/AbstractController.cs @@ -7,10 +7,11 @@ namespace Words.Web #if NET using System.Data; using Dapper; + using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; #endif - public abstract class AbstractController + public abstract class AbstractController : Controller { #if NET private readonly IMemoryCache memoryCache; From 722add3d371f32d380ca88794b071193b2983104 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Sun, 5 Sep 2021 09:36:45 +0200 Subject: [PATCH 05/21] fix --- Words.Web/Controllers/HomeController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index 1f39e62..55fcaae 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -7,6 +7,7 @@ namespace Words.Web.Controllers #if NET using System; using System.Data; + using System.Diagnostics; using Dapper; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; From 2a00d4312414655bd91f28eefa9627c17d2a879e Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Sun, 5 Sep 2021 11:14:36 +0200 Subject: [PATCH 06/21] fix --- Words.Web.Core/WordFinders.cs | 12 ++++++++++++ Words.Web/Controllers/AbstractController.cs | 18 +++++++++++++++++- Words.Web/Controllers/HomeController.cs | 4 ++-- 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 Words.Web.Core/WordFinders.cs diff --git a/Words.Web.Core/WordFinders.cs b/Words.Web.Core/WordFinders.cs new file mode 100644 index 0000000..1689785 --- /dev/null +++ b/Words.Web.Core/WordFinders.cs @@ -0,0 +1,12 @@ +namespace Words.Web +{ + using System.Collections.Generic; + + public class WordFinders + { + public List Matches(string text, SearchType searchType, int limit) + { + return new List(); + } + } +} diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs index f15f469..2b481eb 100644 --- a/Words.Web/Controllers/AbstractController.cs +++ b/Words.Web/Controllers/AbstractController.cs @@ -16,9 +16,16 @@ public abstract class AbstractController : Controller #if NET private readonly IMemoryCache memoryCache; private readonly IDbConnection connection; + private readonly WordFinders wordFinders; - protected AbstractController(IMemoryCache memoryCache, IDbConnection connection) => + protected AbstractController( + IMemoryCache memoryCache, + IDbConnection connection, + WordFinders wordFinders) + { (this.memoryCache, this.connection) = (memoryCache, connection); + this.wordFinders = wordFinders; + } #endif protected object? CacheGet(string cacheKey) @@ -64,6 +71,15 @@ await MvcApplication.Transact( #endif } + protected List Matches(string text, SearchType searchType, int limit) + { +#if NET + return wordFinders.Matches(text, searchType, limit); +#else + return MvcApplication.Matches(text, searchType, limit); +#endif + } + #if !NET private void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) { diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index 55fcaae..345e210 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -68,7 +68,7 @@ public async Task Index(int? id, CancellationToken cancellationTok // perform search again Stopwatch sw = Stopwatch.StartNew(); - List matches = MvcApplication.Matches(text, SearchType.All, 100); + List matches = Matches(text, SearchType.All, 100); sw.Stop(); results = new ResultsViewModel(text, matches, sw.Elapsed.TotalMilliseconds); _ = HttpContext.Cache.Add( @@ -107,7 +107,7 @@ public async Task Search(QueryViewModel q, CancellationToken cance } Stopwatch sw = Stopwatch.StartNew(); - List matches = MvcApplication.Matches(q.Text, SearchType.All, 100); + List matches = Matches(q.Text, SearchType.All, 100); sw.Stop(); ResultsViewModel results = new(q.Text, matches, sw.Elapsed.TotalMilliseconds); Log.Info(CultureInfo.InvariantCulture, "Query '{0}',{1:F2}", q.Text, sw.Elapsed.TotalMilliseconds); From 7be6e9fb44a9c7bea9af86e5054321acff13eb5f Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Sun, 5 Sep 2021 23:59:55 +0200 Subject: [PATCH 07/21] fixes --- Words.Web.Core/Extensions.cs | 20 ++++++ Words.Web.Core/Words.Web.Core.csproj | 3 + Words.Web/Controllers/AbstractController.cs | 39 ++++++++++-- Words.Web/Controllers/HomeController.cs | 70 +++++++++++---------- Words.Web/Infrastructure/Extensions.cs | 13 +++- Words.Web/ViewModels/QueryViewModel.cs | 4 ++ 6 files changed, 111 insertions(+), 38 deletions(-) create mode 100644 Words.Web.Core/Extensions.cs diff --git a/Words.Web.Core/Extensions.cs b/Words.Web.Core/Extensions.cs new file mode 100644 index 0000000..444d571 --- /dev/null +++ b/Words.Web.Core/Extensions.cs @@ -0,0 +1,20 @@ +namespace Words.Web.Core +{ + public static class Extensions + { + private static readonly Action cacheItemRemoved; + + static Extensions() + { + cacheItemRemoved = LoggerMessage.Define( + LogLevel.Information, + new EventId(1, nameof(CacheItemRemoved)), + "Cache item {Key} removed due to {Reason}: {@Value}"); + } + + public static void CacheItemRemoved(this ILogger logger, object key, object reason, object value) + { + cacheItemRemoved.Invoke(logger, key, reason, value, null); + } + } +} diff --git a/Words.Web.Core/Words.Web.Core.csproj b/Words.Web.Core/Words.Web.Core.csproj index f470e79..8d58dd1 100644 --- a/Words.Web.Core/Words.Web.Core.csproj +++ b/Words.Web.Core/Words.Web.Core.csproj @@ -6,6 +6,9 @@ + + + diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs index 2b481eb..31a1531 100644 --- a/Words.Web/Controllers/AbstractController.cs +++ b/Words.Web/Controllers/AbstractController.cs @@ -6,9 +6,9 @@ namespace Words.Web { #if NET using System.Data; - using Dapper; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; + using Words.Web.Core; #endif public abstract class AbstractController : Controller @@ -16,14 +16,18 @@ public abstract class AbstractController : Controller #if NET private readonly IMemoryCache memoryCache; private readonly IDbConnection connection; + private readonly ILogger logger; private readonly WordFinders wordFinders; protected AbstractController( IMemoryCache memoryCache, IDbConnection connection, + ILogger logger, WordFinders wordFinders) { - (this.memoryCache, this.connection) = (memoryCache, connection); + this.memoryCache = memoryCache; + this.connection = connection; + this.logger = logger; this.wordFinders = wordFinders; } #endif @@ -37,6 +41,27 @@ protected AbstractController( #endif } + protected void CachePut(string key, object item, TimeSpan expirationDelay) + { +#if NET + MemoryCacheEntryOptions opts = new() + { + AbsoluteExpirationRelativeToNow = expirationDelay + }; + opts.RegisterPostEvictionCallback(EvictionCallback); + memoryCache.Set(key, item, opts); +#else + _ = HttpContext.Cache.Add( + key, + item, + null, + Cache.NoAbsoluteExpiration, + expirationDelay, + CacheItemPriority.Normal, + OnCacheItemRemoved); +#endif + } + protected async Task Transact( Func> func, CancellationToken cancellationToken) @@ -80,10 +105,16 @@ protected List Matches(string text, SearchType searchType, int limit) #endif } -#if !NET + +#if NET + private void EvictionCallback(object key, object value, EvictionReason reason, object state) + { + logger.CacheItemRemoved(key, reason, value); + } +#else private void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) { - Log.Info("Cache item {key} removed due to {reason}: {@value}", key, reason, value); + LogManager.GetCurrentClassLogger().Info("Cache item {key} removed due to {reason}: {@value}", key, reason, value); } #endif } diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index 345e210..18e4bb2 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -8,10 +8,16 @@ namespace Words.Web.Controllers using System; using System.Data; using System.Diagnostics; + using static System.FormattableString; + using System.Text; using Dapper; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; + using Words.Web.Entities; + using Words.Web.Infrastructure; + using Words.Web.Models; using Words.Web.ViewModels; + using Microsoft.Net.Http.Headers; #else using System; using System.Collections.Generic; @@ -33,7 +39,7 @@ namespace Words.Web.Controllers public class HomeController : AbstractController { -#if NET40 +#if !NET private static readonly Logger Log = LogManager.GetCurrentClassLogger(); #else private readonly ILogger logger; @@ -41,8 +47,9 @@ public class HomeController : AbstractController public HomeController( IMemoryCache memoryCache, ILogger logger, - IDbConnection connection) - : base(memoryCache, connection) + IDbConnection connection, + WordFinders wordFinders) + : base(memoryCache, connection, logger, wordFinders) { this.logger = logger; } @@ -71,14 +78,7 @@ public async Task Index(int? id, CancellationToken cancellationTok List matches = Matches(text, SearchType.All, 100); sw.Stop(); results = new ResultsViewModel(text, matches, sw.Elapsed.TotalMilliseconds); - _ = HttpContext.Cache.Add( - $"query-{id}", - results, - null, - Cache.NoAbsoluteExpiration, - TimeSpan.FromDays(1), - CacheItemPriority.Normal, - OnCacheItemRemoved); + CachePut($"query-{id}", results, TimeSpan.FromDays(1)); QueryViewModel model = new() { Text = text, Results = results }; if (results.Count == 0) { @@ -101,7 +101,7 @@ public async Task Search(QueryViewModel q, CancellationToken cance return View(q); } - if (HttpContext.Cache.Get($"query-{Encoding.UTF8.GetBytes(q.Text).ComputeHash()}") is QueryId cachedQueryId) + if (CacheGet($"query-{Encoding.UTF8.GetBytes(q.Text).ComputeHash()}") is QueryId cachedQueryId) { return RedirectToAction(nameof(Index), new { id = cachedQueryId.Id }); } @@ -110,15 +110,30 @@ public async Task Search(QueryViewModel q, CancellationToken cance List matches = Matches(q.Text, SearchType.All, 100); sw.Stop(); ResultsViewModel results = new(q.Text, matches, sw.Elapsed.TotalMilliseconds); +#if NET + logger.LogInformation(Invariant($"Query '{q.Text}',{sw.Elapsed.TotalMilliseconds:F2}")); +#else Log.Info(CultureInfo.InvariantCulture, "Query '{0}',{1:F2}", q.Text, sw.Elapsed.TotalMilliseconds); +#endif // save query - int queryId = await MvcApplication.Transact(async (connection, tran) => + int queryId = await Transact(async (connection, tran) => { int id = await connection.QuerySingleAsync(@" insert into query(type, text, elapsed_milliseconds, created_date, user_agent, user_host_address, browser_screen_pixels_height, browser_screen_pixels_width) values (@type, @text, @elapsedmilliseconds, @createddate, @useragent, @userhostaddress::cidr, @browserscreenpixelsheight, @browserscreenpixelswidth) returning query_id", +#if NET + new Query( + Type: QueryType.Word.ToString(), + Text: q.Text, + ElapsedMilliseconds: (int)Math.Round(sw.Elapsed.TotalMilliseconds), + CreatedDate: DateTime.UtcNow, + UserAgent: Request.Headers[HeaderNames.UserAgent], + UserHostAddress: Request.HttpContext.Connection.RemoteIpAddress?.ToString() ?? string.Empty, + BrowserScreenPixelsHeight: 480, + BrowserScreenPixelsWidth: 640), +#else new Query( Type: QueryType.Word.ToString(), Text: q.Text, @@ -128,41 +143,34 @@ insert into query(type, text, elapsed_milliseconds, created_date, user_agent, us UserHostAddress: Request.UserHostAddress, BrowserScreenPixelsHeight: Request.Browser.ScreenPixelsHeight, BrowserScreenPixelsWidth: Request.Browser.ScreenPixelsWidth), +#endif tran); return id; }, cancellationToken); - _ = HttpContext.Cache.Add( + CachePut( $"query-{queryId}", results, - null, - Cache.NoAbsoluteExpiration, - TimeSpan.FromDays(1), - CacheItemPriority.Normal, - OnCacheItemRemoved); + TimeSpan.FromDays(1)); - _ = HttpContext.Cache.Add( + CachePut( $"query-{Encoding.UTF8.GetBytes(q.Text).ComputeHash()}", new QueryId(queryId, q.Text), - null, - Cache.NoAbsoluteExpiration, - TimeSpan.FromDays(1), - CacheItemPriority.Normal, - OnCacheItemRemoved); + TimeSpan.FromDays(1)); return RedirectToAction(nameof(Index), new { id = queryId }); } private async Task GetRecentQueries(CancellationToken cancellationToken) { - if (HttpContext.Cache.Get("recent-queries") is RecentQuery[] cachedRecentQueries) + if (CacheGet("recent-queries") is RecentQuery[] cachedRecentQueries) { return cachedRecentQueries; } RecentQuery[] recentQueries = - await MvcApplication.Transact(async (connection, tran) => + await Transact(async (connection, tran) => { IEnumerable qs = await connection.QueryAsync(@" SELECT q.query_id as queryid @@ -172,14 +180,10 @@ SELECT q.query_id as queryid return qs.ToArray().Randomize().Take(20).OrderBy(x => x.CreatedDate).ToArray(); }, cancellationToken); - _ = HttpContext.Cache.Add( + CachePut( "recent-queries", recentQueries, - null, - Cache.NoAbsoluteExpiration, - TimeSpan.FromDays(1), - CacheItemPriority.Normal, - OnCacheItemRemoved); + TimeSpan.FromDays(1)); return recentQueries; } diff --git a/Words.Web/Infrastructure/Extensions.cs b/Words.Web/Infrastructure/Extensions.cs index 9710572..bd655dc 100644 --- a/Words.Web/Infrastructure/Extensions.cs +++ b/Words.Web/Infrastructure/Extensions.cs @@ -9,12 +9,18 @@ namespace Words.Web.Infrastructure using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; +#if NET + using Microsoft.AspNetCore.Mvc.Routing; +#else using System.Web.Hosting; using System.Web.Mvc; +#endif using Dapper; + using System.Globalization; public static class UrlExtensions { +#if !NET public static string ContentCacheBreak(this UrlHelper url, string contentPath) { if (contentPath == null) @@ -38,6 +44,7 @@ public static string ContentCacheBreak(this UrlHelper url, string contentPath) return $"{url.Content(contentPath)}{hashPart}"; } +#endif public static string ComputeHash(this byte[] bytes) { @@ -46,7 +53,11 @@ public static string ComputeHash(this byte[] bytes) StringBuilder hashBuilder = new(); foreach (byte b in hash) { +#if NET + hashBuilder.Append(CultureInfo.InvariantCulture, $"{b:x2}"); +#else _ = hashBuilder.Append($"{b:x2}"); +#endif } return hashBuilder.ToString(); @@ -63,4 +74,4 @@ public static async Task> QueryAsync( return result; } } -} \ No newline at end of file +} diff --git a/Words.Web/ViewModels/QueryViewModel.cs b/Words.Web/ViewModels/QueryViewModel.cs index 034dfeb..0bcdcde 100644 --- a/Words.Web/ViewModels/QueryViewModel.cs +++ b/Words.Web/ViewModels/QueryViewModel.cs @@ -8,7 +8,11 @@ public class QueryViewModel { public QueryViewModel() { +#if NET + Recent = Array.Empty(); +#else Recent = new RecentQuery[0]; +#endif } [Required(ErrorMessage = "*")] From 5617e1c6dd96203cb1c113437b4e378b3eb85c44 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Mon, 6 Sep 2021 22:42:55 +0200 Subject: [PATCH 08/21] fix --- Words.Web.Core/Extensions.cs | 16 +++++++++++++++- Words.Web/Controllers/HomeController.cs | 3 ++- Words.Web/Infrastructure/Extensions.cs | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Words.Web.Core/Extensions.cs b/Words.Web.Core/Extensions.cs index 444d571..a23e80f 100644 --- a/Words.Web.Core/Extensions.cs +++ b/Words.Web.Core/Extensions.cs @@ -1,20 +1,34 @@ namespace Words.Web.Core { + using static System.FormattableString; + public static class Extensions { private static readonly Action cacheItemRemoved; + private static readonly Action queryElapsed; static Extensions() { + int eventId = 1; cacheItemRemoved = LoggerMessage.Define( LogLevel.Information, - new EventId(1, nameof(CacheItemRemoved)), + new EventId(eventId++, nameof(CacheItemRemoved)), "Cache item {Key} removed due to {Reason}: {@Value}"); + + queryElapsed = LoggerMessage.Define( + LogLevel.Information, + new EventId(eventId++, nameof(QueryElapsed)), + "Query '{Text}',{MilliSeconds:F2}"); } public static void CacheItemRemoved(this ILogger logger, object key, object reason, object value) { cacheItemRemoved.Invoke(logger, key, reason, value, null); } + + public static void QueryElapsed(this ILogger logger, string text, double milliseconds) + { + queryElapsed.Invoke(logger, text, milliseconds, null); + } } } diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index 18e4bb2..0fc6e72 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -18,6 +18,7 @@ namespace Words.Web.Controllers using Words.Web.Models; using Words.Web.ViewModels; using Microsoft.Net.Http.Headers; + using Words.Web.Core; #else using System; using System.Collections.Generic; @@ -111,7 +112,7 @@ public async Task Search(QueryViewModel q, CancellationToken cance sw.Stop(); ResultsViewModel results = new(q.Text, matches, sw.Elapsed.TotalMilliseconds); #if NET - logger.LogInformation(Invariant($"Query '{q.Text}',{sw.Elapsed.TotalMilliseconds:F2}")); + logger.QueryElapsed(q.Text, sw.Elapsed.TotalMilliseconds); #else Log.Info(CultureInfo.InvariantCulture, "Query '{0}',{1:F2}", q.Text, sw.Elapsed.TotalMilliseconds); #endif diff --git a/Words.Web/Infrastructure/Extensions.cs b/Words.Web/Infrastructure/Extensions.cs index bd655dc..c77ff05 100644 --- a/Words.Web/Infrastructure/Extensions.cs +++ b/Words.Web/Infrastructure/Extensions.cs @@ -48,7 +48,7 @@ public static string ContentCacheBreak(this UrlHelper url, string contentPath) public static string ComputeHash(this byte[] bytes) { - using MD5 md5 = MD5.Create(); + using SHA512 md5 = SHA512.Create(); byte[] hash = md5.ComputeHash(bytes); StringBuilder hashBuilder = new(); foreach (byte b in hash) From f7c012ce6e75e972c224bcf0409496e4462162dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lidstr=C3=B6m?= Date: Mon, 6 Sep 2021 07:41:22 +0200 Subject: [PATCH 09/21] fix --- Words.Web/Words.Web.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Words.Web/Words.Web.csproj b/Words.Web/Words.Web.csproj index 83d2086..b1ffe28 100644 --- a/Words.Web/Words.Web.csproj +++ b/Words.Web/Words.Web.csproj @@ -97,6 +97,7 @@ + From 9eb37740a9fdebe21d8048d2c9635e1d4a803f43 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Mon, 6 Sep 2021 22:50:42 +0200 Subject: [PATCH 10/21] fix mvc --- Words.Web.Core/Extensions.cs | 2 -- Words.Web/Controllers/AbstractController.cs | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Words.Web.Core/Extensions.cs b/Words.Web.Core/Extensions.cs index a23e80f..ffb3f42 100644 --- a/Words.Web.Core/Extensions.cs +++ b/Words.Web.Core/Extensions.cs @@ -1,7 +1,5 @@ namespace Words.Web.Core { - using static System.FormattableString; - public static class Extensions { private static readonly Action cacheItemRemoved; diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs index 31a1531..0281785 100644 --- a/Words.Web/Controllers/AbstractController.cs +++ b/Words.Web/Controllers/AbstractController.cs @@ -9,6 +9,13 @@ namespace Words.Web using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using Words.Web.Core; +#else + using System; + using System.Collections.Generic; + using System.Data; + using System.Threading; + using System.Web.Caching; + using System.Web.Mvc; #endif public abstract class AbstractController : Controller From 62396912e5b20ff97f32fb2b6c12f29989a5349a Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Mon, 6 Sep 2021 22:52:42 +0200 Subject: [PATCH 11/21] fix mvc --- Words.Web/Controllers/AbstractController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs index 0281785..7426b00 100644 --- a/Words.Web/Controllers/AbstractController.cs +++ b/Words.Web/Controllers/AbstractController.cs @@ -14,6 +14,7 @@ namespace Words.Web using System.Collections.Generic; using System.Data; using System.Threading; + using System.Threading.Tasks; using System.Web.Caching; using System.Web.Mvc; #endif From 4bd57ee4fb29afc996c2c2d58de8079f50eadd1c Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Mon, 6 Sep 2021 22:54:48 +0200 Subject: [PATCH 12/21] fix mvc --- Words.Web/Controllers/AbstractController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Words.Web/Controllers/AbstractController.cs b/Words.Web/Controllers/AbstractController.cs index 7426b00..2b55039 100644 --- a/Words.Web/Controllers/AbstractController.cs +++ b/Words.Web/Controllers/AbstractController.cs @@ -17,6 +17,7 @@ namespace Words.Web using System.Threading.Tasks; using System.Web.Caching; using System.Web.Mvc; + using NLog; #endif public abstract class AbstractController : Controller @@ -45,7 +46,7 @@ protected AbstractController( #if NET return memoryCache.Get(cacheKey); #else - return HttpContext.Cache.Get($"query-{id}") as TResult?; + return HttpContext.Cache.Get(cacheKey); #endif } From 93f0ffd707a59f9c635eb3d2aa8857619b66c15f Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Wed, 8 Sep 2021 16:23:12 +0200 Subject: [PATCH 13/21] startup --- Words.Web.Core/Startup.cs | 17 ++++++----------- Words.Web.Core/Words.Web.Core.csproj | 1 + Words.Web.Core/WordsOptions.cs | 9 +++++++++ Words.Web.Core/appsettings.json | 3 +++ 4 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 Words.Web.Core/WordsOptions.cs diff --git a/Words.Web.Core/Startup.cs b/Words.Web.Core/Startup.cs index 1c3cb23..73df231 100644 --- a/Words.Web.Core/Startup.cs +++ b/Words.Web.Core/Startup.cs @@ -1,16 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - namespace Words.Web.Core { + using System.Data; + using Npgsql; + public class Startup { public Startup(IConfiguration configuration) @@ -23,7 +15,10 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + WordsOptions wordsOptions = new(); + Configuration.GetSection(WordsOptions.Words).Bind(wordsOptions); services.AddControllersWithViews(); + services.AddScoped(sp => new NpgsqlConnection(wordsOptions.ConnectionString)); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/Words.Web.Core/Words.Web.Core.csproj b/Words.Web.Core/Words.Web.Core.csproj index 8d58dd1..41957b7 100644 --- a/Words.Web.Core/Words.Web.Core.csproj +++ b/Words.Web.Core/Words.Web.Core.csproj @@ -17,6 +17,7 @@ + diff --git a/Words.Web.Core/WordsOptions.cs b/Words.Web.Core/WordsOptions.cs new file mode 100644 index 0000000..009fef4 --- /dev/null +++ b/Words.Web.Core/WordsOptions.cs @@ -0,0 +1,9 @@ +namespace Words.Web.Core +{ + public class WordsOptions + { + public const string Words = "Words"; + + public string ConnectionString { get; set; } = null!; + } +} diff --git a/Words.Web.Core/appsettings.json b/Words.Web.Core/appsettings.json index 81ff877..f02cec3 100644 --- a/Words.Web.Core/appsettings.json +++ b/Words.Web.Core/appsettings.json @@ -1,4 +1,7 @@ { + "Words": { + "ConnectionString": "Host=localhost;Username=prisma;Password=prisma;Database=words" + }, "Logging": { "LogLevel": { "Default": "Information", From f3774732ab8346db9a924ff82f07a014ca371ac1 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Wed, 8 Sep 2021 16:57:55 +0200 Subject: [PATCH 14/21] fix mvc, then logging (sql) --- Words.Web.Core/Startup.cs | 29 +++++++++++++++++++++++++++-- Words.Web.Core/WordFinders.cs | 35 +++++++++++++++++++++++++++++++++++ Words/WordFinder.cs | 19 +++++++++++++++++-- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/Words.Web.Core/Startup.cs b/Words.Web.Core/Startup.cs index 73df231..05c5530 100644 --- a/Words.Web.Core/Startup.cs +++ b/Words.Web.Core/Startup.cs @@ -1,24 +1,35 @@ namespace Words.Web.Core { using System.Data; + using System.Text; + using System.Text.Json; + using Dapper; using Npgsql; public class Startup { - public Startup(IConfiguration configuration) + public Startup(IConfiguration configuration, IWebHostEnvironment env) { Configuration = configuration; + WebHostEnvironment = env; } public IConfiguration Configuration { get; } + public IWebHostEnvironment WebHostEnvironment {get;set;} + // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { WordsOptions wordsOptions = new(); Configuration.GetSection(WordsOptions.Words).Bind(wordsOptions); services.AddControllersWithViews(); - services.AddScoped(sp => new NpgsqlConnection(wordsOptions.ConnectionString)); + services.AddScoped(sp => { + NpgsqlConnection connection = new(wordsOptions.ConnectionString); + connection.Open(); + return connection; + }); + services.AddSingleton(LoadWordFinders(Path.Combine(WebHostEnvironment.ContentRootPath, "App_Data", "words.json"))); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -52,5 +63,19 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) "DataDirectory", Path.Combine(env.ContentRootPath, "App_Data")); } + + private static WordFinders LoadWordFinders(string filename) + { + string path = Path.Combine("DataDirectory", filename); + string succinctTreeDataJson = File.ReadAllText(path, Encoding.UTF8); + Bucket[]? buckets = JsonSerializer.Deserialize(succinctTreeDataJson); + if (buckets is null) + { + throw new InvalidOperationException("deserialization failed"); + } + + WordFinders wordFinders = new(buckets); + return wordFinders; + } } } diff --git a/Words.Web.Core/WordFinders.cs b/Words.Web.Core/WordFinders.cs index 1689785..fc03854 100644 --- a/Words.Web.Core/WordFinders.cs +++ b/Words.Web.Core/WordFinders.cs @@ -1,12 +1,47 @@ namespace Words.Web { using System.Collections.Generic; + using Dapper; + using Npgsql; public class WordFinders { + private readonly IDictionary wordFinders; + + public WordFinders(Bucket[] buckets) + { + wordFinders = + buckets.ToDictionary( + x => x.Number, + x => + WordFinder.CreateSuccinct( + x.Data, + Language.Swedish, + y => Array.Empty(), + y => Array.Empty())); + } + public List Matches(string text, SearchType searchType, int limit) { return new List(); } + + private static string[] GetOriginal(NpgsqlConnection connection, string[] normalized) + { + IEnumerable query = + connection.Query( + "select original from normalized where normalized = any(@normalized)", + new { normalized }); + return query.ToArray(); + } + + private static string[] GetPermutations(NpgsqlConnection connection, string normalized) + { + IEnumerable query = + connection.Query( + "select permutation from permutation where normalized = @normalized", + new { normalized }); + return query.ToArray(); + } } } diff --git a/Words/WordFinder.cs b/Words/WordFinder.cs index d73ea1f..5aa0761 100644 --- a/Words/WordFinder.cs +++ b/Words/WordFinder.cs @@ -115,7 +115,13 @@ public static WordFinder CreateSuccinct( return wordFinder; } - public List Matches(string input, int d, SearchType searchType = SearchType.All, int limit = 100) + public List Matches( + string input, + int d, + Func getOriginal, + Func getPermutations, + SearchType searchType = SearchType.All, + int limit = 100) { if (input == null) { @@ -123,7 +129,14 @@ public List Matches(string input, int d, SearchType searchType = SearchTy } List matches = new(); - Matches(input, matches.Add, d, searchType, limit); + Matches( + input, + matches.Add, + d, + getOriginal, + getPermutations, + searchType, + limit); return matches; } @@ -131,6 +144,8 @@ private void Matches( string input, Action action, int d, + Func getOriginal, + Func getPermutations, SearchType searchType, int limit) { From b6d201c7d1a6a42628d0c4bba6390ed878b039ae Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Wed, 8 Sep 2021 21:58:46 +0200 Subject: [PATCH 15/21] fix --- Words.Console/Program.cs | 6 ++++- Words.Web/Global.asax.cs | 51 +++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/Words.Console/Program.cs b/Words.Console/Program.cs index e9f0a64..b40b34a 100644 --- a/Words.Console/Program.cs +++ b/Words.Console/Program.cs @@ -134,7 +134,11 @@ private static void Run(string connectionString, string wordsFilename) foreach (WordFinder wordFinder in new[] { ternary, succinct }.Where(x => x != null)) { stopwatch.Restart(); - List matches = wordFinder.Matches(input, 2); + List matches = wordFinder.Matches( + input, + 2, + x => x, + x => new[] { x }); stopwatch.Stop(); if (matches.Count > 0) { diff --git a/Words.Web/Global.asax.cs b/Words.Web/Global.asax.cs index eac48db..f982b22 100644 --- a/Words.Web/Global.asax.cs +++ b/Words.Web/Global.asax.cs @@ -27,13 +27,20 @@ public class MvcApplication : HttpApplication { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private static string connectionString = null!; private static IDictionary? wordFinders; public static List Matches(string text, SearchType searchType, int limit) { int bucket = Bucket.ToBucket(text.Length); return wordFinders!.TryGetValue(bucket, out WordFinder? wordFinder) - ? wordFinder.Matches(text, 0, searchType, limit) + ? wordFinder.Matches( + text, + 0, + x => GetOriginal(connectionString, x), + x => GetPermutations(connectionString, x), + searchType, + limit) : throw new Exception($"no word finder found for word: {text}"); } @@ -124,7 +131,7 @@ protected void Application_Start() string appDataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); string filename = Path.Combine(appDataDirectory, "words.json"); - string connectionString = ConfigurationManager.ConnectionStrings["Words"].ConnectionString; + connectionString = ConfigurationManager.ConnectionStrings["Words"].ConnectionString; wordFinders = LoadWordFinders(filename, connectionString); Log.Info("Dictionary loaded"); NpgsqlLogManager.Provider = new SqlLoggingProvider(); @@ -186,27 +193,27 @@ private IDictionary LoadWordFinders(string filename, string con y => GetPermutations(connectionString, y), y => GetOriginal(connectionString, y))); return wordFinders; + } - static string[] GetOriginal(string connectionString, string[] normalized) - { - using IDbConnection connection = new NpgsqlConnection(connectionString); - connection.Open(); - IEnumerable query = - connection.Query( - "select original from normalized where normalized = any(@normalized)", - new { normalized }); - return query.ToArray(); - } + private static string[] GetOriginal(string connectionString, string[] normalized) + { + using IDbConnection connection = new NpgsqlConnection(connectionString); + connection.Open(); + IEnumerable query = + connection.Query( + "select original from normalized where normalized = any(@normalized)", + new { normalized }); + return query.ToArray(); + } - static string[] GetPermutations(string connectionString, string normalized) - { - using IDbConnection connection = new NpgsqlConnection(connectionString); - IEnumerable query = - connection.Query( - "select permutation from permutation where normalized = @normalized", - new { normalized }); - return query.ToArray(); - } + private static string[] GetPermutations(string connectionString, string normalized) + { + using IDbConnection connection = new NpgsqlConnection(connectionString); + IEnumerable query = + connection.Query( + "select permutation from permutation where normalized = @normalized", + new { normalized }); + return query.ToArray(); } } -} \ No newline at end of file +} From 05a79d2fd5edb6e5ad91d43ce760446b5bbf4560 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Wed, 8 Sep 2021 22:42:55 +0200 Subject: [PATCH 16/21] logging, tag helper starter --- .gitignore | 1 + Words.Web.Core/NLog.config | 41 ++++++ Words.Web.Core/Program.cs | 37 ++++-- Words.Web.Core/Startup.cs | 8 +- Words.Web.Core/TagHelpers/EmailTagHelper.cs | 20 +++ Words.Web.Core/Views/Home/Index.cshtml | 139 +++++++++++++++++++- Words.Web.Core/Views/_ViewImports.cshtml | 4 +- Words.Web.Core/Words.Web.Core.csproj | 2 + Words.Web.Core/appsettings.json | 2 +- 9 files changed, 232 insertions(+), 22 deletions(-) create mode 100644 Words.Web.Core/NLog.config create mode 100644 Words.Web.Core/TagHelpers/EmailTagHelper.cs diff --git a/.gitignore b/.gitignore index 35a9140..610eaa3 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,4 @@ Words.Core/dist Words.Console/normalized_to_originals.csv Words.Console/word_permutations.csv Words.Console/words.json +internal-nlog-AspNetCore.txt diff --git a/Words.Web.Core/NLog.config b/Words.Web.Core/NLog.config new file mode 100644 index 0000000..ceeae96 --- /dev/null +++ b/Words.Web.Core/NLog.config @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Words.Web.Core/Program.cs b/Words.Web.Core/Program.cs index 9799389..4be6be7 100644 --- a/Words.Web.Core/Program.cs +++ b/Words.Web.Core/Program.cs @@ -1,19 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - namespace Words.Web.Core { + using Microsoft.Extensions.Logging; + using NLog; + using NLog.Web; + public class Program { public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + var logger = NLogBuilder.ConfigureNLog("NLog.config").GetCurrentClassLogger(); + try + { + logger.Debug("starting"); + CreateHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + logger.Error(ex, "unhandled exception"); + } + finally + { + logger.Debug("stopping"); + LogManager.Shutdown(); + } } public static IHostBuilder CreateHostBuilder(string[] args) => @@ -21,6 +30,12 @@ public static IHostBuilder CreateHostBuilder(string[] args) => .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); - }); + }) + .ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); + }) + .UseNLog(); } } diff --git a/Words.Web.Core/Startup.cs b/Words.Web.Core/Startup.cs index 05c5530..9335294 100644 --- a/Words.Web.Core/Startup.cs +++ b/Words.Web.Core/Startup.cs @@ -3,7 +3,6 @@ namespace Words.Web.Core using System.Data; using System.Text; using System.Text.Json; - using Dapper; using Npgsql; public class Startup @@ -21,8 +20,11 @@ public Startup(IConfiguration configuration, IWebHostEnvironment env) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - WordsOptions wordsOptions = new(); - Configuration.GetSection(WordsOptions.Words).Bind(wordsOptions); + WordsOptions wordsOptions = + Configuration + .GetSection(WordsOptions.Words) + .Get(); + services.Configure(Configuration.GetSection(WordsOptions.Words)); services.AddControllersWithViews(); services.AddScoped(sp => { NpgsqlConnection connection = new(wordsOptions.ConnectionString); diff --git a/Words.Web.Core/TagHelpers/EmailTagHelper.cs b/Words.Web.Core/TagHelpers/EmailTagHelper.cs new file mode 100644 index 0000000..aaf8312 --- /dev/null +++ b/Words.Web.Core/TagHelpers/EmailTagHelper.cs @@ -0,0 +1,20 @@ +namespace Words.Web.Core.TagHelpers +{ + using System.Threading.Tasks; + using Microsoft.AspNetCore.Razor.TagHelpers; + + public class EmailTagHelper : TagHelper + { + private const string EmailDomain = "contoso.com"; + public string MailTo { get; set; } = null!; + + public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + output.TagName = "a"; + string address = $"{MailTo}@{EmailDomain}"; + output.Attributes.SetAttribute("href", $"mailto:{address}"); + output.Content.SetContent(address); + return Task.CompletedTask; + } + } +} diff --git a/Words.Web.Core/Views/Home/Index.cshtml b/Words.Web.Core/Views/Home/Index.cshtml index 08a5a5c..960c719 100644 --- a/Words.Web.Core/Views/Home/Index.cshtml +++ b/Words.Web.Core/Views/Home/Index.cshtml @@ -1,8 +1,137 @@ -@{ - ViewData["Title"] = "Home Page"; + +@* +@model QueryViewModel +@helper Table(string header, List results) +{ +
+

@header

+
+ foreach (string item in results) + { +
+
+ + + + + + + + + + +
@item
+ + Google + + + + Wikipedia + + + + NE + + + + Synonymer + +
+
+
+ } } -
-

Welcome

-

Learn about building Web apps with ASP.NET Core.

+
+
+ @using (Html.BeginForm( + "Search", + "Home", + null, + FormMethod.Post, + new { @class = "well form-search input-append" })) + { + @Html.TextBoxFor( + m => m.Text, + new + { + @class = "input-large", + placeholder = "Ange sökterm", + autocorrect = "off", + autocapitalize = "off", + autocomplete = "off", + autofocus = "autofocus" + }) + + } +
+ +
+ @Html.Partial("_Help") +
+ +@if (Model.Results != null) +{ +
+
+

+ Sökningen tog @Model.Results.ElapsedMilliseconds.ToString("N2") ms. + @if (Model.Results.Count == 0) + { + @:Hittade 0 resultat. + } + else + { + @:Hittade @Model.Results.Count resultat: + } +

+
+
+ + if (Model.Results.Words.Any()) + { +
+ @Table("Ord", Model.Results.Words) +
+ } + + if (Model.Results.Anagrams.Any()) + { +
+ @Table("Anagram", Model.Results.Anagrams) +
+ } + + if (Model.Results.Near.Any()) + { +
+ @Table("Nära", Model.Results.Near) +
+ } +} + +@if (Model.Recent.Any()) +{ +
+

Nyligen gjorda sökningar:

+
+
+
+ + @foreach (RecentQuery item in Model.Recent) + { + + + + } +
@Html.ActionLink(item.Text, "Index", new { id = item.QueryId })
+
+
+} +*@ + +Daniel diff --git a/Words.Web.Core/Views/_ViewImports.cshtml b/Words.Web.Core/Views/_ViewImports.cshtml index bbb7b16..61a0896 100644 --- a/Words.Web.Core/Views/_ViewImports.cshtml +++ b/Words.Web.Core/Views/_ViewImports.cshtml @@ -1,3 +1,3 @@ -@* @using Words.Web.Core -@using Words.Web.Core.Models *@ +@using Words.Web.Core @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Words.Web.Core diff --git a/Words.Web.Core/Words.Web.Core.csproj b/Words.Web.Core/Words.Web.Core.csproj index 41957b7..1b1e54a 100644 --- a/Words.Web.Core/Words.Web.Core.csproj +++ b/Words.Web.Core/Words.Web.Core.csproj @@ -17,6 +17,8 @@ + + diff --git a/Words.Web.Core/appsettings.json b/Words.Web.Core/appsettings.json index f02cec3..725caa1 100644 --- a/Words.Web.Core/appsettings.json +++ b/Words.Web.Core/appsettings.json @@ -4,7 +4,7 @@ }, "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Trace", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } From e42ff2bed8676f2552e814839d32d9837c8e1e4f Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Thu, 9 Sep 2021 22:20:32 +0200 Subject: [PATCH 17/21] sql logging not working --- Words.Web.Core/NLog.config | 4 +- Words.Web.Core/Program.cs | 3 ++ Words.Web.Core/SqlLoggingProvider.cs | 58 ++++++++++++++++++++++ Words.Web.Core/TagHelpers/BoldTagHelper.cs | 16 ++++++ Words.Web.Core/Views/Home/Index.cshtml | 17 +++++-- Words.Web.Core/Views/_ViewImports.cshtml | 2 +- Words.Web/Controllers/HomeController.cs | 5 +- Words.Web/ViewModels/QueryViewModel.cs | 1 + 8 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 Words.Web.Core/SqlLoggingProvider.cs create mode 100644 Words.Web.Core/TagHelpers/BoldTagHelper.cs diff --git a/Words.Web.Core/NLog.config b/Words.Web.Core/NLog.config index ceeae96..b9dfb4d 100644 --- a/Words.Web.Core/NLog.config +++ b/Words.Web.Core/NLog.config @@ -13,11 +13,11 @@ - - diff --git a/Words.Web.Core/Program.cs b/Words.Web.Core/Program.cs index 4be6be7..bade75f 100644 --- a/Words.Web.Core/Program.cs +++ b/Words.Web.Core/Program.cs @@ -3,11 +3,14 @@ namespace Words.Web.Core using Microsoft.Extensions.Logging; using NLog; using NLog.Web; + using Npgsql.Logging; public class Program { public static void Main(string[] args) { + NpgsqlLogManager.IsParameterLoggingEnabled = true; + NpgsqlLogManager.Provider = new SqlLoggingProvider(); var logger = NLogBuilder.ConfigureNLog("NLog.config").GetCurrentClassLogger(); try { diff --git a/Words.Web.Core/SqlLoggingProvider.cs b/Words.Web.Core/SqlLoggingProvider.cs new file mode 100644 index 0000000..6746126 --- /dev/null +++ b/Words.Web.Core/SqlLoggingProvider.cs @@ -0,0 +1,58 @@ +namespace Words.Web.Core +{ + using NLog; + using Npgsql.Logging; + + internal class SqlLoggingProvider : INpgsqlLoggingProvider + { + public NpgsqlLogger CreateLogger(string name) + { + return new CustomLogger(LogManager.GetLogger(name)); + } + + private class CustomLogger : NpgsqlLogger + { + private readonly Logger logger; + + public CustomLogger(Logger logger) + { + this.logger = logger; + } + + public override bool IsEnabled(NpgsqlLogLevel level) + { + return logger.IsEnabled(ToNLogLogLevel(level)); + } + + public override void Log(NpgsqlLogLevel level, int connectorId, string msg, Exception? exception = null) + { + LogEventInfo ev = new(ToNLogLogLevel(level), "", msg); + if (exception != null) + { + ev.Exception = exception; + } + + if (connectorId != 0) + { + ev.Properties["ConnectorId"] = connectorId; + } + + logger.Log(ev); + } + + private static LogLevel ToNLogLogLevel(NpgsqlLogLevel level) + { + return level switch + { + NpgsqlLogLevel.Trace => LogLevel.Trace, + NpgsqlLogLevel.Debug => LogLevel.Debug, + NpgsqlLogLevel.Info => LogLevel.Info, + NpgsqlLogLevel.Warn => LogLevel.Warn, + NpgsqlLogLevel.Error => LogLevel.Error, + NpgsqlLogLevel.Fatal => LogLevel.Fatal, + _ => throw new ArgumentOutOfRangeException(nameof(level)), + }; + } + } + } +} diff --git a/Words.Web.Core/TagHelpers/BoldTagHelper.cs b/Words.Web.Core/TagHelpers/BoldTagHelper.cs new file mode 100644 index 0000000..d79f92a --- /dev/null +++ b/Words.Web.Core/TagHelpers/BoldTagHelper.cs @@ -0,0 +1,16 @@ +namespace Words.Web.Core +{ + using Microsoft.AspNetCore.Razor.TagHelpers; + + [HtmlTargetElement("bold")] + [HtmlTargetElement(Attributes = "bold")] + public class BoldTagHelper : TagHelper + { + public override void Process(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.RemoveAll("bold"); + output.PreContent.SetHtmlContent(""); + output.PostContent.SetHtmlContent(""); + } + } +} diff --git a/Words.Web.Core/Views/Home/Index.cshtml b/Words.Web.Core/Views/Home/Index.cshtml index 960c719..5f79311 100644 --- a/Words.Web.Core/Views/Home/Index.cshtml +++ b/Words.Web.Core/Views/Home/Index.cshtml @@ -1,6 +1,6 @@ - +@model QueryViewModel + @* -@model QueryViewModel @helper Table(string header, List results) {
@@ -40,7 +40,7 @@
} -} +}*@
@@ -49,8 +49,15 @@ "Home", null, FormMethod.Post, + null, new { @class = "well form-search input-append" })) { + @Html.TextBoxFor( m => m.Text, new @@ -69,7 +76,7 @@ }
- +@*
@Html.Partial("_Help")
@@ -134,4 +141,6 @@ } *@ +

Some additional information

+Is this bold? Daniel diff --git a/Words.Web.Core/Views/_ViewImports.cshtml b/Words.Web.Core/Views/_ViewImports.cshtml index 61a0896..8730a1e 100644 --- a/Words.Web.Core/Views/_ViewImports.cshtml +++ b/Words.Web.Core/Views/_ViewImports.cshtml @@ -1,3 +1,3 @@ -@using Words.Web.Core +@using Words.Web.ViewModels @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Words.Web.Core diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index 0fc6e72..8c5fac4 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -94,7 +94,10 @@ public async Task Index(int? id, CancellationToken cancellationTok return View(new QueryViewModel { Recent = recentQueries }); } - [HttpPost] +#if NET + [ValidateAntiForgeryToken] +#endif + [HttpPost("search")] public async Task Search(QueryViewModel q, CancellationToken cancellationToken) { if (ModelState.IsValid == false || q.Text is null) diff --git a/Words.Web/ViewModels/QueryViewModel.cs b/Words.Web/ViewModels/QueryViewModel.cs index 0bcdcde..2b78714 100644 --- a/Words.Web/ViewModels/QueryViewModel.cs +++ b/Words.Web/ViewModels/QueryViewModel.cs @@ -18,6 +18,7 @@ public QueryViewModel() [Required(ErrorMessage = "*")] [MinLength(1)] [MaxLength(255)] + [DataType(DataType.Text)] public string? Text { get; set; } public ResultsViewModel? Results { get; set; } From 15ec675b41debc5ec10f0b5d72493e9de4c5980d Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Mon, 13 Sep 2021 22:34:40 +0200 Subject: [PATCH 18/21] fix logging --- Words.Web.Core/NLog.config | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Words.Web.Core/NLog.config b/Words.Web.Core/NLog.config index b9dfb4d..4cd28da 100644 --- a/Words.Web.Core/NLog.config +++ b/Words.Web.Core/NLog.config @@ -1,9 +1,6 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" internalLogLevel="Debug" internalLogFile="internal-nlog-AspNetCore.txt"> @@ -13,15 +10,13 @@ - + - + - + From 2009c4b517438524e9d3f599b9b23e91f640ce1e Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Tue, 14 Sep 2021 21:33:00 +0200 Subject: [PATCH 19/21] fix mvc --- Words.Web/Controllers/HomeController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index 8c5fac4..9fc2b82 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -96,8 +96,10 @@ public async Task Index(int? id, CancellationToken cancellationTok #if NET [ValidateAntiForgeryToken] -#endif [HttpPost("search")] +#else + [HttpPost] +#endif public async Task Search(QueryViewModel q, CancellationToken cancellationToken) { if (ModelState.IsValid == false || q.Text is null) From 33afd2bcbff741bf783078e966e6f9a93814fd24 Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Tue, 14 Sep 2021 22:17:17 +0200 Subject: [PATCH 20/21] fix logging --- Words.Web.Core/Extensions.cs | 11 +++++++++++ Words.Web.Core/NLog.config | 18 ++++++++++++++++-- Words.Web.Core/Program.cs | 5 +++-- Words.Web.Core/Words.Web.Core.csproj | 4 ++++ Words.Web.Core/appsettings.Development.json | 6 +++--- Words.Web.Core/appsettings.json | 4 ++-- Words.Web/Controllers/HomeController.cs | 9 ++++++++- 7 files changed, 47 insertions(+), 10 deletions(-) diff --git a/Words.Web.Core/Extensions.cs b/Words.Web.Core/Extensions.cs index ffb3f42..435c406 100644 --- a/Words.Web.Core/Extensions.cs +++ b/Words.Web.Core/Extensions.cs @@ -2,12 +2,18 @@ namespace Words.Web.Core { public static class Extensions { + private static readonly Action information; private static readonly Action cacheItemRemoved; private static readonly Action queryElapsed; static Extensions() { int eventId = 1; + information = LoggerMessage.Define( + LogLevel.Information, + new EventId(eventId++, nameof(Information)), + "Informational message: {Information}"); + cacheItemRemoved = LoggerMessage.Define( LogLevel.Information, new EventId(eventId++, nameof(CacheItemRemoved)), @@ -19,6 +25,11 @@ static Extensions() "Query '{Text}',{MilliSeconds:F2}"); } + public static void Information(this ILogger logger, string message) + { + + } + public static void CacheItemRemoved(this ILogger logger, object key, object reason, object value) { cacheItemRemoved.Invoke(logger, key, reason, value, null); diff --git a/Words.Web.Core/NLog.config b/Words.Web.Core/NLog.config index 4cd28da..bafb503 100644 --- a/Words.Web.Core/NLog.config +++ b/Words.Web.Core/NLog.config @@ -10,10 +10,24 @@ - + - + diff --git a/Words.Web.Core/Program.cs b/Words.Web.Core/Program.cs index bade75f..9f453d2 100644 --- a/Words.Web.Core/Program.cs +++ b/Words.Web.Core/Program.cs @@ -36,8 +36,9 @@ public static IHostBuilder CreateHostBuilder(string[] args) => }) .ConfigureLogging(logging => { - logging.ClearProviders(); - logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); + logging + .ClearProviders() + .SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); }) .UseNLog(); } diff --git a/Words.Web.Core/Words.Web.Core.csproj b/Words.Web.Core/Words.Web.Core.csproj index 1b1e54a..bf2d7d6 100644 --- a/Words.Web.Core/Words.Web.Core.csproj +++ b/Words.Web.Core/Words.Web.Core.csproj @@ -22,4 +22,8 @@
+ + + + diff --git a/Words.Web.Core/appsettings.Development.json b/Words.Web.Core/appsettings.Development.json index dba68eb..a2eef67 100644 --- a/Words.Web.Core/appsettings.Development.json +++ b/Words.Web.Core/appsettings.Development.json @@ -1,9 +1,9 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Default": "Trace", + "Microsoft": "Trace", + "Microsoft.Hosting.Lifetime": "Trace" } } } diff --git a/Words.Web.Core/appsettings.json b/Words.Web.Core/appsettings.json index 725caa1..2eb72d4 100644 --- a/Words.Web.Core/appsettings.json +++ b/Words.Web.Core/appsettings.json @@ -5,8 +5,8 @@ "Logging": { "LogLevel": { "Default": "Trace", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Microsoft": "Trace", + "Microsoft.Hosting.Lifetime": "Trace" } }, "AllowedHosts": "*" diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index 9fc2b82..df7de79 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -53,11 +53,13 @@ public HomeController( : base(memoryCache, connection, logger, wordFinders) { this.logger = logger; + logger.Information("here i am"); } #endif public async Task Index(int? id, CancellationToken cancellationToken) { + logger.Information("index daniel"); if (id != null) { string text = await Transact(async (conn, tran) => @@ -89,6 +91,8 @@ public async Task Index(int? id, CancellationToken cancellationTok return View(model); } + logger.Information("get recent queries"); + // get recent queries RecentQuery[] recentQueries = await GetRecentQueries(cancellationToken); return View(new QueryViewModel { Recent = recentQueries }); @@ -102,13 +106,16 @@ public async Task Index(int? id, CancellationToken cancellationTok #endif public async Task Search(QueryViewModel q, CancellationToken cancellationToken) { + logger.Information("begin search"); if (ModelState.IsValid == false || q.Text is null) { - return View(q); + logger.Information("view index"); + return View("Index", q); } if (CacheGet($"query-{Encoding.UTF8.GetBytes(q.Text).ComputeHash()}") is QueryId cachedQueryId) { + logger.Information("cache found"); return RedirectToAction(nameof(Index), new { id = cachedQueryId.Id }); } From 94deda6504ad0b038bdaf5bc40648e4405f6773b Mon Sep 17 00:00:00 2001 From: Daniel Lidstrom Date: Tue, 5 Oct 2021 11:12:00 +0200 Subject: [PATCH 21/21] logging works --- Words.Web.Core/Extensions.cs | 11 +++++++++++ Words.Web.Core/NLog.config | 11 +++++++---- Words.Web.Core/SqlLoggingProvider.cs | 9 ++++++--- Words.Web.Core/Words.Web.Core.csproj | 1 + Words.Web.Core/appsettings.Development.json | 6 +++--- Words.Web.Core/appsettings.json | 7 ++++--- Words.Web/Controllers/HomeController.cs | 3 ++- 7 files changed, 34 insertions(+), 14 deletions(-) diff --git a/Words.Web.Core/Extensions.cs b/Words.Web.Core/Extensions.cs index 435c406..015fe5e 100644 --- a/Words.Web.Core/Extensions.cs +++ b/Words.Web.Core/Extensions.cs @@ -3,6 +3,7 @@ namespace Words.Web.Core public static class Extensions { private static readonly Action information; + private static readonly Action error; private static readonly Action cacheItemRemoved; private static readonly Action queryElapsed; @@ -14,6 +15,11 @@ static Extensions() new EventId(eventId++, nameof(Information)), "Informational message: {Information}"); + error = LoggerMessage.Define( + LogLevel.Error, + new EventId(eventId++, nameof(Error)), + "Error message: {Error}"); + cacheItemRemoved = LoggerMessage.Define( LogLevel.Information, new EventId(eventId++, nameof(CacheItemRemoved)), @@ -27,7 +33,12 @@ static Extensions() public static void Information(this ILogger logger, string message) { + information.Invoke(logger, message, null); + } + public static void Error(this ILogger logger, string message) + { + error.Invoke(logger, message, null); } public static void CacheItemRemoved(this ILogger logger, object key, object reason, object value) diff --git a/Words.Web.Core/NLog.config b/Words.Web.Core/NLog.config index bafb503..c0260ec 100644 --- a/Words.Web.Core/NLog.config +++ b/Words.Web.Core/NLog.config @@ -27,19 +27,22 @@ archiveFileName="${basedir}log/web.{#}.log" archiveNumbering="Rolling" filename="${basedir}log/web.log" - layout="date='${longdate}' eventId=${event-properties:item=EventId_Id:whenEmpty=0} level=${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}| body: ${aspnet-request-posted-body}" /> + layout="${longdate};${event-properties:item=EventId_Id:whenEmpty=0};${uppercase:${level}};${logger};${message};${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}| body: ${aspnet-request-posted-body}" /> - + - + - + diff --git a/Words.Web.Core/SqlLoggingProvider.cs b/Words.Web.Core/SqlLoggingProvider.cs index 6746126..7f1b5b5 100644 --- a/Words.Web.Core/SqlLoggingProvider.cs +++ b/Words.Web.Core/SqlLoggingProvider.cs @@ -3,14 +3,14 @@ namespace Words.Web.Core using NLog; using Npgsql.Logging; - internal class SqlLoggingProvider : INpgsqlLoggingProvider + public class SqlLoggingProvider : INpgsqlLoggingProvider { public NpgsqlLogger CreateLogger(string name) { return new CustomLogger(LogManager.GetLogger(name)); } - private class CustomLogger : NpgsqlLogger + public class CustomLogger : NpgsqlLogger { private readonly Logger logger; @@ -26,7 +26,10 @@ public override bool IsEnabled(NpgsqlLogLevel level) public override void Log(NpgsqlLogLevel level, int connectorId, string msg, Exception? exception = null) { - LogEventInfo ev = new(ToNLogLogLevel(level), "", msg); + LogEventInfo ev = new( + ToNLogLogLevel(level), + logger.Name, + msg); if (exception != null) { ev.Exception = exception; diff --git a/Words.Web.Core/Words.Web.Core.csproj b/Words.Web.Core/Words.Web.Core.csproj index bf2d7d6..87885d2 100644 --- a/Words.Web.Core/Words.Web.Core.csproj +++ b/Words.Web.Core/Words.Web.Core.csproj @@ -25,5 +25,6 @@ + diff --git a/Words.Web.Core/appsettings.Development.json b/Words.Web.Core/appsettings.Development.json index a2eef67..8983e0f 100644 --- a/Words.Web.Core/appsettings.Development.json +++ b/Words.Web.Core/appsettings.Development.json @@ -1,9 +1,9 @@ { "Logging": { "LogLevel": { - "Default": "Trace", - "Microsoft": "Trace", - "Microsoft.Hosting.Lifetime": "Trace" + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" } } } diff --git a/Words.Web.Core/appsettings.json b/Words.Web.Core/appsettings.json index 2eb72d4..dc2be70 100644 --- a/Words.Web.Core/appsettings.json +++ b/Words.Web.Core/appsettings.json @@ -4,9 +4,10 @@ }, "Logging": { "LogLevel": { - "Default": "Trace", - "Microsoft": "Trace", - "Microsoft.Hosting.Lifetime": "Trace" + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "Npgsql": "Trace" } }, "AllowedHosts": "*" diff --git a/Words.Web/Controllers/HomeController.cs b/Words.Web/Controllers/HomeController.cs index df7de79..6e3178c 100644 --- a/Words.Web/Controllers/HomeController.cs +++ b/Words.Web/Controllers/HomeController.cs @@ -53,7 +53,7 @@ public HomeController( : base(memoryCache, connection, logger, wordFinders) { this.logger = logger; - logger.Information("here i am"); + logger.Error("here i am"); } #endif @@ -95,6 +95,7 @@ public async Task Index(int? id, CancellationToken cancellationTok // get recent queries RecentQuery[] recentQueries = await GetRecentQueries(cancellationToken); + logger.Information(string.Join(", ", recentQueries.Select(x => x.Text))); return View(new QueryViewModel { Recent = recentQueries }); }