Here is my issue. I am working on an E-commerce solution that is deployed to multiple European countries. We persist all exceptions within the application to SQL Server and I have found that there are records in the DB that have a DateTime in the future!
We define the culture in the web.config, for example pt-PT, and the format expected is DD-MM-YYYY.
After debugging I found the issue with these 'future' records in the DB is because of Callback methods we use. For example, in our Caching architecture we use Callbacks, as such -
CacheItemRemovedCallback ReloadCallBack = new CacheItemRemovedCallback(OnRefreshRequest);
When I check the current threads CultureInfo, on these Callbacks it is en-US instead of pt-PT and also the HttpContext is null. If an exception occurs on the Callback our exception manager reports it as MM-DD-YYYY and thus it is persisted to SQL Server incorrectly.
Unfortunately, in the exception manager code, we use DateTime.Now, which is fine if it is not a callback. I can't change this code to be culture specific due to it being shared across other verticals.
So, why don't callbacks into ASP.Net maintain context? Is there any way to maintain it on this callback thread? What are the best practices here?
Thanks.
DateTime.Now does not depend on culture. Are you saving it as a string? ToString does depend on culture.
In fact, as a general rule, try to store things in the database in a manner not dependent on culture. This is especially important in a web application, where the culture for each request may be different from the next. In order to be able to "compare apples to apples", you need to ignore culture.
You should separate the DateTime (UTC recommended) from the rest of the error description in your logging management AND also store the culture associated with the log entry. Then you can reassemble the info with these 3 pieces independently.
The cache callback comes on a threadpool thread which in your case always has en-US and there's no HttpContext. You should be able to retrieve the culture by associating the removed cache item with your callback logic.
Related
We have a web app that displays data from a couple of different web apps. In each of those web apps, the culture is configurable (it is set by a company admin at the app level for all users, not per user).
In our web app, we need to display the data from each of the other web apps, in the culture configured for that app. The only good news is that currently we don't display "mixed" data from both apps, in a single view. And generally, a controller and all of its actions will only work with data from a single app (meaning that all actions from that controller will use the same culture).
There are several things I'm struggling with:
Does .NET Core really not honor setting culture like in the old days? I've tried it but it doesn't work (unless I'm setting it in the wrong place in the pipeline).
Thread.CurrentThread.CurrentCulture = firstWebAppCultureInfo;
Thread.CurrentThread.CurrentUICulture = firstWebAppCultureInfo;
Does it really only check these to find and set the culture?
QueryStringRequestCultureProvider
CookieRequestCultureProvider
AcceptLanguageHeaderRequestCultureProvider
I know I could write a custom provider, but that wouldn't seem to help me. We already get the various culture settings for the apps, from a database and store them in session.
My first thought was to write out a Culture Cookie every time the user hits a controller. I'd read the culture from session and write the cookie. But there are a couple of issues I see with this.
Issue 1: If I write the cookie out in the controller action, it doesn't "take affect" the first time it is changed from one culture to another. Obviously the cookie has already been read and the culture has already been set for that request. I'm too late in the pipeline to do that.
I tried writing the cookie out in the constructor of the controller (or constructor of a base controller) but HttpContext is not yet set at that point (it is null).
I have worked a bit with Filters and I will write an Action Filter that writes the cookie to see if that works, but see Issue 2.
Issue 2: Another issue would surface if the user opened multiple instances of their browser. The last culture written will "win" I suspect. So if a user is displaying data in Culture fr-FR in one browser and en-US in another instance of a browser and navigates to a controller/view that writes that en-US cookie, and then from the other browser instance navigates to a page that is supposed to use the fr-FR culture, the user would get the en-US setting from the cookie and that's not acceptable.
I don't really want to use the QueryStringRequestCultureProvider, because having that show up in the url is ugly.
I'm definitely not an expert in this area.
What am I missing?
How would you all implement this?
Thanks in Advance.
UPDATE
I created an ActionFilter and TypeFilterAttribute that allows me to pass a parameter into the ActionFilter. Using the parameter I can decide which key to read from Session to give me the culture setting I've stored for each web app.
From there I can mark each controller with that attribute and the appropriate parameter to tell the action filter what culture to use (remember we don't mix data, so each controller displays views that show data from only one of the two web apps, not both).
In the ActionFilter I can look up the culture string from session and do the following and it works:
var ci = new CultureInfo(this.cultureSetting);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
While that is encouraging, it feels hacky. So please comment on the solution...
I'd like to find a way to do all of this in a BaseController or somewhere, so I wouldn't need to put attributes on all of our controllers.
But whatever I've tried in a base controller seems like it is either:
a) too late in the pipeline to set the culture for that request, or
b) too early in the pipeline to get access to httpcontext.
Thoughts? Ideas?
UPDATE 2
I guess overriding OnActionExecuting in my base controller lets me do the same things as the ActionFilter...
So I guess I have a solution, but if anyone has a better idea, I'm listening.
We decided to go with an ActionFilter for our solution, so we can mark Controllers and/or Actions with an Attribute that indicates which app we are getting data from, so we can apply that app's culture setting...
I am in a situation where requirement is to keep an application level object in web api which can be accessed by all requests. I know one can use HttpContext.Current but that is not required since HttpContext is only for the liftime of request. I need a solution where i can keep an object that all requests can access and update as required.
Use a static class to hold your application level objects. static classes and static data members are created once for the application lifetime and all ASP.NET requests can access them.
I learnt it the hard way. Some time back, I mistakenly created a static field to hold customer-specific database connection string, in a ASP.NET Web API project and it became a mess. On each customer's login it was being set (overridden) in the code and the requests from the previously logged customers were using this newly set static SQL connection string for their queries. It was an embarrassing situation when customer's inadvertently saw each other's data.
You could use SessionState (per session).
I.e.
Session["YourDataKey"] = ApplicationLevelObject;
And then check the session state variable on each request that requires it.
However if you require the object for longer, I.e. every single user session, then I would suggest persisting your object to a database. You could use an ORM such as Entity Framework.
Cheers
I expect that if controller has attribute SessionStateBehavior.ReadOnly then I can't change session variables inside this controller
but I can change values.
I try this code
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class GLobalController : Controller
{
public ActionResult Index()
{
Session["xxx"] = DateTime.Now.ToString();
return View();
}
see Writing to a read only session in MVC 3+
That post claims the behavior is inconsistent.
I am definitely able to write to Session in Controllers using ReadOnly.
I Would treat it like this:
Required means you are requesting a exclusive lock on Session (i.e. no parallel processing of requests for the same sessionID)
ReadOnly means you are requesting a non-exclusive lock on Session (i.e. your request still has to wait for requests with an exclusive lock to finish, but you can process requests with non-exclusive locks in parallel. However it is up to you to ensure that your code doesn't write to Session. It's not necessarily enforced by the framework)
I realize this is counter to http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstatebehavior.aspx
Read-only session state is enabled for the request. This means that session state cannot be updated.
but it seems you in fact can update session state under some scenarios.
According to Patrick Y. Ng (Software Engineer at Microsoft) who designed and developed the Session State engine of ASP.NET:
Even though EnableSessionState is marked as ReadOnly, in InProc state the user can still modify the session. The only difference is that the session will not be locked during the request. This limitation is by-design. And I'm sorry that it’s not documented in MSDN.
There is much more useful information about session state in this post. It is really worth reading.
This is just my interpretation:
I see that you can add to Session during the action method - after all Session is just a dictionary really. However the session is not saved at the end.
It does seem like it ought to throw an exception, but perhaps since this feature came later to the framework they decided against checking every time.
Results may vary also depending upon what session state storage you are using (inproc / sql server).
Is there any way to use caching in ASP.Net except SQL Server second level cache. As it is the first time to work with caching I want any way with an example. I have found that NHibernate implements this but we are using .netTiers as an application framework.
The Session cache seems to be the appropriate caching mechanism here. The Session cache is a fault-tolerant cache of objects.
Inserting an object
Session["Username"] = "Matt";
Reading an object
string username = (string)Session["Username"];
Removing an object
Session.Remove("Username");
I say fault-tolerant because if the value with the key you specify doesn't exist in the Session cache, it will not through an exception, it will return null. You need to consider that when implementing your code.
One thing to note, if you are using Sql Server or State Server, the objects you can put in the cache need to be serializable.
Memcached is also a very good way to go, as it is very flexible. It is a windows service that runs on any number of machines and your app can talk to the instances to store and retrieve from the cache. Good Article Here
As per title. I want to be able to save some data in a cache object but this object must be available to all users/sessions and can expire.
What is the best method to achieve this in a asp.net web app?
HttpContext.Current is available to all pages, but not necessarily to all threads. If you try to use it inside a background thread, ThreadPool delegate, async call (using an ASP.NET Async page), etc., you'll end up with a NullReferenceException.
If you need to get access to the cache from library classes, i.e. classes that don't have knowledge of the current request, you should use HttpRuntime.Cache instead. This is more reliable because it doesn't depend on an HttpContext.
HttpContext.Current.Cache will be present, but Current should only be used if you cant get to your context member.
Also to answer your second question, yes, the Cache object is global to the application.
Here's a good intro to caching...
How to cache in ASP.NET by using Visual C# .NET
and...
Caching with ASP.NET . Don't skip part 2, "Data Caching"