Repository navigation
Fix six defects found testing a LiteDB install and the admin - #1004
Merged
Merged
Conversation
added 6 commits
October 31, 2026 11:00
With Singleton false every request opened its own LiteDatabase on the same file. LiteDB locks the file, so every request failed with an IOException and the application could not serve a single page. There is no working setup behind the option, so it goes. A value left in an existing configuration is ignored.
BsonMapper publishes a mapper before filling its members and reads published mappers without a lock. Threads meeting a type for the first time together either threw "Collection was modified" or "Member Id not found", or silently read an object with fields missing. Requests right after every start got a 500, and so did the scheduled tasks and the admin dashboard. The mappers are now built during the single-threaded part of startup, for every entity with its nested types, base types and interfaces. Base types matter because LINQ resolves x.Id on the type declaring it, ParentEntity.
The installer leaves the store time zone empty, which means the server's. The settings form has no empty option, so its select showed the first zone in the list, and saving the page for any other reason stored UTC-12 and moved every date in the shop by hours. The form now preselects the configured zone, or the server's when none is set or the machine does not know it.
On the login page the email field has focus from the start. Pressing the mouse on Checkout as Guest blurred it, the required message appeared and moved the layout, and the button slid from under the pointer before mouseup, so the first click did nothing. Register and Forgot password were hit the same way. Leaving a field nobody has touched is no attempt to fill it in, so it now reports nothing. Submitting and typing still validate.
A voucher created by hand has no PurchasedWithOrderItem. LiteDB filters in memory, so one such voucher made the query throw and deleting any order returned a 500.
The edit validator lowercased the posted email without checking it, so saving a guest, who has none, or clearing a registered customer's email threw. The existing required rule now reports it, as it already does on create.
Comment on lines
+44
to
+47
| catch (Exception exc) | ||
| { | ||
| failures.Add(exc.Message); | ||
| } |
Comment on lines
+104
to
+107
| catch (Exception exc) | ||
| { | ||
| failures.Add(exc.Message); | ||
| } |
added 5 commits
October 31, 2026 18:13
Starting from installed BaseEntity classes left two ways to build a mapper during live traffic, on the dictionary every other request reads without a lock. API queries read their models through TableCollection, and no entity leads to those models. Installing a plugin writes its entities on the running site, before the restart that would have warmed them.
Preselecting the server's zone still turned "not configured" into a fixed zone on any save, so the store stopped following the host. It also depended on TimeZoneInfo.Local.Id being one of the listed options. An empty option keeps the value empty. A configured zone that resolves but is not listed, such as a Windows id on Linux, gets its own option so that saving does not change it.
Replacing the 500 with a required error still left a guest uneditable unless an address was invented for them. The email is now required unless the customer has none stored and is not being moved to the Registered group. The required message is also localized now: WithMessage covered only EmailAddress, so an empty field showed the FluentValidation default.
Table on LiteDB is LINQ over objects, so one address with no last name made the admin order list return a 500 when filtered by it.
An unticked box reports false and a group reports an empty list, neither of which counted as empty. Their message could still appear on mousedown, move the layout and lose the click, as it did for text fields. Submitting still reports them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type: bugfix
Issue
An end-to-end run of a fresh LiteDB install (storefront, checkout, about 180 admin scenarios) found six defects. There is no linked issue, so they are described here.
On LiteDB only
BsonMapperpublishes an entity mapper before filling its members and reads published mappers without a lock. Threads meeting a type for the first time together threwCollection was modifiedorMember Id not found ... ParentEntity, or silently read an object with fields missing. With 36 parallel storefront requests after a start, 2 to 7 returned a 500 on every start. Scheduled tasks failed too, and the admin dashboard failed with a single user.Database:Singleton=falsecould not work. Each request opened its ownLiteDatabaseon the same file, so all of them failed on the file lock.GetGiftVoucherQueryHandlerreadPurchasedWithOrderItem.Idwithout a null check, and LiteDB filters in memory.On any database
CustomerValidatorlowercased the posted email without checking it.Solution
One commit per defect:
LiteDBMapperWarmupbuilds the mappers during the single-threaded part of startup, for every entity with its nested types, base types and interfaces. It only runs withUseLiteDb.Singletonoption is gone; LiteDB is always one shared instance.GetGiftVoucherQueryHandlergets the same null checkGiftVoucherServicealready has.CustomerValidatorcompares the email only when one was posted, as the create path does. The existing required rule reports a missing one.ValidationProviderno longer reports a field nobody has touched when it loses focus. Submitting and typing still validate.app.runtime.bundle.jsis rebuilt.LiteDBMapperWarmupuses reflection, which.ai/constraints.mdlimits: LiteDB exposes mapper building only through the genericBsonMapper.Entity<T>(), and the entity types come fromITypeSearcher. It stays insideGrand.Data.Not changed:
_type, is not warmed up.Breaking changes
DatabaseConfig.Singletonis removed, along withDatabase:Singletoninappsettings.json. A value left in configuration is ignored. The only other value it allowed never worked.validation-provider.Testing
Run with
Database__UseLiteDb=trueand install with the LiteDB provider, with sample data. Restart once after the install.Collection was modifiedorMember Id not found. Verified: 0 of 540 failed over 10 starts.Database__Singleton=false. The application works normally.Automated: new tests are
LiteDBMapperWarmupTests,GetGiftVoucherQueryHandlerTests,DateTimeServiceExtensionsTestsand new cases inCustomerValidatorTests. On a fulldotnet test GrandNode.slnxrun after rebasing ondevelop, 23 tests failed inGrand.Business.Messages,Checkout,Marketing,AuthenticationandCatalog; the failing set varies between parallel runs. Run one by one, all five projects pass. The other 18 projects passed in the full run.Not tested: online payments, real e-mail delivery, the Store and Vendor panels in a browser, and running in a Linux container, where a server zone missing from the list would still show the first option.