-
Notifications
You must be signed in to change notification settings - Fork 2.1k
IStringLocalizerFactory.Create(type) Invocation Strangeness - n + 1 times, then never again #7887
Comments
Thanks for contacting us, @bgribaudo. |
Hi @kichalla, thank you for looking into this. Is there any chance this behavior could be changed? If I have a class that has 15 properties, it seems strange/wasteful for 16+ instances of the localizer to be created when one instance should work just fine. :-) It would also be really neat if the factory were invoked for each request. This would align its behavior with how IViewLocaizer calls the factory once per request (or, more accurately, probably once per view per request). On the second item: I'm working with a custom localizer factory that factors in the current request when it builds the localizer. This works fine for localizing text on pages, because IViewLocalizer doesn't cache localizers across requests. However, localization of field validation error messages doesn't work because of the caching (the request-specific localizer is cached across requests). If this behavior were changed to invoking the factory once per request, the localizer setup I'm using would work and ASP.Net Core would avoid keeping potentially every localizer used by the site cached for the lifetime of site's hosting processes. |
@bgribaudo, can you please describe the scenario you're having trouble with current behavior? We would like to understand the main problem, as there may be some other approach you should take. |
Hi @mkArtakMSFT! Sure thing! Context: I'm working with a custom JSON-powered string localizer and localizer factory. Scenarios:
Hope this helps! Thank you for being willing to chat about this. Hope you have a great day! |
Thanks for all the details, @bgribaudo. |
Hi @mkArtakMSFT! You're most welcome! Are you looking for a demo that shows the caching of the DataAnnotation-related localizers vs. how a fresh view localizer is created for each request or are you looking for something more in-depth than that? |
@bgribaudo We are specifically interested in the part where you see the problem of caching of DataAnnotation related localizers and since you are using a custom localizer factory with json file etc. |
@kichalla & @mkArtakMSFT, does https://github.com/bgribaudo/JsonStringLocalizerDemo provide what you're looking for? It contains a basic (demo-grade) implementation of a JSON-powered string localization setup. The readme file gives instructions for how to observe the seemingly wasteful creation of localizers on the first request and the difference in behavior between how |
Thanks, @bgribaudo. Sorry for the delay. @ryanbrandenburg, please look into the provided repro so we can discuss it later. |
Correct me if I'm wrong but the following are your problems:
My thoughts are as follows:
Does that address all your concerns? If not let me know where I'm off-base. |
I'm going to close this issue as |
Hi @ryanbrandenburg, Thanks for looking into this! I was traveling so I'm a bit delayed in responding. :-( I'd reopen but apparently I don't have rights to do that. In regards to my caching concerns, maybe it would help to describe a couple scenarios then contrast them with how ASP.Net Core 2.1 works. Scenario 1Factory.Create is called each time a localizer is needed. No caching is provided by ASP.Net Core. If caching is desired, implementing it is the responsibility of the localization developer. Pro: Developer has full control over caching (yes cache/no cache/cache lifespan). Scenario 2Factory.Create is called the first time a particular localizer is needed; the output returned is cached by ASP.Net Core. Any future requests for the same localizer are satisfied by the framework-provided cache; Factory.Create is not invoked. Pro: Developer doesn't need to implement caching. ASP.Net Core 2.1 (real life)Both of the above scenarios apply. The developer has all of the cons of both but only gets the intersection of their benefits. :-( During the first client request that triggers the need for a given localizer, Factory.Create may be called multiple times with a request for that localizer. If caching is important, the localizer developer must implement it. However, after that first client request, localizer caching is provided by the framework—whatever caching logic the developer may have put in Factory.Create is bypassed. The developer needed to put the effort into implementing caching (the con of scenario 1) but because the framework’s caching then takes over the developer is unable to manage/control that caching (the con of scenario 2). Any chance the current design could be changed? :-) Maybe when a localizer factory is registered, an argument could be passed specifying the scope of caching to apply to localizer requests. If this cache setting were respected on both the initial request and all subsequent requests, then we'd have the best of both worlds:
|
Related: #7636 |
If an
IStringLocalizerFactory
is registered and an action returns a view (return new View(new SomeModel());
) where the view's cshtml sets its model class (@model SomeModel
) then uses a field from that model in a form:IStringLocalizerFactory.Create(typeof(SomeModel))
is invoked at least n + 1 times, where n is the number of properties in the model. For example, ifSomeModel
has 3 properties, thenCreate(typeof(SomeModel))
will be called 4+ times.IStringLocalizerFactory.Create(typeof(SomeModel))
is never invoked again. When the site processes future requests that involvereturn new View(new SomeModel());
, theIStringLocalizer
previously returned byCreate(typeof(SomeModel))
is used.This behavior is puzzling. :-)
The first time the view is returned, it seems like
Create(typeof(SomeModel))
should be called one time, regardless of the number of fields inSomeModel
. Creatingn + 1
or more instances seems wasteful.Then, each subsequent time the view is returned, it seems advantageous for
Create(typeof(SomeModel))
to be invoked one time to get theIStringLocalizer
to use for that request (that is, don't cache theIStringLocalizer
used for the previous request; instead, use the factory to create a new one just for this request). This would enable the factory to create a string localizer that's appropriate for the request (for example, if the localizer is database-powered, maybe the localizer should be loaded fresh from the database on each request) and avoid the need to keep every string localizer that's ever been used cached in memory.To sum it up, it seems like the
IStringLocalizer
forSomeModel
isn't cached when it should be (the initial return) but then is cached when maybe it shouldn't be (subsequent returns). :-)Is this behavior expected/intended? Is there a chance it could be changed to 'use
Create()
to create one instance per action invocation' instead of 'useCreate()
to create a lot of instances then cache one forever'?Demo - https://github.com/bgribaudo/StringLocalizerFactoryInvocationDemo
The text was updated successfully, but these errors were encountered: