This commit is contained in:
2020-01-28 18:49:15 +00:00
parent 6a682a6475
commit a94260b300
14 changed files with 84 additions and 261 deletions

View File

@@ -1,5 +1,12 @@
MARKETING AND SALES IDEAS
This was interesting: https://getpocket.com/explore/item/tapping-into-the-power-of-humble-narcissism
"The first kind of humility is humility about your ideas. Take Rufus Griscom (TED Talk, given with Alisa Volkman: Lets talk parenting taboos). When he founded the parenting blog Babble, he did something Ive never seen an entrepreneur do. He said, “Here are the three reasons you should not invest in my company” — and he walked away with $3.1 million in funding that year. Two years later, he went to pitch Babble to Disney, and he included a slide in his pitch deck that read, “Here are the five reasons you should not buy Babble.” Disney acquired it for $40 million.
By speaking candidly about the downsides of his idea, Rufus made his comments about the upsides more credible. Admitting the flaws outright also made it tougher for investors to come up with their own objections. The harder they had to work to identify what was wrong with the company, the more they thought was right with the company. The conversation changed: his investors proposed solutions to the problems.
If you ever took a debate class, you were taught to identify the weaknesses in your argument and address them out loud. But we forget to do this when we pitch our ideas: we worry that theyre fragile and we dont want to shoot ourselves in the foot. We overlook the fact that well actually seem more credible and trustworthy — and other people will see more potential in our ideas — if we have the humility to acknowledge their limitations."
We will have two markets:

View File

@@ -5,14 +5,10 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIxNTcxODU5OTU0IiwiZXhwIjoiMTU3MjQ
## IMMEDIATE ITEMS
TODO: Any code at the server that access the db or does file IO MUST be async top to bottom!!
- FileUtil may have a bunch of IO stuff
TODO: REFACTOR GetNoLogAsync function is used in many places redundantly when the logging version could do the same thing but not log it with an optional bool switch so refactor that shit
TODO: REFACTOR biz objects have two creates, an async and sync one, WTF is that about? See if can make it just one async version.
- https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1
TODO: REFACTOR BIZ objects have getbiz internal which can be removed and just check if httpcontext is null
TODO: Seeder create widget is creating a new widgetbiz each loop iteration but create user is not, try both ways and performance test
@@ -33,6 +29,15 @@ TODO: DataFilter how to distinguish between filtering on specific ID value or on
- Might need to add a filter on ID type of thing maybe?
- Wait and see...YAGNI scenario maybe
TODO: Need to test for memory leaks
TODO: HUGE BURN TEST OVER WEEKEND
- Currently with huge dataset burn test takes 11 hours
- If start Friday night can do 4.3 runs of 1000 and be done by Monday morning to analyze the results
- so start it friday night with 4300 runs and observer over the weekend, does it get slower?, crash?
- Ideally need to do this with the server as well once next release is posted there
DO CLIENT STUFF NOW COME BACK TO THIS STUFF LATER
TODO: the license being logged breaks the format of the log file because it has line breaks etc, so instead, maybe log out as a single line (remove breaks)

View File

@@ -114,7 +114,7 @@ namespace AyaNova.Api.Controllers
//Instantiate the business object handler
DataListFilterBiz biz = DataListFilterBiz.GetBiz(ct, HttpContext);
var o = await biz.GetNoLogAsync(id);
var o = await biz.GetAsync(id, false);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
@@ -187,7 +187,7 @@ namespace AyaNova.Api.Controllers
//Instantiate the business object handler
DataListFilterBiz biz = DataListFilterBiz.GetBiz(ct, HttpContext);
var o = await biz.GetNoLogAsync(id);
var o = await biz.GetAsync(id, false);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));

View File

@@ -156,7 +156,7 @@ namespace AyaNova.Api.Controllers
//Instantiate the business object handler
FormCustomBiz biz = FormCustomBiz.GetBiz(ct, HttpContext);
var o = await biz.GetNoLogAsync(formkey);
var o = await biz.GetAsync(formkey);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));

