From e358e61f606ef60de9419275de5d14b7aeb91e26 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 22 Sep 2018 19:49:53 +0200 Subject: [PATCH 1/9] changed to named tuples in articlesearch --- .../Articles/Search/Impl/ArticlesDbSearchEngine.cs | 13 +++++-------- .../Interfaces/IArticleRepository.cs | 6 +++--- CoreWiki.Data/Repositories/ArticleRepository.cs | 12 ++++-------- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/CoreWiki.Application/Articles/Search/Impl/ArticlesDbSearchEngine.cs b/CoreWiki.Application/Articles/Search/Impl/ArticlesDbSearchEngine.cs index 55de6163..a3f0d397 100644 --- a/CoreWiki.Application/Articles/Search/Impl/ArticlesDbSearchEngine.cs +++ b/CoreWiki.Application/Articles/Search/Impl/ArticlesDbSearchEngine.cs @@ -1,10 +1,9 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; +using AutoMapper; using CoreWiki.Application.Articles.Search.Dto; using CoreWiki.Core.Domain; using CoreWiki.Data.Abstractions.Interfaces; +using System.Linq; +using System.Threading.Tasks; namespace CoreWiki.Application.Articles.Search.Impl { @@ -25,9 +24,7 @@ public async Task> SearchAsync(string query, i var filteredQuery = query.Trim(); var offset = (pageNumber - 1) * resultsPerPage; - (IEnumerable
articles, int totalFound) searchQueryResult = - _articleRepo.GetArticlesForSearchQuery(filteredQuery, offset, resultsPerPage); - var articles = searchQueryResult.articles; + var (articles, totalFound) = _articleRepo.GetArticlesForSearchQuery(filteredQuery, offset, resultsPerPage); var result = new SearchResult
{ @@ -35,7 +32,7 @@ public async Task> SearchAsync(string query, i Results = articles.ToList(), CurrentPage = pageNumber, ResultsPerPage = resultsPerPage, - TotalResults = searchQueryResult.totalFound + TotalResults = totalFound }; return _mapper.Map>(result); diff --git a/CoreWiki.Data.Abstractions/Interfaces/IArticleRepository.cs b/CoreWiki.Data.Abstractions/Interfaces/IArticleRepository.cs index c1cddc33..b1e2fbee 100644 --- a/CoreWiki.Data.Abstractions/Interfaces/IArticleRepository.cs +++ b/CoreWiki.Data.Abstractions/Interfaces/IArticleRepository.cs @@ -1,7 +1,7 @@ -using System; +using CoreWiki.Core.Domain; +using System; using System.Collections.Generic; using System.Threading.Tasks; -using CoreWiki.Core.Domain; namespace CoreWiki.Data.Abstractions.Interfaces { @@ -18,7 +18,7 @@ public interface IArticleRepository : IDisposable Task
CreateArticleAndHistory(Article article); - (IEnumerable
, int) GetArticlesForSearchQuery(string filteredQuery, int offset, int resultsPerPage); + (IEnumerable
articles, int totalFound) GetArticlesForSearchQuery(string filteredQuery, int offset, int resultsPerPage); Task IsTopicAvailable(string articleSlug, int articleId); diff --git a/CoreWiki.Data/Repositories/ArticleRepository.cs b/CoreWiki.Data/Repositories/ArticleRepository.cs index 86ce25cc..36aae226 100644 --- a/CoreWiki.Data/Repositories/ArticleRepository.cs +++ b/CoreWiki.Data/Repositories/ArticleRepository.cs @@ -1,11 +1,10 @@ using CoreWiki.Core.Domain; +using CoreWiki.Data.Abstractions.Interfaces; using CoreWiki.Data.EntityFramework.Models; using Microsoft.EntityFrameworkCore; -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using CoreWiki.Data.Abstractions.Interfaces; namespace CoreWiki.Data.EntityFramework.Repositories { @@ -72,17 +71,15 @@ public async Task IsTopicAvailable(string articleSlug, int articleId) .AnyAsync(a => a.Slug == articleSlug && a.Id != articleId); } - - public (IEnumerable
, int) GetArticlesForSearchQuery(string filteredQuery, int offset, int resultsPerPage) + public (IEnumerable
articles, int totalFound) GetArticlesForSearchQuery(string filteredQuery, int offset, int resultsPerPage) { - // WARNING: This may need to be further refactored to allow for database optimized search queries var articles = Context.Articles .AsNoTracking() .Where(a => - a.Topic.ToUpper().Contains(filteredQuery.ToUpper()) || - a.Content.ToUpper().Contains(filteredQuery.ToUpper()) + a.Topic.ToUpper().Contains(filteredQuery.ToUpper()) + || a.Content.ToUpper().Contains(filteredQuery.ToUpper()) ).Select(a => a.ToDomain()); var articleCount = articles.Count(); var list = articles.Skip(offset).Take(resultsPerPage).OrderByDescending(a => a.ViewCount).ToList(); @@ -90,7 +87,6 @@ public async Task IsTopicAvailable(string articleSlug, int articleId) return (list, articleCount); } - public void Dispose() { Context.Dispose(); From b3b4c936d56fc9f4528ad362ad384e95d08bea8d Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 23 Sep 2018 13:13:33 +0200 Subject: [PATCH 2/9] Added AzureSearch v0.5 --- .../Articles/Search/IArticlesSearchEngine.cs | 5 +- .../Articles/Search/ISearchProvider.cs | 12 +++ .../Search/Impl/ArticlesDbSearchEngine.cs | 41 ++++++--- .../ArticlesAzureSearcSearchEngine.cs | 85 +++++++++++++++++++ .../Impl/AzureSearch/AzureSearchClient.cs | 55 ++++++++++++ .../Impl/AzureSearch/IAzureSearchClient.cs | 21 +++++ .../LocalDbArticleSearchProviderAdapter.cs | 47 ++++++++++ .../Search/Queries/SearchArticlesHandler.cs | 9 +- .../Articles/Search/SearchProviderSettings.cs | 9 ++ .../Articles/Search/SetupSearchprovider.cs | 28 ++++++ .../CoreWiki.Application.csproj | 2 + .../Configuration/Settings/AppSettings.cs | 2 - CoreWiki/Startup.cs | 7 +- CoreWiki/appsettings.json | 23 +++-- 14 files changed, 313 insertions(+), 33 deletions(-) create mode 100644 CoreWiki.Application/Articles/Search/ISearchProvider.cs create mode 100644 CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs create mode 100644 CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs create mode 100644 CoreWiki.Application/Articles/Search/Impl/AzureSearch/IAzureSearchClient.cs create mode 100644 CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs create mode 100644 CoreWiki.Application/Articles/Search/SearchProviderSettings.cs create mode 100644 CoreWiki.Application/Articles/Search/SetupSearchprovider.cs diff --git a/CoreWiki.Application/Articles/Search/IArticlesSearchEngine.cs b/CoreWiki.Application/Articles/Search/IArticlesSearchEngine.cs index ee0091b3..593ed051 100644 --- a/CoreWiki.Application/Articles/Search/IArticlesSearchEngine.cs +++ b/CoreWiki.Application/Articles/Search/IArticlesSearchEngine.cs @@ -1,6 +1,5 @@ -using System.Threading.Tasks; -using CoreWiki.Application.Articles.Search.Dto; -using CoreWiki.Core.Domain; +using CoreWiki.Application.Articles.Search.Dto; +using System.Threading.Tasks; namespace CoreWiki.Application.Articles.Search { diff --git a/CoreWiki.Application/Articles/Search/ISearchProvider.cs b/CoreWiki.Application/Articles/Search/ISearchProvider.cs new file mode 100644 index 00000000..f17680bc --- /dev/null +++ b/CoreWiki.Application/Articles/Search/ISearchProvider.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace CoreWiki.Application.Articles.Search +{ + public interface ISearchProvider where T : class + { + Task<(IEnumerable results, long total)> SearchAsync(string Query, int pageNumber, int resultsPerPage); + + Task IndexElementsAsync(bool clearIndex = false, params T[] items); + } +} diff --git a/CoreWiki.Application/Articles/Search/Impl/ArticlesDbSearchEngine.cs b/CoreWiki.Application/Articles/Search/Impl/ArticlesDbSearchEngine.cs index a3f0d397..4b37f776 100644 --- a/CoreWiki.Application/Articles/Search/Impl/ArticlesDbSearchEngine.cs +++ b/CoreWiki.Application/Articles/Search/Impl/ArticlesDbSearchEngine.cs @@ -1,7 +1,7 @@ using AutoMapper; using CoreWiki.Application.Articles.Search.Dto; using CoreWiki.Core.Domain; -using CoreWiki.Data.Abstractions.Interfaces; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -9,33 +9,50 @@ namespace CoreWiki.Application.Articles.Search.Impl { public class ArticlesDbSearchEngine : IArticlesSearchEngine { - - private readonly IArticleRepository _articleRepo; + private readonly ISearchProvider
_searchProvider; private readonly IMapper _mapper; - public ArticlesDbSearchEngine(IArticleRepository articleRepo, IMapper mapper) + public ArticlesDbSearchEngine(ISearchProvider
searchProvider, IMapper mapper) { - _articleRepo = articleRepo; + _searchProvider = searchProvider; _mapper = mapper; } public async Task> SearchAsync(string query, int pageNumber, int resultsPerPage) { var filteredQuery = query.Trim(); - var offset = (pageNumber - 1) * resultsPerPage; + var (articles, totalFound) = await _searchProvider.SearchAsync(filteredQuery, pageNumber, resultsPerPage).ConfigureAwait(false); + + // TODO maybe make this searchproviders problem + var total = int.TryParse(totalFound.ToString(), out var inttotal); + if (!total) + { + inttotal = int.MaxValue; + } - var (articles, totalFound) = _articleRepo.GetArticlesForSearchQuery(filteredQuery, offset, resultsPerPage); + return _mapper.CreateArticleResultDTO(filteredQuery, articles, pageNumber, resultsPerPage, inttotal); + } + } + internal static class SearchResultFactory + { + internal static SearchResultDto CreateArticleResultDTO(this IMapper mapper, string query, IEnumerable
articles, int currenPage, int resultsPerPage, int totalResults) + { + var results = new List
(); + if (articles?.Any() == true) + { + results = articles.ToList(); + } var result = new SearchResult
{ - Query = filteredQuery, - Results = articles.ToList(), - CurrentPage = pageNumber, + Query = query, + Results = results, + CurrentPage = currenPage, ResultsPerPage = resultsPerPage, - TotalResults = totalFound + TotalResults = totalResults }; - return _mapper.Map>(result); + return mapper.Map>(result); } } } diff --git a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs new file mode 100644 index 00000000..aad28b41 --- /dev/null +++ b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Azure.Search; +using Microsoft.Azure.Search.Models; +using Microsoft.Extensions.Logging; + +namespace CoreWiki.Application.Articles.Search.AzureSearch +{ + /// + /// Tutorial here: https://github.com/Azure-Samples/search-dotnet-getting-started/blob/master/DotNetHowTo/DotNetHowTo/Program.cs + /// + /// + public class AzureSearchProvider : ISearchProvider where T : class + { + private readonly ILogger _logger; + private readonly IAzureSearchClient _searchClient; + private readonly ISearchIndexClient _myclient; + + public AzureSearchProvider(ILogger> logger, IAzureSearchClient searchClient) + { + _logger = logger; + _searchClient = searchClient; + _myclient = _searchClient.GetSearchClient(); + } + + public async Task IndexElementsAsync(bool clearIndex = false, params T[] items) + { + if (clearIndex) + { + DeleteCurrentItems(); + } + + var action = items.Select(IndexAction.MergeOrUpload); + var job = new IndexBatch(action); + + try + { + var res = await _searchClient.CreateServiceClient().Documents.IndexAsync(job).ConfigureAwait(false); + return res.Results.Count; + } + catch (IndexBatchException e) + { + // Sometimes when your Search service is under load, indexing will fail for some of the documents in + // the batch. Depending on your application, you can take compensating actions like delaying and + // retrying. For this simple demo, we just log the failed document keys and continue. + + var failedElements = e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key); + _logger.LogError(e, "Failed to index some of the documents", failedElements); + return items.Length - failedElements.Count(); + } + } + + private void DeleteCurrentItems() + { + // TODO + } + + public async Task<(IEnumerable results, long total)> SearchAsync(string Query, int pageNumber, int resultsPerPage) + { + var offset = (pageNumber - 1) * resultsPerPage; + var parameters = new SearchParameters() + { + IncludeTotalResultCount = true, + Top = resultsPerPage, + Skip = offset, + }; + try + { + var res = await _myclient.Documents.SearchAsync(Query, parameters).ConfigureAwait(false); + + var total = res.Count.GetValueOrDefault(); + var list = res.Results; + //TODO: map results + + return (results: null, total: total); + } + catch (System.Exception e) + { + _logger.LogCritical(e, $"{nameof(SearchAsync)} Search failed horribly, you should check it out"); + return (results: null, total: 0); + } + } + } +} diff --git a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs new file mode 100644 index 00000000..0f0b449d --- /dev/null +++ b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs @@ -0,0 +1,55 @@ +using Microsoft.Azure.Search; +using Microsoft.Azure.Search.Models; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace CoreWiki.Application.Articles.Search.AzureSearch +{ + internal class AzureSearchClient : IAzureSearchClient + { + private readonly IOptionsSnapshot _config; + private readonly IConfiguration _configuration; + + private string searchServiceName => _config.Value.Az_ApiGateway; //_configuration["SearchProvider:Az_ApiGateway"]; + private string adminApiKey => _config.Value.Az_ReadApiKey; //_configuration["SearchProvider:Az_ReadApiKey"]; + private string queryApiKey => _config.Value.Az_WriteApiKey; //_configuration["SearchProvider:Az_WriteApiKey"]; + + public AzureSearchClient(IOptionsSnapshot config, IConfiguration configuration) + { + _config = config; + _configuration = configuration; + } + + public ISearchIndexClient CreateServiceClient() + { + var index = typeof(T).FullName; + var serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey)); + return GetOrCreateIndex(serviceClient); + } + + private ISearchIndexClient GetOrCreateIndex(SearchServiceClient serviceClient) + { + var index = typeof(T).FullName; + if (serviceClient.Indexes.Exists(index)) + { + return serviceClient.Indexes.GetClient(index); + } + + var definition = new Index() + { + Name = index, + Fields = FieldBuilder.BuildForType() + }; + + serviceClient.Indexes.Create(definition); + + return serviceClient.Indexes.GetClient(index); + } + + public ISearchIndexClient GetSearchClient() + { + var indexClient = new SearchIndexClient(searchServiceName, typeof(T).FullName, new SearchCredentials(queryApiKey)); + return indexClient; + } + } +} diff --git a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/IAzureSearchClient.cs b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/IAzureSearchClient.cs new file mode 100644 index 00000000..ef617028 --- /dev/null +++ b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/IAzureSearchClient.cs @@ -0,0 +1,21 @@ +using Microsoft.Azure.Search; + +namespace CoreWiki.Application.Articles.Search.AzureSearch +{ + public interface IAzureSearchClient + { + /// + /// This client can be used for search + /// + /// + /// + ISearchIndexClient GetSearchClient(); + + /// + /// This client can be used to Index elements + /// + /// + /// + ISearchIndexClient CreateServiceClient(); + } +} diff --git a/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs b/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs new file mode 100644 index 00000000..3329a910 --- /dev/null +++ b/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using CoreWiki.Core.Domain; +using CoreWiki.Data.Abstractions.Interfaces; +using Microsoft.Extensions.Logging; + +namespace CoreWiki.Application.Articles.Search.Impl +{ + /// + /// When using local DB convert Generic search to Concrete Articlesearch + /// + /// + public class LocalDbArticleSearchProviderAdapter : ISearchProvider where T : Article + { + private readonly ILogger _logger; + private readonly IArticleRepository _articleRepo; + + public LocalDbArticleSearchProviderAdapter(ILogger> logger, IArticleRepository articleRepo) + { + _logger = logger; + _articleRepo = articleRepo; + } + + public async Task IndexElementsAsync(bool clearIndex = false, params T[] items) + { + // For LocalDB DB itself is responsible for "Indexing" + return await Task.Run(() => 0); + } + + public async Task<(IEnumerable results, long total)> SearchAsync(string Query, int pageNumber, int resultsPerPage) + { + var offset = (pageNumber - 1) * resultsPerPage; + var (articles, totalFound) = _articleRepo.GetArticlesForSearchQuery(Query, offset, resultsPerPage); + + var supportedType = articles.GetType().GetGenericArguments()[0]; + if (typeof(T) == supportedType) + { + var tlist = articles.Cast(); + return (results: tlist, total: totalFound); + } + + _logger.LogWarning($"{nameof(SearchAsync)}: Only supports search for {nameof(supportedType)} but asked for {typeof(T).FullName}"); + return (Enumerable.Empty(), 0); + } + } +} diff --git a/CoreWiki.Application/Articles/Search/Queries/SearchArticlesHandler.cs b/CoreWiki.Application/Articles/Search/Queries/SearchArticlesHandler.cs index 6b9ea376..0e294eb8 100644 --- a/CoreWiki.Application/Articles/Search/Queries/SearchArticlesHandler.cs +++ b/CoreWiki.Application/Articles/Search/Queries/SearchArticlesHandler.cs @@ -1,11 +1,11 @@ -using System.Threading; -using System.Threading.Tasks; -using CoreWiki.Application.Articles.Search.Dto; +using CoreWiki.Application.Articles.Search.Dto; using MediatR; +using System.Threading; +using System.Threading.Tasks; namespace CoreWiki.Application.Articles.Search.Queries { - class SearchArticlesHandler: IRequestHandler> + internal class SearchArticlesHandler : IRequestHandler> { private readonly IArticlesSearchEngine _articlesSearchEngine; @@ -13,6 +13,7 @@ public SearchArticlesHandler(IArticlesSearchEngine articlesSearchEngine) { _articlesSearchEngine = articlesSearchEngine; } + public Task> Handle(SearchArticlesQuery request, CancellationToken cancellationToken) { return _articlesSearchEngine.SearchAsync(request.Query, request.PageNumber, request.ResultsPerPage); diff --git a/CoreWiki.Application/Articles/Search/SearchProviderSettings.cs b/CoreWiki.Application/Articles/Search/SearchProviderSettings.cs new file mode 100644 index 00000000..4c53b7f1 --- /dev/null +++ b/CoreWiki.Application/Articles/Search/SearchProviderSettings.cs @@ -0,0 +1,9 @@ +namespace CoreWiki.Application.Articles.Search +{ + public class SearchProviderSettings + { + public string Az_ApiGateway { get; set; } + public string Az_ReadApiKey { get; set; } + public string Az_WriteApiKey { get; set; } + } +} diff --git a/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs b/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs new file mode 100644 index 00000000..6e2b2d9d --- /dev/null +++ b/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs @@ -0,0 +1,28 @@ +using CoreWiki.Application.Articles.Search.AzureSearch; +using CoreWiki.Application.Articles.Search.Impl; +using CoreWiki.Core.Domain; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace CoreWiki.Application.Articles.Search +{ + public static class SetupSearchprovider + { + public static IServiceCollection ConfigureSearchProvider(this IServiceCollection services, IConfiguration configuration) + { + switch (configuration["SearchProvider"]) + { + case "Az": + services.AddTransient(typeof(ISearchProvider<>), typeof(AzureSearchProvider<>)); + services.AddTransient(); + break; + default: + services.AddTransient, LocalDbArticleSearchProviderAdapter
>(); + break; + } + + // db repos + return services; + } + } +} diff --git a/CoreWiki.Application/CoreWiki.Application.csproj b/CoreWiki.Application/CoreWiki.Application.csproj index 0d588674..01f270a0 100644 --- a/CoreWiki.Application/CoreWiki.Application.csproj +++ b/CoreWiki.Application/CoreWiki.Application.csproj @@ -11,11 +11,13 @@ + + diff --git a/CoreWiki/Configuration/Settings/AppSettings.cs b/CoreWiki/Configuration/Settings/AppSettings.cs index a300a54b..90aaaf63 100644 --- a/CoreWiki/Configuration/Settings/AppSettings.cs +++ b/CoreWiki/Configuration/Settings/AppSettings.cs @@ -5,12 +5,10 @@ namespace CoreWiki.Configuration.Settings { public class AppSettings { - public Uri Url { get; set; } public Connectionstrings ConnectionStrings { get; set; } public Comments Comments { get; set; } public EmailNotifications EmailNotifications { get; set; } public CspSettings CspSettings { get; set; } - } } diff --git a/CoreWiki/Startup.cs b/CoreWiki/Startup.cs index ea9bd4cc..1fcc33fd 100644 --- a/CoreWiki/Startup.cs +++ b/CoreWiki/Startup.cs @@ -1,3 +1,4 @@ +using CoreWiki.Application.Articles.Search; using CoreWiki.Configuration.Settings; using CoreWiki.Configuration.Startup; using CoreWiki.Data.EntityFramework.Security; @@ -7,13 +8,11 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using System.Threading.Tasks; namespace CoreWiki { public class Startup { - public Startup(IConfiguration configuration) { Configuration = configuration; @@ -24,7 +23,6 @@ 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) { - services.ConfigureAutomapper(); services.ConfigureRSSFeed(); @@ -32,6 +30,8 @@ public void ConfigureServices(IServiceCollection services) services.ConfigureSecurityAndAuthentication(); services.ConfigureDatabase(Configuration); services.ConfigureScopedServices(Configuration); + services.Configure(Configuration.GetSection(nameof(SearchProviderSettings))); + services.ConfigureSearchProvider(Configuration); services.ConfigureRouting(); services.ConfigureLocalisation(); services.ConfigureApplicationServices(); @@ -54,6 +54,5 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions app.UseStatusCodePagesWithReExecute("/HttpErrors/{0}"); app.UseMvc(); } - } } diff --git a/CoreWiki/appsettings.json b/CoreWiki/appsettings.json index 38ccfedf..d8762435 100644 --- a/CoreWiki/appsettings.json +++ b/CoreWiki/appsettings.json @@ -6,13 +6,20 @@ "Default": "Warning" } }, - "DataProvider": "", + "DataProvider": "", "ConnectionStrings": { //"CoreWikiIdentityContextConnection": "host=localhost;port=5432;user id=postgres;password=password", //"CoreWikiData": "host=localhost;port=5432;user id=postgres;password=password" "CoreWikiIdentityContextConnection": "DataSource=./App_Data/wikiIdentity.db", "CoreWikiData": "DataSource=./App_Data/wikiContent.db" }, + "SearchProvider": "Azd", + "SearchProviderSettings": { + "Az_ApiGateway": "https://mycorewikisearchtest.search.windows.net", // free plan, 50Mb of storage + "Az_ReadApiKey": "D91D336DFABB0A9C4393B2371D0E288C", + "Az_WriteApiKey": "NotCommited" + }, + "Authentication": { "Microsoft": { "ApplicationId": "", @@ -32,12 +39,12 @@ "FromName": "No Reply Team" }, "CspSettings": { - "ImageSources": [ ], - "StyleSources": [ ], - "ScriptSources": [ ], - "FontSources": [ ], - "FormActions": [ ], - "FrameAncestors": [ ], - "ReportUris": [ ] + "ImageSources": [], + "StyleSources": [], + "ScriptSources": [], + "FontSources": [], + "FormActions": [], + "FrameAncestors": [], + "ReportUris": [] } } From 24bd77a7e61bbf0cf87bc23e15dd9f4bfc4d961c Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 23 Sep 2018 14:47:40 +0200 Subject: [PATCH 3/9] Added RepositoryProxy for updating searchindex --- .../ArticleRepositorySearchIndexingProxy.cs | 87 +++++++++++++++++++ .../Articles/Search/ISearchProvider.cs | 2 +- .../ArticlesAzureSearcSearchEngine.cs | 15 +--- .../LocalDbArticleSearchProviderAdapter.cs | 4 +- CoreWiki.Data/StartupExtensions.cs | 20 +++-- .../Startup/ConfigureScopedServices.cs | 21 ++++- 6 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 CoreWiki.Application/Articles/Search/ArticleRepositorySearchIndexingProxy.cs diff --git a/CoreWiki.Application/Articles/Search/ArticleRepositorySearchIndexingProxy.cs b/CoreWiki.Application/Articles/Search/ArticleRepositorySearchIndexingProxy.cs new file mode 100644 index 00000000..2879d2be --- /dev/null +++ b/CoreWiki.Application/Articles/Search/ArticleRepositorySearchIndexingProxy.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using CoreWiki.Core.Domain; +using CoreWiki.Data.Abstractions.Interfaces; + +namespace CoreWiki.Application.Articles.Search +{ + public class ArticleRepositorySearchIndexingProxy : IArticleRepository + { + private readonly ISearchProvider
_searchProvider; + private readonly IArticleRepository _repository; + + public ArticleRepositorySearchIndexingProxy(ISearchProvider
searchProvider, Func repository) + { + _searchProvider = searchProvider; + _repository = repository(1); + } + + public Task
CreateArticleAndHistory(Article article) + { + _searchProvider.IndexElementsAsync(article); + return _repository.CreateArticleAndHistory(article); + } + + public Task
Delete(string slug) + { + return _repository.Delete(slug); + } + + public void Dispose() + { + _repository.Dispose(); + } + + // GetFrom Search + public Task Exists(int id) + { + return _repository.Exists(id); + } + + public Task
GetArticleById(int articleId) + { + return _repository.GetArticleById(articleId); + } + + public Task
GetArticleBySlug(string articleSlug) + { + return _repository.GetArticleBySlug(articleSlug); + } + + // TODO: Search should not be a part of repository + public (IEnumerable
articles, int totalFound) GetArticlesForSearchQuery(string filteredQuery, int offset, int resultsPerPage) + { + return _repository.GetArticlesForSearchQuery(filteredQuery, offset, resultsPerPage); + } + + public Task
GetArticleWithHistoriesBySlug(string articleSlug) + { + return _repository.GetArticleWithHistoriesBySlug(articleSlug); + } + + // TODO: get from search + public Task> GetLatestArticles(int numOfArticlesToGet) + { + return _repository.GetLatestArticles(numOfArticlesToGet); + } + + //TODO: update search? + public Task IncrementViewCount(string slug) + { + return _repository.IncrementViewCount(slug); + } + + //TODO: Topic from Search + public Task IsTopicAvailable(string articleSlug, int articleId) + { + return _repository.IsTopicAvailable(articleSlug, articleId); + } + + public Task Update(Article article) + { + _searchProvider.IndexElementsAsync(article); + return _repository.Update(article); + } + } +} diff --git a/CoreWiki.Application/Articles/Search/ISearchProvider.cs b/CoreWiki.Application/Articles/Search/ISearchProvider.cs index f17680bc..8562445e 100644 --- a/CoreWiki.Application/Articles/Search/ISearchProvider.cs +++ b/CoreWiki.Application/Articles/Search/ISearchProvider.cs @@ -7,6 +7,6 @@ public interface ISearchProvider where T : class { Task<(IEnumerable results, long total)> SearchAsync(string Query, int pageNumber, int resultsPerPage); - Task IndexElementsAsync(bool clearIndex = false, params T[] items); + Task IndexElementsAsync(params T[] items); } } diff --git a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs index aad28b41..5662b474 100644 --- a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs +++ b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs @@ -24,19 +24,15 @@ public AzureSearchProvider(ILogger> logger, IAzureSearchC _myclient = _searchClient.GetSearchClient(); } - public async Task IndexElementsAsync(bool clearIndex = false, params T[] items) + public async Task IndexElementsAsync(params T[] items) { - if (clearIndex) - { - DeleteCurrentItems(); - } - var action = items.Select(IndexAction.MergeOrUpload); var job = new IndexBatch(action); try { - var res = await _searchClient.CreateServiceClient().Documents.IndexAsync(job).ConfigureAwait(false); + var myclient = _searchClient.CreateServiceClient(); + var res = await _myclient.Documents.IndexAsync(job).ConfigureAwait(false); return res.Results.Count; } catch (IndexBatchException e) @@ -51,11 +47,6 @@ public async Task IndexElementsAsync(bool clearIndex = false, params T[] it } } - private void DeleteCurrentItems() - { - // TODO - } - public async Task<(IEnumerable results, long total)> SearchAsync(string Query, int pageNumber, int resultsPerPage) { var offset = (pageNumber - 1) * resultsPerPage; diff --git a/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs b/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs index 3329a910..f776a0aa 100644 --- a/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs +++ b/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs @@ -22,10 +22,10 @@ public LocalDbArticleSearchProviderAdapter(ILogger IndexElementsAsync(bool clearIndex = false, params T[] items) + public Task IndexElementsAsync(params T[] items) { // For LocalDB DB itself is responsible for "Indexing" - return await Task.Run(() => 0); + return Task.Run(() => items.Length); } public async Task<(IEnumerable results, long total)> SearchAsync(string Query, int pageNumber, int resultsPerPage) diff --git a/CoreWiki.Data/StartupExtensions.cs b/CoreWiki.Data/StartupExtensions.cs index faf5b4dc..c440aa78 100644 --- a/CoreWiki.Data/StartupExtensions.cs +++ b/CoreWiki.Data/StartupExtensions.cs @@ -1,9 +1,9 @@ -using CoreWiki.Data.EntityFramework.Repositories; +using System; +using CoreWiki.Data.Abstractions.Interfaces; +using CoreWiki.Data.EntityFramework.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using CoreWiki.Data.Abstractions.Interfaces; -using System; namespace CoreWiki.Data.EntityFramework { @@ -17,12 +17,14 @@ public static class StartupExtensions /// /// /// - public static IServiceCollection AddRepositories(this IServiceCollection services, IConfiguration config) { + public static IServiceCollection AddRepositories(this IServiceCollection services, IConfiguration config) + { Action optionsBuilder; var connectionString = config.GetConnectionString("CoreWikiData"); - switch (config["DataProvider"].ToLowerInvariant()) { + switch (config["DataProvider"].ToLowerInvariant()) + { case "postgres": services.AddEntityFrameworkNpgsql(); optionsBuilder = options => options.UseNpgsql(connectionString); @@ -34,20 +36,22 @@ public static IServiceCollection AddRepositories(this IServiceCollection service break; } - services.AddDbContextPool(options => { + services.AddDbContextPool(options => + { optionsBuilder(options); options.EnableSensitiveDataLogging(); }); // db repos - services.AddTransient(); + //services.AddTransient(); services.AddTransient(); services.AddTransient(); return services; } - public static IServiceScope SeedData(this IServiceScope serviceScope) { + public static IServiceScope SeedData(this IServiceScope serviceScope) + { var context = serviceScope.ServiceProvider.GetService(); diff --git a/CoreWiki/Configuration/Startup/ConfigureScopedServices.cs b/CoreWiki/Configuration/Startup/ConfigureScopedServices.cs index d363fc5c..03fec88b 100644 --- a/CoreWiki/Configuration/Startup/ConfigureScopedServices.cs +++ b/CoreWiki/Configuration/Startup/ConfigureScopedServices.cs @@ -1,5 +1,8 @@ -using CoreWiki.Application.Articles.Search; +using System; +using CoreWiki.Application.Articles.Search; using CoreWiki.Application.Articles.Search.Impl; +using CoreWiki.Data.Abstractions.Interfaces; +using CoreWiki.Data.EntityFramework.Repositories; using CoreWiki.Notifications; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; @@ -21,8 +24,22 @@ public static IServiceCollection ConfigureScopedServices(this IServiceCollection services.AddEmailNotifications(configuration); services.AddScoped(); - services.AddProgressiveWebApp(new PwaOptions { EnableCspNonce = true }); + services.AddTransient(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped>(serviceProvider => key => + { + //TODO: enum + switch (key) + { + case 1: + return serviceProvider.GetService(); + default: + return serviceProvider.GetService(); + } + }); + services.AddProgressiveWebApp(new PwaOptions { EnableCspNonce = true }); return services; } } From 9c1f85e4a83424e0559ed3e273213a3722e377ca Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 23 Sep 2018 14:48:30 +0200 Subject: [PATCH 4/9] Found Azure Search bug? --- .../Impl/AzureSearch/AzureSearchClient.cs | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs index 0f0b449d..0474c8a4 100644 --- a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs +++ b/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs @@ -10,9 +10,13 @@ internal class AzureSearchClient : IAzureSearchClient private readonly IOptionsSnapshot _config; private readonly IConfiguration _configuration; - private string searchServiceName => _config.Value.Az_ApiGateway; //_configuration["SearchProvider:Az_ApiGateway"]; - private string adminApiKey => _config.Value.Az_ReadApiKey; //_configuration["SearchProvider:Az_ReadApiKey"]; - private string queryApiKey => _config.Value.Az_WriteApiKey; //_configuration["SearchProvider:Az_WriteApiKey"]; + private string SearchServiceName => _config.Value.Az_ApiGateway; + private string AdminApiKey => _config.Value.Az_WriteApiKey; + private string QueryApiKey => _config.Value.Az_ReadApiKey; + private string GetIndexName() + { + return typeof(T).Name.ToLowerInvariant(); + } public AzureSearchClient(IOptionsSnapshot config, IConfiguration configuration) { @@ -23,33 +27,44 @@ public AzureSearchClient(IOptionsSnapshot config, IConfi public ISearchIndexClient CreateServiceClient() { var index = typeof(T).FullName; - var serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey)); + var serviceClient = new SearchServiceClient(SearchServiceName, new SearchCredentials(AdminApiKey)); return GetOrCreateIndex(serviceClient); } private ISearchIndexClient GetOrCreateIndex(SearchServiceClient serviceClient) { - var index = typeof(T).FullName; + //indexname must be lowercase + var index = GetIndexName(); if (serviceClient.Indexes.Exists(index)) { return serviceClient.Indexes.GetClient(index); } - var definition = new Index() - { - Name = index, - Fields = FieldBuilder.BuildForType() - }; - serviceClient.Indexes.Create(definition); + try + { + var definition = new Index() + { + Name = index, + // NodaTimecrash + Fields = FieldBuilder.BuildForType() + }; + var createdindex = serviceClient.Indexes.Create(definition); + } + catch (System.Exception e) + { + throw; + } return serviceClient.Indexes.GetClient(index); } public ISearchIndexClient GetSearchClient() { - var indexClient = new SearchIndexClient(searchServiceName, typeof(T).FullName, new SearchCredentials(queryApiKey)); + var indexClient = new SearchIndexClient(SearchServiceName, GetIndexName(), new SearchCredentials(QueryApiKey)); return indexClient; } + + } } From 19147711814f6eb4ee290e22cd6caf7a79d28f72 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 23 Sep 2018 16:03:09 +0200 Subject: [PATCH 5/9] correct gateway setting --- CoreWiki/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CoreWiki/appsettings.json b/CoreWiki/appsettings.json index d8762435..2e513df1 100644 --- a/CoreWiki/appsettings.json +++ b/CoreWiki/appsettings.json @@ -15,7 +15,7 @@ }, "SearchProvider": "Azd", "SearchProviderSettings": { - "Az_ApiGateway": "https://mycorewikisearchtest.search.windows.net", // free plan, 50Mb of storage + "Az_ApiGateway": "mycorewikisearchtest", // free plan, 50Mb of storage "Az_ReadApiKey": "D91D336DFABB0A9C4393B2371D0E288C", "Az_WriteApiKey": "NotCommited" }, From b1931af91736564a381b6da3d08fcf1d371e2345 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 23 Sep 2018 16:32:28 +0200 Subject: [PATCH 6/9] Fixed crash when using localDbSearch --- .../Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs | 7 ++++--- .../Articles/Search/SetupSearchprovider.cs | 5 +---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs b/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs index f776a0aa..5b381882 100644 --- a/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs +++ b/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CoreWiki.Core.Domain; @@ -16,10 +17,10 @@ public class LocalDbArticleSearchProviderAdapter : ISearchProvider where T private readonly ILogger _logger; private readonly IArticleRepository _articleRepo; - public LocalDbArticleSearchProviderAdapter(ILogger> logger, IArticleRepository articleRepo) + public LocalDbArticleSearchProviderAdapter(ILogger> logger, Func articleRepo) { _logger = logger; - _articleRepo = articleRepo; + _articleRepo = articleRepo(1); } public Task IndexElementsAsync(params T[] items) diff --git a/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs b/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs index 6e2b2d9d..dc3ad84e 100644 --- a/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs +++ b/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs @@ -1,6 +1,5 @@ using CoreWiki.Application.Articles.Search.AzureSearch; using CoreWiki.Application.Articles.Search.Impl; -using CoreWiki.Core.Domain; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -17,11 +16,9 @@ public static IServiceCollection ConfigureSearchProvider(this IServiceCollection services.AddTransient(); break; default: - services.AddTransient, LocalDbArticleSearchProviderAdapter
>(); + services.AddTransient(typeof(ISearchProvider<>), typeof(LocalDbArticleSearchProviderAdapter<>)); break; } - - // db repos return services; } } From 977199041ae0ea10bbe6fed9401ab037d25f6e03 Mon Sep 17 00:00:00 2001 From: "Jeffrey T. Fritz" Date: Sat, 6 Oct 2018 13:54:34 -0400 Subject: [PATCH 7/9] Added CoreWiki.Azure project --- .../Areas/MyFeature/Pages/Page1.cshtml | 16 ++++++++++++++++ .../Areas/MyFeature/Pages/Page1.cshtml.cs | 17 +++++++++++++++++ CoreWiki.Azure/CoreWiki.Azure.csproj | 10 ++++++++++ CoreWiki.sln | 6 ++++++ CoreWiki/CoreWiki.csproj | 1 + 5 files changed, 50 insertions(+) create mode 100644 CoreWiki.Azure/Areas/MyFeature/Pages/Page1.cshtml create mode 100644 CoreWiki.Azure/Areas/MyFeature/Pages/Page1.cshtml.cs create mode 100644 CoreWiki.Azure/CoreWiki.Azure.csproj diff --git a/CoreWiki.Azure/Areas/MyFeature/Pages/Page1.cshtml b/CoreWiki.Azure/Areas/MyFeature/Pages/Page1.cshtml new file mode 100644 index 00000000..d86d51fb --- /dev/null +++ b/CoreWiki.Azure/Areas/MyFeature/Pages/Page1.cshtml @@ -0,0 +1,16 @@ +@page +@model CoreWiki.Azure.MyFeature.Pages.Page1Model +@{ + Layout = null; +} + + + + + + + Page1 + + + + diff --git a/CoreWiki.Azure/Areas/MyFeature/Pages/Page1.cshtml.cs b/CoreWiki.Azure/Areas/MyFeature/Pages/Page1.cshtml.cs new file mode 100644 index 00000000..918e4398 --- /dev/null +++ b/CoreWiki.Azure/Areas/MyFeature/Pages/Page1.cshtml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace CoreWiki.Azure.MyFeature.Pages +{ + public class Page1Model : PageModel + { + public void OnGet() + { + + } + } +} \ No newline at end of file diff --git a/CoreWiki.Azure/CoreWiki.Azure.csproj b/CoreWiki.Azure/CoreWiki.Azure.csproj new file mode 100644 index 00000000..2aa0d35b --- /dev/null +++ b/CoreWiki.Azure/CoreWiki.Azure.csproj @@ -0,0 +1,10 @@ + + + + netstandard2.0 + + + + + + diff --git a/CoreWiki.sln b/CoreWiki.sln index 6828309d..5d0512a0 100644 --- a/CoreWiki.sln +++ b/CoreWiki.sln @@ -47,6 +47,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pipelines", "pipelines", "{ pipelines\azure-pipelines-cake.yml = pipelines\azure-pipelines-cake.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreWiki.Azure", "CoreWiki.Azure\CoreWiki.Azure.csproj", "{6DF86E8A-DC29-4621-ACFD-849A26E0F003}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -85,6 +87,10 @@ Global {192CBE23-68A7-4437-A2E1-B88FC3DE152B}.Debug|Any CPU.Build.0 = Debug|Any CPU {192CBE23-68A7-4437-A2E1-B88FC3DE152B}.Release|Any CPU.ActiveCfg = Release|Any CPU {192CBE23-68A7-4437-A2E1-B88FC3DE152B}.Release|Any CPU.Build.0 = Release|Any CPU + {6DF86E8A-DC29-4621-ACFD-849A26E0F003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DF86E8A-DC29-4621-ACFD-849A26E0F003}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DF86E8A-DC29-4621-ACFD-849A26E0F003}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DF86E8A-DC29-4621-ACFD-849A26E0F003}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CoreWiki/CoreWiki.csproj b/CoreWiki/CoreWiki.csproj index caa14869..ee12bf39 100644 --- a/CoreWiki/CoreWiki.csproj +++ b/CoreWiki/CoreWiki.csproj @@ -33,6 +33,7 @@ + From 0ca3e342a403e290c4067bd5f1d6c1e0d43fec44 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 7 Oct 2018 17:15:55 +0200 Subject: [PATCH 8/9] Move AzureSearch into AzureProject --- .../ArticlesAzureSearcSearchEngine.cs | 3 ++- .../Areas}/AzureSearch/AzureSearchClient.cs | 11 ++++---- .../Areas}/AzureSearch/IAzureSearchClient.cs | 2 +- CoreWiki.Azure/CoreWiki.Azure.csproj | 12 ++++++--- .../Startup/ConfigureSearchProvider.cs | 26 +++++++++++++++++++ 5 files changed, 42 insertions(+), 12 deletions(-) rename {CoreWiki.Application/Articles/Search/Impl => CoreWiki.Azure/Areas}/AzureSearch/ArticlesAzureSearcSearchEngine.cs (96%) rename {CoreWiki.Application/Articles/Search/Impl => CoreWiki.Azure/Areas}/AzureSearch/AzureSearchClient.cs (90%) rename {CoreWiki.Application/Articles/Search/Impl => CoreWiki.Azure/Areas}/AzureSearch/IAzureSearchClient.cs (88%) create mode 100644 CoreWiki/Configuration/Startup/ConfigureSearchProvider.cs diff --git a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs b/CoreWiki.Azure/Areas/AzureSearch/ArticlesAzureSearcSearchEngine.cs similarity index 96% rename from CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs rename to CoreWiki.Azure/Areas/AzureSearch/ArticlesAzureSearcSearchEngine.cs index 5662b474..307fce66 100644 --- a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/ArticlesAzureSearcSearchEngine.cs +++ b/CoreWiki.Azure/Areas/AzureSearch/ArticlesAzureSearcSearchEngine.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using CoreWiki.Application.Articles.Search; using Microsoft.Azure.Search; using Microsoft.Azure.Search.Models; using Microsoft.Extensions.Logging; -namespace CoreWiki.Application.Articles.Search.AzureSearch +namespace CoreWiki.Azure.Areas.AzureSearch { /// /// Tutorial here: https://github.com/Azure-Samples/search-dotnet-getting-started/blob/master/DotNetHowTo/DotNetHowTo/Program.cs diff --git a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs b/CoreWiki.Azure/Areas/AzureSearch/AzureSearchClient.cs similarity index 90% rename from CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs rename to CoreWiki.Azure/Areas/AzureSearch/AzureSearchClient.cs index 0474c8a4..33f99bb1 100644 --- a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/AzureSearchClient.cs +++ b/CoreWiki.Azure/Areas/AzureSearch/AzureSearchClient.cs @@ -1,11 +1,12 @@ -using Microsoft.Azure.Search; +using CoreWiki.Application.Articles.Search; +using Microsoft.Azure.Search; using Microsoft.Azure.Search.Models; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; -namespace CoreWiki.Application.Articles.Search.AzureSearch +namespace CoreWiki.Azure.Areas.AzureSearch { - internal class AzureSearchClient : IAzureSearchClient + public class AzureSearchClient : IAzureSearchClient { private readonly IOptionsSnapshot _config; private readonly IConfiguration _configuration; @@ -13,6 +14,7 @@ internal class AzureSearchClient : IAzureSearchClient private string SearchServiceName => _config.Value.Az_ApiGateway; private string AdminApiKey => _config.Value.Az_WriteApiKey; private string QueryApiKey => _config.Value.Az_ReadApiKey; + private string GetIndexName() { return typeof(T).Name.ToLowerInvariant(); @@ -40,7 +42,6 @@ private ISearchIndexClient GetOrCreateIndex(SearchServiceClient serviceClient return serviceClient.Indexes.GetClient(index); } - try { var definition = new Index() @@ -64,7 +65,5 @@ public ISearchIndexClient GetSearchClient() var indexClient = new SearchIndexClient(SearchServiceName, GetIndexName(), new SearchCredentials(QueryApiKey)); return indexClient; } - - } } diff --git a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/IAzureSearchClient.cs b/CoreWiki.Azure/Areas/AzureSearch/IAzureSearchClient.cs similarity index 88% rename from CoreWiki.Application/Articles/Search/Impl/AzureSearch/IAzureSearchClient.cs rename to CoreWiki.Azure/Areas/AzureSearch/IAzureSearchClient.cs index ef617028..c96fb2b3 100644 --- a/CoreWiki.Application/Articles/Search/Impl/AzureSearch/IAzureSearchClient.cs +++ b/CoreWiki.Azure/Areas/AzureSearch/IAzureSearchClient.cs @@ -1,6 +1,6 @@ using Microsoft.Azure.Search; -namespace CoreWiki.Application.Articles.Search.AzureSearch +namespace CoreWiki.Azure.Areas.AzureSearch { public interface IAzureSearchClient { diff --git a/CoreWiki.Azure/CoreWiki.Azure.csproj b/CoreWiki.Azure/CoreWiki.Azure.csproj index 2aa0d35b..60226c50 100644 --- a/CoreWiki.Azure/CoreWiki.Azure.csproj +++ b/CoreWiki.Azure/CoreWiki.Azure.csproj @@ -1,10 +1,14 @@ - + - - netstandard2.0 - + + netcoreapp2.1 + + + + + diff --git a/CoreWiki/Configuration/Startup/ConfigureSearchProvider.cs b/CoreWiki/Configuration/Startup/ConfigureSearchProvider.cs new file mode 100644 index 00000000..4183f2b1 --- /dev/null +++ b/CoreWiki/Configuration/Startup/ConfigureSearchProvider.cs @@ -0,0 +1,26 @@ +using CoreWiki.Application.Articles.Search; +using CoreWiki.Application.Articles.Search.Impl; +using CoreWiki.Azure.Areas.AzureSearch; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace CoreWiki.Configuration.Startup +{ + public static partial class ConfigurationExtensions + { + public static IServiceCollection ConfigureSearchProvider(this IServiceCollection services, IConfiguration configuration) + { + switch (configuration["SearchProvider"]) + { + case "Az": + services.AddTransient(typeof(ISearchProvider<>), typeof(AzureSearchProvider<>)); + services.AddTransient(); + break; + default: + services.AddTransient(typeof(ISearchProvider<>), typeof(LocalDbArticleSearchProviderAdapter<>)); + break; + } + return services; + } + } +} From 3ba1c44ac0039869e4835ef2a45135d386400c13 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 7 Oct 2018 18:07:10 +0200 Subject: [PATCH 9/9] trying to decorate DI for ArticleProxy --- .../ArticleRepositorySearchIndexingProxy.cs | 5 ++++ .../LocalDbArticleSearchProviderAdapter.cs | 2 +- .../Articles/Search/SetupSearchprovider.cs | 25 ------------------- .../Startup/ConfigureScopedServices.cs | 23 +---------------- .../Startup/ConfigureSearchProvider.cs | 23 ++++++++++++++++- CoreWiki/CoreWiki.csproj | 1 + 6 files changed, 30 insertions(+), 49 deletions(-) delete mode 100644 CoreWiki.Application/Articles/Search/SetupSearchprovider.cs diff --git a/CoreWiki.Application/Articles/Search/ArticleRepositorySearchIndexingProxy.cs b/CoreWiki.Application/Articles/Search/ArticleRepositorySearchIndexingProxy.cs index 2879d2be..b18b66f5 100644 --- a/CoreWiki.Application/Articles/Search/ArticleRepositorySearchIndexingProxy.cs +++ b/CoreWiki.Application/Articles/Search/ArticleRepositorySearchIndexingProxy.cs @@ -6,6 +6,11 @@ namespace CoreWiki.Application.Articles.Search { + /// + /// Proxy pattern: Using a middleman to extend functionality of original class but not changing it. + /// Ex caching, when you dont want/need to muddle the original class with logic for another resposibility, + /// Or here when we want to extend our app with a searchengine, wich needs indexing. The repository dont need to know we do indexing somewere else + /// public class ArticleRepositorySearchIndexingProxy : IArticleRepository { private readonly ISearchProvider
_searchProvider; diff --git a/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs b/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs index 5b381882..31e67fcb 100644 --- a/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs +++ b/CoreWiki.Application/Articles/Search/Impl/LocalDb/LocalDbArticleSearchProviderAdapter.cs @@ -9,7 +9,7 @@ namespace CoreWiki.Application.Articles.Search.Impl { /// - /// When using local DB convert Generic search to Concrete Articlesearch + /// Adapter pattern: When using local DB, convert Concrete Articlesearch to Generic ISearchProvider /// /// public class LocalDbArticleSearchProviderAdapter : ISearchProvider where T : Article diff --git a/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs b/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs deleted file mode 100644 index dc3ad84e..00000000 --- a/CoreWiki.Application/Articles/Search/SetupSearchprovider.cs +++ /dev/null @@ -1,25 +0,0 @@ -using CoreWiki.Application.Articles.Search.AzureSearch; -using CoreWiki.Application.Articles.Search.Impl; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace CoreWiki.Application.Articles.Search -{ - public static class SetupSearchprovider - { - public static IServiceCollection ConfigureSearchProvider(this IServiceCollection services, IConfiguration configuration) - { - switch (configuration["SearchProvider"]) - { - case "Az": - services.AddTransient(typeof(ISearchProvider<>), typeof(AzureSearchProvider<>)); - services.AddTransient(); - break; - default: - services.AddTransient(typeof(ISearchProvider<>), typeof(LocalDbArticleSearchProviderAdapter<>)); - break; - } - return services; - } - } -} diff --git a/CoreWiki/Configuration/Startup/ConfigureScopedServices.cs b/CoreWiki/Configuration/Startup/ConfigureScopedServices.cs index 03fec88b..eda46693 100644 --- a/CoreWiki/Configuration/Startup/ConfigureScopedServices.cs +++ b/CoreWiki/Configuration/Startup/ConfigureScopedServices.cs @@ -1,9 +1,4 @@ -using System; -using CoreWiki.Application.Articles.Search; -using CoreWiki.Application.Articles.Search.Impl; -using CoreWiki.Data.Abstractions.Interfaces; -using CoreWiki.Data.EntityFramework.Repositories; -using CoreWiki.Notifications; +using CoreWiki.Notifications; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -22,22 +17,6 @@ public static IServiceCollection ConfigureScopedServices(this IServiceCollection services.AddSingleton(); services.AddEmailNotifications(configuration); - services.AddScoped(); - - services.AddTransient(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped>(serviceProvider => key => - { - //TODO: enum - switch (key) - { - case 1: - return serviceProvider.GetService(); - default: - return serviceProvider.GetService(); - } - }); services.AddProgressiveWebApp(new PwaOptions { EnableCspNonce = true }); return services; diff --git a/CoreWiki/Configuration/Startup/ConfigureSearchProvider.cs b/CoreWiki/Configuration/Startup/ConfigureSearchProvider.cs index 4183f2b1..017821a8 100644 --- a/CoreWiki/Configuration/Startup/ConfigureSearchProvider.cs +++ b/CoreWiki/Configuration/Startup/ConfigureSearchProvider.cs @@ -1,6 +1,9 @@ -using CoreWiki.Application.Articles.Search; +using System; +using CoreWiki.Application.Articles.Search; using CoreWiki.Application.Articles.Search.Impl; using CoreWiki.Azure.Areas.AzureSearch; +using CoreWiki.Data.Abstractions.Interfaces; +using CoreWiki.Data.EntityFramework.Repositories; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -10,6 +13,24 @@ public static partial class ConfigurationExtensions { public static IServiceCollection ConfigureSearchProvider(this IServiceCollection services, IConfiguration configuration) { + services.AddScoped(); + + //Testing scrutor (https://github.com/khellang/Scrutor): getting overflowexception + //services.AddScoped(); + //services.Decorate(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped>(serviceProvider => key => + { + //TODO: enum or other DI that supports decorator + switch (key) + { + default: + return serviceProvider.GetService(); + } + }); + switch (configuration["SearchProvider"]) { case "Az": diff --git a/CoreWiki/CoreWiki.csproj b/CoreWiki/CoreWiki.csproj index ee12bf39..ea4f8255 100644 --- a/CoreWiki/CoreWiki.csproj +++ b/CoreWiki/CoreWiki.csproj @@ -21,6 +21,7 @@ +