diff --git a/devdocs/todo.txt b/devdocs/todo.txt index 4ddaefca..44f6e4e4 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -8,7 +8,7 @@ TODO: Ensure scaleability by checking for performance issues now before replicat - As per this document https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1 - For scaleability go back to async only for any db calls functions like creating widgets etc -TODO: look for any line of code that does this or similar: await Task.CompletedTask; +TODO: look for any line of code that does this or similar: await Task.CompletedTask, [anything].Result; - Needs to be turned into a true async function or not an async function TODO: Look at any of my middleware code as it's a HOT PATH, make sure it's async db access etc and nothing slowing it down diff --git a/server/AyaNova/Controllers/AttachmentController.cs b/server/AyaNova/Controllers/AttachmentController.cs index 923f8ed9..1cf1765f 100644 --- a/server/AyaNova/Controllers/AttachmentController.cs +++ b/server/AyaNova/Controllers/AttachmentController.cs @@ -187,7 +187,7 @@ namespace AyaNova.Api.Controllers if (!badRequest) { //check if object exists - if (!BizObjectExistsInDatabase.Exists(attachToObject)) + if (!await BizObjectExistsInDatabase.ExistsAsync(attachToObject)) { badRequest = true; errorMessage = "Invalid attach object"; @@ -230,13 +230,13 @@ namespace AyaNova.Api.Controllers }); //EVENT LOG - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, attachToObject.ObjectId, attachToObject.ObjectType, AyaEvent.AttachmentCreate, v.DisplayFileName), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, attachToObject.ObjectId, attachToObject.ObjectType, AyaEvent.AttachmentCreate, v.DisplayFileName), ct); //SEARCH INDEXING // Search.ProcessNewObjectKeywords( UserLocaleIdFromContext.Id(HttpContext.Items), v.Id, AyaType.FileAttachment, v.DisplayFileName, v.DisplayFileName, v.Notes, v.StoredFileName); var SearchParams = new Search.SearchIndexProcessObjectParameters(UserLocaleIdFromContext.Id(HttpContext.Items), v.Id, AyaType.FileAttachment, v.DisplayFileName); SearchParams.AddText(v.Notes).AddText(v.DisplayFileName).AddText(v.StoredFileName); - Search.ProcessNewObjectKeywordsAsync(SearchParams); + await Search.ProcessNewObjectKeywordsAsync(SearchParams); } } @@ -303,10 +303,10 @@ namespace AyaNova.Api.Controllers FileUtil.deleteFileAttachment(dbObj, ct); //Event log process delete - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.AttachToObjectId, dbObj.AttachToObjectType, AyaEvent.AttachmentDelete, dbObj.DisplayFileName), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.AttachToObjectId, dbObj.AttachToObjectType, AyaEvent.AttachmentDelete, dbObj.DisplayFileName), ct); //Delete search index - Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, AyaType.FileAttachment); + await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, AyaType.FileAttachment); return NoContent(); } @@ -385,7 +385,7 @@ namespace AyaNova.Api.Controllers } //Log - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.AttachToObjectId, dbObj.AttachToObjectType, AyaEvent.AttachmentDownload, dbObj.DisplayFileName), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.AttachToObjectId, dbObj.AttachToObjectType, AyaEvent.AttachmentDownload, dbObj.DisplayFileName), ct); return PhysicalFile(filePath, mimetype, dbObj.DisplayFileName); diff --git a/server/AyaNova/Controllers/DataListFilterController.cs b/server/AyaNova/Controllers/DataListFilterController.cs index 19b9b5c1..8e7aab49 100644 --- a/server/AyaNova/Controllers/DataListFilterController.cs +++ b/server/AyaNova/Controllers/DataListFilterController.cs @@ -123,7 +123,7 @@ namespace AyaNova.Api.Controllers try { - if (!biz.Put(o, inObj)) + if (!await biz.PutAsync(o, inObj)) return BadRequest(new ApiErrorResponse(biz.Errors)); } catch (DbUpdateConcurrencyException) @@ -194,7 +194,7 @@ namespace AyaNova.Api.Controllers if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); - if (!biz.Delete(o)) + if (!await biz.DeleteAsync(o)) return BadRequest(new ApiErrorResponse(biz.Errors)); return NoContent(); diff --git a/server/AyaNova/Controllers/DataListTemplateController.cs b/server/AyaNova/Controllers/DataListTemplateController.cs index e20e9f48..7cd27416 100644 --- a/server/AyaNova/Controllers/DataListTemplateController.cs +++ b/server/AyaNova/Controllers/DataListTemplateController.cs @@ -122,7 +122,7 @@ namespace AyaNova.Api.Controllers try { - if (!biz.Put(o, inObj, DataList)) + if (!biz.PutAsync(o, inObj, DataList)) return BadRequest(new ApiErrorResponse(biz.Errors)); } catch (DbUpdateConcurrencyException) @@ -168,7 +168,7 @@ namespace AyaNova.Api.Controllers if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); - if (!biz.Delete(o)) + if (!biz.DeleteAsync(o)) return BadRequest(new ApiErrorResponse(biz.Errors)); return NoContent(); diff --git a/server/AyaNova/Controllers/FormCustomController.cs b/server/AyaNova/Controllers/FormCustomController.cs index f88f2f9b..a53998be 100644 --- a/server/AyaNova/Controllers/FormCustomController.cs +++ b/server/AyaNova/Controllers/FormCustomController.cs @@ -165,7 +165,7 @@ namespace AyaNova.Api.Controllers try { - if (!biz.Put(o, inObj)) + if (!biz.PutAsync(o, inObj)) return BadRequest(new ApiErrorResponse(biz.Errors)); } catch (DbUpdateConcurrencyException) diff --git a/server/AyaNova/Controllers/LocaleController.cs b/server/AyaNova/Controllers/LocaleController.cs index dc72e23f..7442ddd9 100644 --- a/server/AyaNova/Controllers/LocaleController.cs +++ b/server/AyaNova/Controllers/LocaleController.cs @@ -124,7 +124,7 @@ namespace AyaNova.Api.Controllers LocaleBiz biz = LocaleBiz.GetBiz(ct, HttpContext); //LocaleBiz biz = new LocaleBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items)); - var l = biz.LocaleKeyCoverage(); + var l = biz.LocaleKeyCoverageAsync(); return Ok(ApiOkResponse.Response(l, true)); } #endif @@ -238,7 +238,7 @@ namespace AyaNova.Api.Controllers } catch (DbUpdateConcurrencyException) { - if (!biz.LocaleItemExists(inObj.Id)) + if (!biz.LocaleItemExistsAsync(inObj.Id)) { return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); } @@ -301,7 +301,7 @@ namespace AyaNova.Api.Controllers } catch (DbUpdateConcurrencyException) { - if (!biz.LocaleExists(inObj.Id)) + if (!biz.LocaleExistsAsync(inObj.Id)) { return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); } @@ -355,7 +355,7 @@ namespace AyaNova.Api.Controllers //Instantiate the business object handler //LocaleBiz biz = new LocaleBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items)); LocaleBiz biz = LocaleBiz.GetBiz(ct, HttpContext); - if (!biz.Delete(dbObj)) + if (!biz.DeleteAsync(dbObj)) { return BadRequest(new ApiErrorResponse(biz.Errors)); } diff --git a/server/AyaNova/Startup.cs b/server/AyaNova/Startup.cs index 436a25f6..ed7413da 100644 --- a/server/AyaNova/Startup.cs +++ b/server/AyaNova/Startup.cs @@ -439,7 +439,7 @@ namespace AyaNova //Ensure locales are present, not missing any keys and that there is a server default locale that exists LocaleBiz lb = new LocaleBiz(dbContext, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.OpsAdminFull); - lb.ValidateLocales(); + lb.ValidateLocalesAsync(); diff --git a/server/AyaNova/biz/BizObjectExistsInDatabase.cs b/server/AyaNova/biz/BizObjectExistsInDatabase.cs index ae880391..ae6ecbc5 100644 --- a/server/AyaNova/biz/BizObjectExistsInDatabase.cs +++ b/server/AyaNova/biz/BizObjectExistsInDatabase.cs @@ -1,12 +1,6 @@ -using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.JsonPatch; -using EnumsNET; using AyaNova.Util; -using AyaNova.Api.ControllerHelpers; -using AyaNova.Biz; using AyaNova.Models; @@ -20,14 +14,14 @@ namespace AyaNova.Biz { //THIS IS THE METHOD CALLED BY THE ATTACHMENT CONTROLLER - internal static async Task Exists(AyaTypeId tid) + internal static async Task ExistsAsync(AyaTypeId tid) { - return await Exists(tid.ObjectType, tid.ObjectId); + return await ExistsAsync(tid.ObjectType, tid.ObjectId); } //Returns existance status of object type and id specified in database - internal static bool Exists(AyaType aytype, long id, AyContext ct = null) + internal static async Task ExistsAsync(AyaType aytype, long id, AyContext ct = null) { //new up a context?? if (ct == null) @@ -37,22 +31,20 @@ namespace AyaNova.Biz switch (aytype) { case AyaType.User: - return ct.User.Any(m => m.Id == id); + return await ct.User.AnyAsync(m => m.Id == id); case AyaType.Widget: - return ct.Widget.Any(m => m.Id == id); + return await ct.Widget.AnyAsync(m => m.Id == id); case AyaType.FileAttachment: - return ct.FileAttachment.Any(m => m.Id == id); + return await ct.FileAttachment.AnyAsync(m => m.Id == id); case AyaType.DataListFilter: - return ct.DataListFilter.Any(m => m.Id == id); + return await ct.DataListFilter.AnyAsync(m => m.Id == id); case AyaType.DataListTemplate: - return ct.DataListTemplate.Any(m => m.Id == id); + return await ct.DataListTemplate.AnyAsync(m => m.Id == id); case AyaType.FormCustom: - return ct.FormCustom.Any(m => m.Id == id); - - + return await ct.FormCustom.AnyAsync(m => m.Id == id); default: - throw new System.NotSupportedException($"AyaNova.Biz.BizObjectExistsInDatabase::Exists type {aytype.ToString()} is not supported"); + throw new System.NotSupportedException($"AyaNova.Biz.BizObjectExistsInDatabase::ExistsAsync type {aytype.ToString()} is not supported"); } } diff --git a/server/AyaNova/biz/DataListFilterBiz.cs b/server/AyaNova/biz/DataListFilterBiz.cs index 489ba7f9..ed1d0924 100644 --- a/server/AyaNova/biz/DataListFilterBiz.cs +++ b/server/AyaNova/biz/DataListFilterBiz.cs @@ -49,7 +49,7 @@ namespace AyaNova.Biz //CREATE internal async Task CreateAsync(DataListFilter inObj) { - Validate(inObj, true); + await ValidateAsync(inObj, true); if (HasErrors) return null; else @@ -65,7 +65,7 @@ namespace AyaNova.Biz //Handle child and associated items: //EVENT LOG - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct); //SEARCH INDEXING // Search.ProcessNewObjectKeywords(UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.Name); @@ -77,9 +77,9 @@ namespace AyaNova.Biz //////////////////////////////////////////////////////////////////////////////////////////////// //CREATE - internal DataListFilter Create(AyContext TempContext, DataListFilter inObj) + internal async Task Create(AyContext ct, DataListFilter inObj) { - Validate(inObj, true); + await ValidateAsync(inObj, true); if (HasErrors) return null; else @@ -89,13 +89,13 @@ namespace AyaNova.Biz outObj.UserId = UserId; - TempContext.DataListFilter.Add(outObj); - TempContext.SaveChanges(); + await ct.DataListFilter.AddAsync(outObj); + await ct.SaveChangesAsync(); //Handle child and associated items: //EVENT LOG - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), TempContext); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct); //SEARCH INDEXING // Search.ProcessNewObjectKeywords(UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.Name); @@ -116,7 +116,7 @@ namespace AyaNova.Biz if (ret != null) { //Log - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct); } return ret; } @@ -158,7 +158,7 @@ namespace AyaNova.Biz // //put - internal bool Put(DataListFilter dbObj, DataListFilter inObj) + internal async Task PutAsync(DataListFilter dbObj, DataListFilter inObj) { //preserve the owner ID if none was specified if (inObj.UserId == 0) @@ -170,12 +170,13 @@ namespace AyaNova.Biz //this will allow EF to check it out ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken; - Validate(dbObj, false); + await ValidateAsync(dbObj, false); if (HasErrors) return false; + await ct.SaveChangesAsync(); //Log modification and save context - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); //Update keywords // Search.ProcessUpdatedObjectKeywords(UserLocaleId, dbObj.Id, BizType, dbObj.Name, dbObj.Name); @@ -187,22 +188,24 @@ namespace AyaNova.Biz //////////////////////////////////////////////////////////////////////////////////////////////// //DELETE // - internal bool Delete(DataListFilter dbObj) + internal async Task DeleteAsync(DataListFilter dbObj) { //Determine if the object can be deleted, do the deletion tentatively //Probably also in here deal with tags and associated search text etc - ValidateCanDelete(dbObj); + //FUTURE POSSIBLE NEED + //ValidateCanDelete(dbObj); + if (HasErrors) return false; ct.DataListFilter.Remove(dbObj); - ct.SaveChanges(); + await ct.SaveChangesAsync(); //Delete sibling objects //Event log process delete - EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct); - ct.SaveChanges(); + await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct); + //Delete search index //Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType); @@ -216,7 +219,7 @@ namespace AyaNova.Biz // //Can save or update? - private void Validate(DataListFilter inObj, bool isNew) + private async Task ValidateAsync(DataListFilter inObj, bool isNew) { //UserId required @@ -240,7 +243,7 @@ namespace AyaNova.Biz if (!PropertyHasErrors("Name")) { //Use Any command is efficient way to check existance, it doesn't return the record, just a true or false - if (ct.DataListFilter.Any(m => m.Name == inObj.Name && m.Id != inObj.Id)) + if (await ct.DataListFilter.AnyAsync(m => m.Name == inObj.Name && m.Id != inObj.Id)) { AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name"); } @@ -387,11 +390,11 @@ namespace AyaNova.Biz } - //Can delete? - private void ValidateCanDelete(DataListFilter inObj) - { - //Leaving this off for now - } + //FOR FUTURE + // private void ValidateCanDelete(DataListFilter inObj) + // { + // //Leaving this off for now + // } diff --git a/server/AyaNova/biz/DataListTemplateBiz.cs b/server/AyaNova/biz/DataListTemplateBiz.cs index d30b9f67..a5fc204c 100644 --- a/server/AyaNova/biz/DataListTemplateBiz.cs +++ b/server/AyaNova/biz/DataListTemplateBiz.cs @@ -56,7 +56,7 @@ namespace AyaNova.Biz if (log) { //Log - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, ret.Id, BizType, AyaEvent.Retrieved), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, ret.Id, BizType, AyaEvent.Retrieved), ct); } return ret; } @@ -68,7 +68,7 @@ namespace AyaNova.Biz // //put - internal bool Put(DataListTemplate dbObj, DataListTemplate inObj, IAyaDataList dataList) + internal async Task PutAsync(DataListTemplate dbObj, DataListTemplate inObj, IAyaDataList dataList) { //Replace the db object with the PUT object @@ -84,8 +84,10 @@ namespace AyaNova.Biz if (HasErrors) return false; + await ct.SaveChangesAsync(); + //Log modification and save context - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); return true; } @@ -94,14 +96,14 @@ namespace AyaNova.Biz //////////////////////////////////////////////////////////////////////////////////////////////// //DELETE // - internal bool Delete(DataListTemplate dbObj) + internal async Task DeleteAsync(DataListTemplate dbObj) { //no validation required, we have defaults so this is ok anytime ct.DataListTemplate.Remove(dbObj); - ct.SaveChanges(); + await ct.SaveChangesAsync(); + //Event log process delete - EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.DataListKey, ct); - ct.SaveChanges(); + await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.DataListKey, ct); return true; } diff --git a/server/AyaNova/biz/FormCustomBiz.cs b/server/AyaNova/biz/FormCustomBiz.cs index c65229b4..bd26c155 100644 --- a/server/AyaNova/biz/FormCustomBiz.cs +++ b/server/AyaNova/biz/FormCustomBiz.cs @@ -55,7 +55,7 @@ namespace AyaNova.Biz //CREATE internal async Task CreateAsync(FormCustom inObj) { - Validate(inObj, true); + await ValidateAsync(inObj, true); if (HasErrors) return null; else @@ -71,7 +71,7 @@ namespace AyaNova.Biz //Handle child and associated items: //EVENT LOG - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct); //SEARCH INDEXING // Search.ProcessNewObjectKeywords(UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.Name); @@ -125,7 +125,7 @@ namespace AyaNova.Biz // //put - internal bool Put(FormCustom dbObj, FormCustom inObj) + internal async Task PutAsync(FormCustom dbObj, FormCustom inObj) { //Replace the db object with the PUT object @@ -134,14 +134,15 @@ namespace AyaNova.Biz //this will allow EF to check it out ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken; - Validate(dbObj, false); + await ValidateAsync(dbObj, false); if (HasErrors) return false; dbObj.Template = JsonUtil.CompactJson(dbObj.Template); + await ct.SaveChangesAsync(); //Log modification and save context - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); //Update keywords // Search.ProcessUpdatedObjectKeywords(UserLocaleId, dbObj.Id, BizType, dbObj.Name, dbObj.Name); @@ -157,7 +158,7 @@ namespace AyaNova.Biz // //Can save or update? - private void Validate(FormCustom inObj, bool isNew) + private async Task ValidateAsync(FormCustom inObj, bool isNew) { //FormKey required and must be valid @@ -179,7 +180,7 @@ namespace AyaNova.Biz if (!PropertyHasErrors("FormKey") && isNew) { //Use Any command is efficient way to check existance, it doesn't return the record, just a true or false - if (ct.FormCustom.Any(m => m.FormKey == inObj.FormKey && m.Id != inObj.Id)) + if (await ct.FormCustom.AnyAsync(m => m.FormKey == inObj.FormKey && m.Id != inObj.Id)) { AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "FormKey"); } diff --git a/server/AyaNova/biz/ImportAyaNova7Biz.cs b/server/AyaNova/biz/ImportAyaNova7Biz.cs index 4fb00236..c6053b48 100644 --- a/server/AyaNova/biz/ImportAyaNova7Biz.cs +++ b/server/AyaNova/biz/ImportAyaNova7Biz.cs @@ -338,7 +338,7 @@ namespace AyaNova.Biz ////////////////////////////////////////////////////////////////// //UTILITIES - internal static void LogEventCreatedModifiedEvents(JObject j, List importMap, AyaType ayaType, AyContext ct) + internal static async Task LogEventCreatedModifiedEventsAsync(JObject j, List importMap, AyaType ayaType, AyContext ct) { var V7Id = new Guid(j["ID"].Value()); var RavenId = importMap.Where(m => m.V7ObjectId == V7Id).First().NewObjectAyaTypeId.ObjectId; @@ -349,9 +349,9 @@ namespace AyaNova.Biz //handle EventLog entries for users now that we have the user's created //Created - EventLogProcessor.LogEventToDatabaseAsync(new Event(Creator, RavenId, ayaType, AyaEvent.Created, Created), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(Creator, RavenId, ayaType, AyaEvent.Created, Created), ct); //MODIFIED - EventLogProcessor.LogEventToDatabaseAsync(new Event(Modifier, RavenId, ayaType, AyaEvent.Modified, Modified), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(Modifier, RavenId, ayaType, AyaEvent.Modified, Modified), ct); } diff --git a/server/AyaNova/biz/LocaleBiz.cs b/server/AyaNova/biz/LocaleBiz.cs index 0afb1ef3..ee462d3a 100644 --- a/server/AyaNova/biz/LocaleBiz.cs +++ b/server/AyaNova/biz/LocaleBiz.cs @@ -50,11 +50,11 @@ namespace AyaNova.Biz { //make sure sourceid exists - if (!LocaleExists(inObj.Id)) + if (!await LocaleExistsAsync(inObj.Id)) AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Id", "Source locale id does not exist"); //Ensure name is unique and not too long and not empty - ValidateAsync(inObj.Name, true); + await ValidateAsync(inObj.Name, true); if (HasErrors) return null; @@ -74,10 +74,10 @@ namespace AyaNova.Biz } //Add it to the context so the controller can save it - ct.Locale.Add(NewLocale); + await ct.Locale.AddAsync(NewLocale); await ct.SaveChangesAsync(); //Log - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, NewLocale.Id, AyaType.Locale, AyaEvent.Created), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, NewLocale.Id, AyaType.Locale, AyaEvent.Created), ct); return NewLocale; } @@ -113,12 +113,12 @@ namespace AyaNova.Biz #if (DEBUG) - internal AyaNova.Api.Controllers.LocaleController.LocaleCoverageInfo LocaleKeyCoverage() + internal async Task LocaleKeyCoverageAsync() { AyaNova.Api.Controllers.LocaleController.LocaleCoverageInfo L = new AyaNova.Api.Controllers.LocaleController.LocaleCoverageInfo(); L.RequestedKeys = ServerBootConfig.LocaleKeysRequested; L.RequestedKeys.Sort(); - var AllKeys = GetKeyList(); + var AllKeys = await GetKeyListAsync(); foreach (string StockKey in AllKeys) { if (!L.RequestedKeys.Contains(StockKey)) @@ -171,9 +171,9 @@ namespace AyaNova.Biz TrackRequestedKey(param); #endif AyContext ct = ServiceProviderProvider.DBContext; - if (!LocaleExistsStatic(localeId, ct)) + if (!await LocaleExistsStaticAsync(localeId, ct)) localeId = ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID; - var ret = await ct.LocaleItem.Where(x => x.LocaleId == localeId && param.Contains(x.Key)).ToDictionaryAsync(x => x.Key, x => x.Display); + var ret = await ct.LocaleItem.Where(x => x.LocaleId == localeId && param.Contains(x.Key)).AsNoTracking().ToDictionaryAsync(x => x.Key, x => x.Display); return ret; } @@ -183,7 +183,7 @@ namespace AyaNova.Biz { if (ct == null) ct = ServiceProviderProvider.DBContext; - var ret = await ct.Locale.Where(x => x.Id == localeId).Select(m => m.CjkIndex).SingleOrDefaultAsync(); + var ret = await ct.Locale.Where(x => x.Id == localeId).AsNoTracking().Select(m => m.CjkIndex).SingleOrDefaultAsync(); return ret; } @@ -201,14 +201,14 @@ namespace AyaNova.Biz TrackRequestedKey(key); #endif AyContext ct = ServiceProviderProvider.DBContext; - return await ct.LocaleItem.Where(m => m.LocaleId == ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID && m.Key == key).Select(m => m.Display).FirstOrDefaultAsync(); + return await ct.LocaleItem.Where(m => m.LocaleId == ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID && m.Key == key).Select(m => m.Display).AsNoTracking().FirstOrDefaultAsync(); } //Get all stock keys that are valid (used for import) - internal static List GetKeyList() + internal static async Task> GetKeyListAsync() { AyContext ct = ServiceProviderProvider.DBContext; - return ct.LocaleItem.Where(m => m.LocaleId == 1).OrderBy(m => m.Key).Select(m => m.Key).ToList(); + return await ct.LocaleItem.Where(m => m.LocaleId == 1).OrderBy(m => m.Key).Select(m => m.Key).AsNoTracking().ToListAsync(); } @@ -262,7 +262,7 @@ namespace AyaNova.Biz //this will allow EF to check it out ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken; - ValidateAsync(dbObj.Name, false); + await ValidateAsync(dbObj.Name, false); if (HasErrors) return false; @@ -281,13 +281,14 @@ namespace AyaNova.Biz //DELETE // - internal bool Delete(Locale dbObj) + internal async Task DeleteAsync(Locale dbObj) { //Determine if the object can be deleted, do the deletion tentatively - ValidateCanDeleteAsync(dbObj); + await ValidateCanDeleteAsync(dbObj); if (HasErrors) return false; ct.Locale.Remove(dbObj); + await ct.SaveChangesAsync(); return true; } @@ -310,7 +311,7 @@ namespace AyaNova.Biz AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "Name", "255 char max"); //Name must be unique - if( await ct.Locale.AnyAsync(m => m.Name == inObjName)) + if (await ct.Locale.AnyAsync(m => m.Name == inObjName)) AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name"); return; @@ -348,51 +349,51 @@ namespace AyaNova.Biz //UTILITIES // - public long LocaleNameToId(string localeName) + public async Task LocaleNameToIdAsync(string localeName) { - var v = ct.Locale.Where(c => c.Name == localeName).Select(x => x.Id); - if (v.Count() < 1) return 0; - return v.First(); + var v = await ct.Locale.AsNoTracking().FirstOrDefaultAsync(c => c.Name == localeName); + if (v == null) return 0; + return v.Id; } - public static long LocaleNameToIdStatic(string localeName, AyContext ct = null) + public static async Task LocaleNameToIdStaticAsync(string localeName, AyContext ct = null) { if (ct == null) { ct = ServiceProviderProvider.DBContext; } - var v = ct.Locale.Where(c => c.Name == localeName).Select(x => x.Id); - if (v.Count() < 1) return ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID; - return v.First(); + var v = await ct.Locale.AsNoTracking().FirstOrDefaultAsync(c => c.Name == localeName); + if (v == null) return ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID; + return v.Id; } - public bool LocaleExists(string localeName) + public async Task LocaleExistsAsync(string localeName) { - return LocaleNameToId(localeName) != 0; + return await LocaleNameToIdAsync(localeName) != 0; } - public bool LocaleExists(long id) + public async Task LocaleExistsAsync(long id) { - return ct.Locale.Any(e => e.Id == id); + return await ct.Locale.AnyAsync(e => e.Id == id); } - public static bool LocaleExistsStatic(long id, AyContext ct) + public static async Task LocaleExistsStaticAsync(long id, AyContext ct) { - return ct.Locale.Any(e => e.Id == id); + return await ct.Locale.AnyAsync(e => e.Id == id); } - public static long EnsuredLocaleIdStatic(long id, AyContext ct) + public static async Task EnsuredLocaleIdStaticAsync(long id, AyContext ct) { - if (!ct.Locale.Any(e => e.Id == id)) + if (!await ct.Locale.AnyAsync(e => e.Id == id)) return ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID; return id; } - public bool LocaleItemExists(long id) + public async Task LocaleItemExistsAsync(long id) { - return ct.LocaleItem.Any(e => e.Id == id); + return await ct.LocaleItem.AnyAsync(e => e.Id == id); } @@ -478,23 +479,23 @@ namespace AyaNova.Biz /// Ensure stock locales and setup defaults /// Called by boot preflight check code AFTER it has already ensured the locale is a two letter code if stock one was chosen /// - public void ValidateLocales() + public async Task ValidateLocalesAsync() { //Ensure default locales are present and that there is a server default locale that exists - if (!LocaleExists("en")) + if (!await LocaleExistsAsync("en")) { throw new System.Exception($"E1015: stock locale English (en) not found in database!"); } - if (!LocaleExists("es")) + if (!await LocaleExistsAsync("es")) { throw new System.Exception($"E1015: stock locale Spanish (es) not found in database!"); } - if (!LocaleExists("de")) + if (!await LocaleExistsAsync("de")) { throw new System.Exception($"E1015: stock locale German (de) not found in database!"); } - if (!LocaleExists("fr")) + if (!await LocaleExistsAsync("fr")) { throw new System.Exception($"E1015: stock locale French (fr) not found in database!"); } @@ -508,7 +509,7 @@ namespace AyaNova.Biz case "fr": break; default: - if (!LocaleExists(ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE)) + if (!await LocaleExistsAsync(ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE)) { throw new System.Exception($"E1015: stock locale {ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE} not found in database!"); } @@ -517,14 +518,16 @@ namespace AyaNova.Biz } //Put the default locale ID number into the ServerBootConfig for later use - ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID = LocaleNameToId(ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE); + ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID = await LocaleNameToIdAsync(ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE); } ///////////////////////////////////////////////////////////////////// /// IMPORT v7 implementation - public async Task ImportV7Async(JObject j, List importMap, Guid jobId, Dictionary> tagLists) + public async Task ImportV7Async( + JObject j, List importMap, + Guid jobId, Dictionary> tagLists) { //some types need to import from more than one source hence the seemingly redundant switch statement for futureproofing switch (j["IMPORT_TASK"].Value()) @@ -538,7 +541,7 @@ namespace AyaNova.Biz var SourceLocaleName = v.Groups[1].ToString(); //Ensure doesn't already exist - if (LocaleExists(SourceLocaleName)) + if (await LocaleExistsAsync(SourceLocaleName)) { //If there are any validation errors, log in joblog and move on JobsBiz.LogJob(jobId, $"LocaleBiz::ImportV7Async -> - Locale \"{SourceLocaleName}\" already exists in database, can not import over an existing locale", ct); @@ -552,7 +555,7 @@ namespace AyaNova.Biz SkipKeys.Add("V7_TYPE"); SkipKeys.Add("IMPORT_TASK"); - List ValidKeys = GetKeyList(); + List ValidKeys = await GetKeyListAsync(); Dictionary NewLocaleDict = new Dictionary(); foreach (var Pair in j.Children()) { @@ -587,7 +590,7 @@ namespace AyaNova.Biz { if (!NewLocaleDict.ContainsKey(s)) { - NewLocaleDict.Add(s, GetDefaultLocalizedTextAsync(s).Result); + NewLocaleDict.Add(s, await GetDefaultLocalizedTextAsync(s)); } } @@ -607,18 +610,18 @@ namespace AyaNova.Biz l.LocaleItems.Add(new LocaleItem() { Key = K.Key, Display = K.Value }); } - ct.Locale.Add(l); - ct.SaveChanges(); + await ct.Locale.AddAsync(l); + await ct.SaveChangesAsync(); //Log now that we have the Id, note that there is no source created / modified for this so just attributing to current userId - EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, l.Id, AyaType.Locale, AyaEvent.Created), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, l.Id, AyaType.Locale, AyaEvent.Created), ct); } break; } - //just to hide compiler warning for now - await Task.CompletedTask; + + //this is the equivalent of returning void for a Task signature with nothing to return return true; } diff --git a/server/AyaNova/biz/PrimeData.cs b/server/AyaNova/biz/PrimeData.cs index 75295ad1..d39de63b 100644 --- a/server/AyaNova/biz/PrimeData.cs +++ b/server/AyaNova/biz/PrimeData.cs @@ -61,7 +61,7 @@ namespace AyaNova.Biz //Ensure locales are present, not missing any keys and that there is a server default locale that exists LocaleBiz lb = LocaleBiz.GetBizInternal(ServiceProviderProvider.DBContext, 1, AuthorizationRoles.OpsAdminFull); - lb.ValidateLocales(); + lb.ValidateLocalesAsync(); } diff --git a/server/AyaNova/biz/Search.cs b/server/AyaNova/biz/Search.cs index 0f56601d..6010f702 100644 --- a/server/AyaNova/biz/Search.cs +++ b/server/AyaNova/biz/Search.cs @@ -125,7 +125,7 @@ namespace AyaNova.Biz searchParameters.Phrase = searchParameters.Phrase.Replace("*", "%"); //BREAK SEARCH PHRASE INTO SEPARATE TERMS - var PhraseItems = BreakSearchPhrase(localeId, searchParameters.Phrase); + var PhraseItems = await BreakSearchPhraseAsync(localeId, searchParameters.Phrase); //SPLIT OUT WILDCARDS FROM NON WILDCARDS List WildCardSearchTerms = new List(); @@ -481,10 +481,10 @@ namespace AyaNova.Biz } //BREAK OBJECT TEXT STRINGS INTO KEYWORD LIST - List KeyWordList = Break(p.LocaleId, p.Words); + List KeyWordList = await BreakAsync(p.LocaleId, p.Words); //BREAK NAME STRING - List NameKeyWordList = Break(p.LocaleId, p.Name); + List NameKeyWordList = await BreakAsync(p.LocaleId, p.Name); //EARLY EXIT IF NO KEYWORDS OR NAME RECORD OR TAGS TO PROCESS @@ -706,7 +706,7 @@ cache or provide directly the locale to save time repeatedly fetching it when do //Get the current stopwords for the user's locale //called in here in this class and also by any bulk ops like seeding etc - internal static LocaleWordBreakingData GetLocaleSearchData(long localeId, AyContext ct = null) + internal static async Task GetLocaleSearchDataAsync(long localeId, AyContext ct = null) { LocaleWordBreakingData LSD = new LocaleWordBreakingData(); if (ct == null) @@ -714,7 +714,7 @@ cache or provide directly the locale to save time repeatedly fetching it when do //Get stopwords //Validate locale id, if not right then use default instead var Param = new List(); - localeId = LocaleBiz.EnsuredLocaleIdStatic(localeId, ct); + localeId = await LocaleBiz.EnsuredLocaleIdStaticAsync(localeId, ct); Param.Add("StopWords1"); Param.Add("StopWords2"); Param.Add("StopWords3"); @@ -722,7 +722,7 @@ cache or provide directly the locale to save time repeatedly fetching it when do Param.Add("StopWords5"); Param.Add("StopWords6"); Param.Add("StopWords7"); - var Stops = LocaleBiz.GetSubsetStaticAsync(Param, localeId).Result; + var Stops = await LocaleBiz.GetSubsetStaticAsync(Param, localeId); foreach (KeyValuePair kvp in Stops) { @@ -733,7 +733,7 @@ cache or provide directly the locale to save time repeatedly fetching it when do } } - LSD.CJKIndex = LocaleBiz.GetCJKIndexAsync(localeId, ct).Result; + LSD.CJKIndex = await LocaleBiz.GetCJKIndexAsync(localeId, ct); return LSD; } @@ -751,30 +751,30 @@ cache or provide directly the locale to save time repeatedly fetching it when do /// (languages with no easily identifiable word boundaries as in english) /// /// List of strings - internal static List Break(long localeId, List textStrings) + internal static async Task> BreakAsync(long localeId, List textStrings) { - return BreakCore(localeId, false, textStrings); + return await BreakCoreAsync(localeId, false, textStrings); } /// /// /// - internal static List Break(long localeId, string textString) + internal static async Task> BreakAsync(long localeId, string textString) { List textStrings = new List(1); textStrings.Add(textString); - return BreakCore(localeId, false, textStrings); + return await BreakCoreAsync(localeId, false, textStrings); } /// /// Used to Process users search phrase and preserve wild /// cards entered /// - internal static List BreakSearchPhrase(long localeId, string searchPhrase) + internal static async Task> BreakSearchPhraseAsync(long localeId, string searchPhrase) { List textStrings = new List(); textStrings.Add(searchPhrase); - return BreakCore(localeId, true, textStrings); + return await BreakCoreAsync(localeId, true, textStrings); } /// @@ -790,14 +790,14 @@ cache or provide directly the locale to save time repeatedly fetching it when do localeWordBreakingDataCache.Remove(localeId); } - internal static List BreakCore(long localeId, bool KeepWildCards, List textStrings) + internal static async Task> BreakCoreAsync(long localeId, bool KeepWildCards, List textStrings) { //For stopwords and CJKIndex flag value //if not provided (will be provided by seeder for performance but normally never) then fetch if (!localeWordBreakingDataCache.ContainsKey(localeId)) { - localeWordBreakingDataCache.Add(localeId, GetLocaleSearchData(localeId)); + localeWordBreakingDataCache.Add(localeId, await GetLocaleSearchDataAsync(localeId)); } var localeWordBreakData = localeWordBreakingDataCache[localeId]; diff --git a/server/AyaNova/biz/UserBiz.cs b/server/AyaNova/biz/UserBiz.cs index 7ee72291..5b8fbb2f 100644 --- a/server/AyaNova/biz/UserBiz.cs +++ b/server/AyaNova/biz/UserBiz.cs @@ -32,12 +32,11 @@ namespace AyaNova.Biz } //This is where active tech license consumers are accounted for - internal static long ActiveCount + internal static async Task ActiveCount() { - get - { - return ServiceProviderProvider.DBContext.User.Where(x => x.Active == true && (x.UserType == UserType.Schedulable || x.UserType == UserType.Subcontractor)).LongCount(); - } + var ct = ServiceProviderProvider.DBContext; + var ret = await ct.User.Where(x => x.Active == true && (x.UserType == UserType.Schedulable || x.UserType == UserType.Subcontractor)).LongCountAsync(); + return ret; } internal static UserBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext) @@ -830,7 +829,7 @@ namespace AyaNova.Biz break; case "eventlog": { - ImportAyaNova7Biz.LogEventCreatedModifiedEvents(j, importMap, BizType, ct); + ImportAyaNova7Biz.LogEventCreatedModifiedEventsAsync(j, importMap, BizType, ct); } break; case "locale": @@ -885,7 +884,7 @@ namespace AyaNova.Biz break; } - u.LocaleId = LocaleBiz.LocaleNameToIdStatic(NewLocaleName, ct); + u.LocaleId = LocaleBiz.LocaleNameToIdStaticAsync(NewLocaleName, ct); ct.SaveChanges(); #endregion set locale diff --git a/server/AyaNova/util/Seeder.cs b/server/AyaNova/util/Seeder.cs index aa14c047..d8f89215 100644 --- a/server/AyaNova/util/Seeder.cs +++ b/server/AyaNova/util/Seeder.cs @@ -469,9 +469,9 @@ namespace AyaNova.Util GenSeedUser(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable, timeZoneOffset, false, "TEST_INACTIVE", "TEST_INACTIVE"); //Alternate locale users for each stock locale - GenSeedUser(log, 1, AuthorizationRoles.All, UserType.Administrator, timeZoneOffset, true, "de", "de", LocaleBiz.LocaleNameToIdStatic("de")); - GenSeedUser(log, 1, AuthorizationRoles.All, UserType.Administrator, timeZoneOffset, true, "es", "es", LocaleBiz.LocaleNameToIdStatic("es")); - GenSeedUser(log, 1, AuthorizationRoles.All, UserType.Administrator, timeZoneOffset, true, "fr", "fr", LocaleBiz.LocaleNameToIdStatic("fr")); + GenSeedUser(log, 1, AuthorizationRoles.All, UserType.Administrator, timeZoneOffset, true, "de", "de", LocaleBiz.LocaleNameToIdStaticAsync("de")); + GenSeedUser(log, 1, AuthorizationRoles.All, UserType.Administrator, timeZoneOffset, true, "es", "es", LocaleBiz.LocaleNameToIdStaticAsync("es")); + GenSeedUser(log, 1, AuthorizationRoles.All, UserType.Administrator, timeZoneOffset, true, "fr", "fr", LocaleBiz.LocaleNameToIdStaticAsync("fr")); }