View File

@@ -409,7 +409,7 @@ namespace AyaNova
// ******************** TESTING WIPE DB *****************************
//
//Set this to true to wipe the db and reinstall a trial license and re-seed the data
var TESTING_REFRESH_DB = true;//#######################################################################################
var TESTING_REFRESH_DB = false;//#######################################################################################
#if (DEBUG)
@@ -450,7 +450,7 @@ namespace AyaNova
{
AyaNova.Core.License.FetchKeyAsync(apiServerState, dbContext, _newLog).Wait();
//NOTE: For unit testing make sure the time zone in util is set to the same figure as here to ensure list filter by date tests will work because server is on same page as user in terms of time
Util.Seeder.SeedDatabaseAsync(Util.Seeder.SeedLevel.HugeForLoadTest, -7).Wait();//#############################################################################################
Util.Seeder.SeedDatabaseAsync(Util.Seeder.SeedLevel.SmallOneManShopTrialDataSet, -7).Wait();//#############################################################################################
}
//TESTING
#endif

View File

@@ -30,11 +30,11 @@ namespace AyaNova.Biz
return new DataListFilterBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
}
//Version for internal use
internal static DataListFilterBiz GetBizInternal(AyContext ct)
{
return new DataListFilterBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
}
// //Version for internal use
// internal static DataListFilterBiz GetBizInternal(AyContext ct)
// {
// return new DataListFilterBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
@@ -75,45 +75,17 @@ namespace AyaNova.Biz
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
internal async Task<DataListFilter> Create(AyContext ct, DataListFilter inObj)
{
await ValidateAsync(inObj, true);
if (HasErrors)
return null;
else
{
//do stuff with datafilter
DataListFilter outObj = inObj;
outObj.UserId = UserId;
await ct.DataListFilter.AddAsync(outObj);
await ct.SaveChangesAsync();
//Handle child and associated items:
//EVENT LOG
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
//SEARCH INDEXING
// Search.ProcessNewObjectKeywords(UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.Name);
return outObj;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
//Get one
internal async Task<DataListFilter> GetAsync(long fetchId)
internal async Task<DataListFilter> GetAsync(long fetchId, bool logTheGetEvent = true)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
var ret = await ct.DataListFilter.SingleOrDefaultAsync(m => m.Id == fetchId && (m.Public == true || m.UserId == UserId));
if (ret != null)
if (logTheGetEvent && ret != null)
{
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct);
@@ -121,13 +93,7 @@ namespace AyaNova.Biz
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
internal async Task<DataListFilter> GetNoLogAsync(long fetchId)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
return await ct.DataListFilter.SingleOrDefaultAsync(m => m.Id == fetchId);
}
//get picklist (NOT PAGED)
@@ -205,7 +171,7 @@ namespace AyaNova.Biz
//Event log process delete
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
//Delete search index
//Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType);

View File

@@ -29,11 +29,11 @@ namespace AyaNova.Biz
return new DataListTemplateBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
}
//Version for internal use
internal static DataListTemplateBiz GetBizInternal(AyContext ct)
{
return new DataListTemplateBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
}
// //Version for internal use
// internal static DataListTemplateBiz GetBizInternal(AyContext ct)
// {
// return new DataListTemplateBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
// }

View File

@@ -30,11 +30,11 @@ namespace AyaNova.Biz
return new FormCustomBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
}
//Version for internal use
internal static FormCustomBiz GetBizInternal(AyContext ct)
{
return new FormCustomBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
}
// //Version for internal use
// internal static FormCustomBiz GetBizInternal(AyContext ct)
// {
// return new FormCustomBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
@@ -43,13 +43,7 @@ namespace AyaNova.Biz
return await ct.FormCustom.AnyAsync(x => x.FormKey == formKey);
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
internal async Task<FormCustom> GetNoLogAsync(string formKey)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
return await ct.FormCustom.SingleOrDefaultAsync(x => x.FormKey == formKey);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE

View File

@@ -30,15 +30,13 @@ namespace AyaNova.Biz
SeedOrImportRelaxedRulesMode = false;//default
}
internal static LocaleBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext)
internal static LocaleBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
{
return new LocaleBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
}
if (httpContext != null)
return new LocaleBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
else
return new LocaleBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
//Version for internal use
internal static LocaleBiz GetBizInternal(AyContext ct, long currentUserId, AuthorizationRoles roles)
{
return new LocaleBiz(ct, currentUserId, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, roles);
}
@@ -171,7 +169,8 @@ namespace AyaNova.Biz
TrackRequestedKey(param);
#endif
AyContext ct = ServiceProviderProvider.DBContext;
if (!await LocaleExistsStaticAsync(localeId, ct))
if (!await ct.Locale.AnyAsync(e => e.Id == localeId))
localeId = ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID;
var ret = await ct.LocaleItem.Where(x => x.LocaleId == localeId && param.Contains(x.Key)).AsNoTracking().ToDictionaryAsync(x => x.Key, x => x.Display);
return ret;
@@ -289,7 +288,7 @@ namespace AyaNova.Biz
return false;
ct.Locale.Remove(dbObj);
await ct.SaveChangesAsync();
//Log
//Log
await EventLogProcessor.DeleteObjectLogAsync(UserId, AyaType.Locale, dbObj.Id, dbObj.Name, ct);
return true;
}
@@ -371,7 +370,8 @@ namespace AyaNova.Biz
public async Task<bool> LocaleExistsAsync(string localeName)
{
return await LocaleNameToIdAsync(localeName) != 0;
return await ct.Locale.AnyAsync(c => c.Name == localeName);
}
public async Task<bool> LocaleExistsAsync(long id)
@@ -380,13 +380,13 @@ namespace AyaNova.Biz
}
public static async Task<bool> LocaleExistsStaticAsync(long id, AyContext ct)
{
return await ct.Locale.AnyAsync(e => e.Id == id);
}
// public static async Task<bool> LocaleExistsStaticAsync(long id, AyContext ct)
// {
// return await ct.Locale.AnyAsync(e => e.Id == id);
// }
public static async Task<long> EnsuredLocaleIdStaticAsync(long id, AyContext ct)
//this is only called by Search.cs to cache a local cjk and stopwords, no one else calls it currently
public static async Task<long> ReturnSpecifiedLocaleIdIfExistsOrDefaultLocaleId(long id, AyContext ct)
{
if (!await ct.Locale.AnyAsync(e => e.Id == id))
return ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID;
@@ -622,8 +622,8 @@ namespace AyaNova.Biz
break;
}
//this is the equivalent of returning void for a Task signature with nothing to return
return true;
}

View File

@@ -59,7 +59,7 @@ namespace AyaNova.Biz
await ImportLocale(ResourceFolderPath, "de");
//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);
LocaleBiz lb = LocaleBiz.GetBiz(ServiceProviderProvider.DBContext);
await lb.ValidateLocalesAsync();
}

View File

@@ -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<string>();
localeId = await LocaleBiz.EnsuredLocaleIdStaticAsync(localeId, ct);
localeId = await LocaleBiz.ReturnSpecifiedLocaleIdIfExistsOrDefaultLocaleId(localeId, ct);
Param.Add("StopWords1");
Param.Add("StopWords2");
Param.Add("StopWords3");

View File

@@ -34,8 +34,8 @@ namespace AyaNova.Biz
{
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;
return ret;
}
internal static UserBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext)
@@ -43,11 +43,11 @@ namespace AyaNova.Biz
return new UserBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
}
//Version for internal use
internal static UserBiz GetBizInternal(AyContext ct)
{
return new UserBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
}
// //Version for internal use
// internal static UserBiz GetBizInternal(AyContext ct)
// {
// return new UserBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
@@ -91,47 +91,6 @@ namespace AyaNova.Biz
}
}
// ////////////////////////////////////////////////////////////////////////////////////////////////
// //CREATE
// internal User Create(AyContext TempContext, User inObj)
// {
// //This is a new user so it will have been posted with a password in plaintext which needs to be salted and hashed
// inObj.Salt = Hasher.GenerateSalt();
// inObj.Password = Hasher.hash(inObj.Salt, inObj.Password);
// inObj.Tags = TagUtil.NormalizeTags(inObj.Tags);
// inObj.CustomFields = JsonUtil.CompactJson(inObj.CustomFields);
// //Seeder sets user options in advance so no need to create them here in that case
// if (inObj.UserOptions == null)
// inObj.UserOptions = new UserOptions();
// ValidateAsync(inObj, null);
// if (HasErrors)
// return null;
// else
// {
// TempContext.User.Add(inObj);
// //save to get Id
// TempContext.SaveChanges();
// //Handle child and associated items
// //Log event
// EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, inObj.Id, BizType, AyaEvent.Created), TempContext);
// //SEARCH INDEXING
// SearchIndexAsync(inObj, true);
// //TAGS
// TagUtil.ProcessUpdateTagsInRepositoryAsync(TempContext, inObj.Tags, null);
// return inObj;
// }
// }
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
@@ -151,114 +110,6 @@ namespace AyaNova.Biz
// public static FilterOptions FilterOptions(long localizeToLocaleId = 0)
// {
// //NOTE: All column names are lowercase to conform with Postgres AyaNova DB which uses lowercase for all identifiers
// //Also all list keys are lower case for consistency
// FilterOptions f = new FilterOptions("user");
// f.
// AddField("id", "ID", AyDataType.Integer).
// AddField("name", "Name", AyDataType.Text).
// AddField("active", "Active", AyDataType.Bool).
// AddField("tags", "Tags", AyDataType.Tags).
// AddField("EmployeeNumber", "UserEmployeeNumber", AyDataType.Text).
// AddField("usertype", "UserUserType", AyDataType.Enum).
// AddField("notes", "UserNotes", AyDataType.Text);
// if (localizeToLocaleId != 0)
// f.Localize(localizeToLocaleId);
// return f;
// }
// //get many (paged)
// internal async Task<ApiPagedResponse<System.Object>> GetManyAsync(IUrlHelper Url, string routeName, ListOptions pagingOptions)
// {
// pagingOptions.Offset = pagingOptions.Offset ?? ListOptions.DefaultOffset;
// pagingOptions.Limit = pagingOptions.Limit ?? ListOptions.DefaultLimit;
// //BUILD THE QUERY
// //base query
// var q = "SELECT *, xmin FROM AUSER ";
// //GET THE FILTER / SORT
// if (pagingOptions.DataFilterId > 0)
// {
// var TheFilter = await ct.DataFilter.FirstOrDefaultAsync(x => x.Id == pagingOptions.DataFilterId);
// //BUILD WHERE AND APPEND IT
// q = q + SqlFilterCriteriaBuilder.DataFilterToSQLCriteria(TheFilter, AyaObjectFieldDefinitions.AyaObjectFields(AyaObjectFieldDefinitions.USER_KEY), UserId);
// //BUILD ORDER BY AND APPEND IT
// q = q + SqlFilterOrderByBuilder.DataFilterToSQLOrderBy(TheFilter);
// }
// // else
// // {
// // //GET DEFAULT ORDER BY
// // q = q + SqlFilterOrderByBuilder.DefaultGetManyOrderBy;
// // }
// #pragma warning disable EF1000
// // var items = await ct.User
// // .AsNoTracking()
// // .FromSql(q)
// // .Skip(pagingOptions.Offset.Value)
// // .Take(pagingOptions.Limit.Value)
// // .ToArrayAsync();
// var items = await ct.User
// .FromSqlRaw(q)
// .AsNoTracking()
// .Skip(pagingOptions.Offset.Value)
// .Take(pagingOptions.Limit.Value)
// .ToArrayAsync();
// // var totalRecordCount = await ct.User
// // .AsNoTracking()
// // .FromSql(q)
// // .CountAsync();
// var totalRecordCount = await ct.User.FromSqlRaw(q)
// .AsNoTracking()
// .CountAsync();
// #pragma warning restore EF1000
// int itemCount = items.Count();//totalRecordCount doesn't skip and take so not usable here
// var cleanedItems = new System.Object[itemCount];
// for (int i = 0; i < itemCount; i++)
// {
// cleanedItems[i] = CleanUserForReturn(items[i]);
// }
// var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, totalRecordCount).PagingLinksObject();
// ApiPagedResponse<System.Object> pr = new ApiPagedResponse<System.Object>(cleanedItems, pageLinks);
// return pr;
// }
// //get picklist (paged)
// internal ApiPagedResponse<NameIdItem> GetPickList(IUrlHelper Url, string routeName, ListOptions pagingOptions)
// {
// pagingOptions.Offset = pagingOptions.Offset ?? ListOptions.DefaultOffset;
// pagingOptions.Limit = pagingOptions.Limit ?? ListOptions.DefaultLimit;
// var ret = PickListFetcher.GetPickList(ct, UserId, pagingOptions, AyaObjectFieldDefinitions.AyaObjectFields(AyaObjectFieldDefinitions.USER_KEY), "auser");
// var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, ret.TotalRecordCount).PagingLinksObject();
// ApiPagedResponse<NameIdItem> pr = new ApiPagedResponse<NameIdItem>(ret.Items, pageLinks);
// return pr;
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
@@ -907,7 +758,7 @@ namespace AyaNova.Biz
break;
}
//this is the equivalent of returning void for a Task signature with nothing to return
return true;

View File

@@ -21,16 +21,16 @@ namespace AyaNova.Biz
BizType = AyaType.Widget;
}
internal static WidgetBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext)
internal static WidgetBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
{
return new WidgetBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
if (httpContext != null)
return new WidgetBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
else//when called internally for internal ops there will be no context so need to set default values for that
return new WidgetBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
}
//Version for internal use
internal static WidgetBiz GetBizInternal(AyContext ct)
{
return new WidgetBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
@@ -44,11 +44,11 @@ namespace AyaNova.Biz
///
///
internal async Task<Widget> GetAsync(long fetchId, bool log = true)
internal async Task<Widget> GetAsync(long fetchId, bool logTheGetEvent = true)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
var ret = await ct.Widget.SingleOrDefaultAsync(m => m.Id == fetchId);
if (log && ret != null)
if (logTheGetEvent && ret != null)
{
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct);

View File

@@ -138,7 +138,7 @@ namespace AyaNova.Util
//Seed special test data for integration testing
//log.LogInformation("Seeding known users");
await SeedKnownUsersAsync(log, timeZoneOffset);
await SeedKnownUsersAsync(log, timeZoneOffset);
//log.LogInformation("Seeding all other data");
switch (slevel)
@@ -163,7 +163,7 @@ namespace AyaNova.Util
//100 widgets
watch = new Stopwatch();
watch.Start();
watch.Start();
await GenSeedWidgetAsync(log, 100);
//PERF
watch.Stop();
@@ -605,8 +605,8 @@ namespace AyaNova.Util
o.CustomFields = $@"{{c1:""{c1}"",c2:""{c2}"",c3:{c3},c4:{c4},c5:{c5}}}";
WidgetBiz biz = WidgetBiz.GetBizInternal(ServiceProviderProvider.DBContext);
WidgetBiz biz = WidgetBiz.GetBiz(ServiceProviderProvider.DBContext);
var NewObject = await biz.CreateAsync(o);
if (NewObject == null)
{