strange problem here. On local development in asp.net webforms (4.5 / 4.7) I am finding httpruntime.Cache always null even when properly set. I attempted it on another iis express workstation and found the same behavior, even with a tester single page web page. That same page in production IIS 7.5 works and is storing and delivering from cache. The code specifically is below, but I have tried a tester storing a simple string in httpruntime.Cache.
var cache = System.Runtime.Caching.MemoryCache.Default;
var luCacheKey = "lu_" + dsName;
var ic = HttpRuntime.Cache.Get(luCacheKey) as ICollection;
if (ic == null) {
and from the tester
var item = HttpRuntime.Cache.Get("x");
if (item == null)
{
HttpContext.Current.Cache.Insert("x", "test" , null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration);
Response.Write("added to cache<br>");
}
else {
Response.Write("already in cache");
}
So, I am wondering if there is something perhaps in web.config that I could look at or is this expected IIS express behavior? Note, System.runtime.Caching does work properly.
var cache = System.Runtime.Caching.MemoryCache.Default;
var ic = cache[luCacheKey] as ICollection;
if (ic == null)
{
var filterCriteria = new BinaryOperator("LookupGroup", dsName, BinaryOperatorType.Equal);
var lookups = xpoSession.GetClassInfo(typeof(Lookups));
ic = xpoSession.GetObjects(lookups, filterCriteria, new SortingCollection(), 0, 0, false, false);
var cachePolicy = new System.Runtime.Caching.CacheItemPolicy() { AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(30) };
cache.Add(new System.Runtime.Caching.CacheItem(luCacheKey, ic), cachePolicy);
You incorrectly add your object to the cache.
Instead of DateTime.Now follow the docs and put DateTime.UtcNow. This resolves a common issue where your machine is in a "non-zero" time zone which prevents the inner logic of the cache to manage your expirations correctly.
From the docs
To avoid possible issues with local time such as changes from standard time to daylight saving time, use UtcNow rather than Now for this parameter value.
https://msdn.microsoft.com/en-us/library/4y13wyk9(v=vs.110).aspx
Adding more information as follow up on why the behavior may change between servers.
This change in behavior may be caused by having .NET 4.7 installed on the machine. The article linked below says that Microsoft will fix this in the next version of .NET and in the next hotfix.
Quoting parts of the Microsoft page:
Symptoms:
Assume that you have Microsoft .NET Framework 4.7 installed on a
computer. When you try to insert items into the Cache object by using
the Cache.Insert (string, object, CacheDependency, DateTime, TimeSpan)
Insert overload method, you may notice that the inserted Cache items
expire much earlier or later than the specified DateTime (expiration
time).
Cause:
The internal implementation of System.Web.Caching.Cache uses
Coordinated Universal Time (UTC) time-stamp for absolute expiration.
But this particular Cache.Insert (string, object, CacheDependecy,
DateTime, TimeSpan) Insert overload method does not make sure whether
the expiration time is converted to UTC. Therefore, expiration for
items that are inserted into the Cache object by using this overload
will occur earlier or later than expected, depending on the computer
time zone difference from Greenwich Mean Time (GMT).
Workaround:
The temporary workaround for this issue is to use either the Cache.Add method or a different Cache.Insert overload method.
Resolution:
This issue will be fixed in the next version of the .NET Framework, and will also be available in the next hotfix for the .NET Framework 4.7.
References:
https://support.microsoft.com/en-us/help/4035412/fix-expiration-time-issue-when-you-insert-items-by-using-the-cache-ins
http://vimvq1987.com/2017/08/episerver-caching-issue-net-4-7/
Related
Put simply, I have a dot net web app and it needs to record the users Timezone information (in order to send out the correct time inside emails).
using NodaTime.TimeZones;
var winmap = TzdbDateTimeZoneSource.Default.WindowsMapping.MapZones
.FirstOrDefault(x => x.TzdbIds.Contains(tzinfo));
if (winmap == null) throw new Exception("Invalid timezone");
NodaTime 2.4.8
https://nodatime.org/2.4.x/api/NodaTime.TimeZones.TzdbDateTimeZoneSource.html#NodaTime_TimeZones_TzdbDateTimeZoneSource_WindowsMapping
The "Asia/Kolkata" timezone doesn't seem to exist, and I'm not sure what is needed to make it work. Is there a better way to achieve this?
The problem is that the Windows/TZDB mapping file (example) doesn't contain "Asia/Kolkata", it contains "Asia/Calcutta".
Accounting for this in user code is relatively tricky, which is why in NodaTime 3.0 we introduced TzdbDateTimeZoneSource.TzdbToWindowsIds.
After updating to 3.0, you can use:
if (!TzdbDateTimeZoneSource.Default.TzdbToWindowsIds.TryGetValue(tzinfo, out var windowsZoneId))
{
throw new Exception($"Unmapped time zone ID '{tzinfo}'");
}
// Use windowsZoneId here
If you really need to stick with 2.4.8, you could canonicalize both tzinfo and all the entries in TzdbDateTimeZoneSource.Default.WindowsMapping.MapZones.TzdbIds, but that will be generally worse.
(The update from 2.4.8 to 3.0.0 should be seamless for most users. It's a breaking change primarily due to removing binary serialization, which I hope you're not using...)
Using the older "Windows.Azure.ServiceBus" library, I am able to setup a SqlFilter that takes as its parameters a TimeSpan, but when I try the same using the "Microsoft.Azure.ServiceBus" library it fails with the following error:
Object is not of supported type: TimeSpan. Only following types are
supported through HTTP: string,int,long,bool,double,DateTime
What I am trying to do:
I want to have 2 subscriptions on my topic (highPriority, normalPriority)
Messages have a user property called "StartDate"
If StartDate <= 1 day, then it should go to highPriority subscription, else it should go to normalPriority. [i.e (StartDate - sys.EnqueuedDateTimeUtc) <= 24 hours].
Code that works (when using the older .net-framework Windows.Azure.ServiceBus package):
SqlFilter highMessagesFilter =
new SqlFilter("(StartDate-sys.EnqueuedTimeUtc) <= #TimeSpanImmediateWindow");
highMessagesFilter.Parameters.Add("#TimeSpanImmediateWindow", TimeSpan.FromDays(1));
var subscription = SubscriptionClient.CreateFromConnectionString(connectionString,topicName, subName1);
subscription.RemoveRule(RuleDescription.DefaultRuleName);
subscription.AddRule(new RuleDescription()
{
Name = RuleDescription.DefaultRuleName,
Filter = highMessagesFilter,
Action = new SqlRuleAction("set priorityCalc = (StartDate-sys.EnqueuedTimeUtc)")
});
Where as, this code (using: Microsoft.Azure.ServiceBus) doesnt work:
var filter = new SqlFilter("(StartDate-sys.EnqueuedTimeUtc) <= #TimeSpanHoursImmediateWindow");
filter.Parameters.Add("#TimeSpanHoursImmediateWindow",TimeSpan.FromDays(1));
var ruleDescription = new RuleDescription
{
Filter = filter,
Action = new SqlRuleAction(#"
SET HighPriority = TRUE;
SET Window = StartDate - sys.EnqueuedTimeUtc
"),
Name = RuleDescription.DefaultRuleName,
};
await managementClient.UpdateRuleAsync(topicPath,subscriptionName,ruleDescription);
The above code throws the following error:
Object is not of supported type: TimeSpan. Only following types are
supported through HTTP: string,int,long,bool,double,DateTime
If instead of managementClient.UpdateRuleAsync, I try using the following code:
var subClient = new SubscriptionClient(connectionString, topicPath, subscriptionName);
await subClient.RemoveRuleAsync(RuleDescription.DefaultRuleName);
await subClient.AddRuleAsync(ruleDescription);
It fails, with the following error (ServiceBusException):
Message The service was unable to process the request; please retry
the operation. For more information on exception types and proper
exception handling, please refer to
http://go.microsoft.com/fwlink/?LinkId=761101
The microsoft link is to a list of exceptions, and FilterException, but its not!
Here is the stack trace of the 2nd exception:
at
Microsoft.Azure.ServiceBus.Amqp.AmqpSubscriptionClient.OnAddRuleAsync(RuleDescription
description) in
C:\source\azure-service-bus-dotnet\src\Microsoft.Azure.ServiceBus\Amqp\AmqpSubscriptionClient.cs:line
132 at
Microsoft.Azure.ServiceBus.SubscriptionClient.AddRuleAsync(RuleDescription
description) in
C:\source\azure-service-bus-dotnet\src\Microsoft.Azure.ServiceBus\SubscriptionClient.cs:line
499 at UserQuery.Main() in
C:\Users\XXXX\AppData\Local\Temp\LINQPad6_quhgasgl\niqvie\LINQPadQuery.cs:line
82
So my questions are:
Can I use TimeSpan parameters using .net standard library? or will I have to use the older .net framework library if I wanted to use TimeSpans.
Is there a better way to implement what I am trying to do, a way that would work with the newer .net standard library? (FYI: I thought about sending the calculation as the parameter (decimal) and then the parameter would be a double, instead of a TimeSpan). And in fact, thats what I might end up doing.
If the library is throwing an exception stating that TimeSpan is not a supported type, then it's pretty much what you have. Note that the .NET Standard client has two implementations, the ManagementClient and some operations via entity clients, such as subscription client. The latter is implemented using AMQP. ManagementClient is entirely based on HTTP. While it would be ideal to use the AMQP implementation, it's incomplete. I would recommend relying on the ManagementClient. This is likely the reason why modifying rules using a subscription client is throwing an exception.
Regarding a better way - your idea sounds right. As long as it's not a type that the new client doesn't accept. Also, you can raise an issue with the library team at https://github.com/Azure/azure-sdk-for-net/issues if you'd like to know the reason why TimeSpan is no longer supported.
I want to develop a new custom session state provider or use an existing (distributed caching, sql ...). Our main website renders more than 10 000 000 visits per day.It is really important for us to provide an easy rollback/switch in case of an error or a performance hit. Change web.config is not optimal because we have more than 20 front end servers. Our idea is to switch between session provider (from our custom to InProc) with a simple config in database.
Is it possible to have multiple session state providers or easily switch between providers ?
i found here http://netpl.blogspot.fr/2007/06/wrapped-inprocsessionstatestore.html, a solution for having a generic wrapper, but it does not seems quite robust.
Thanks,
Just for future references, it is not possible to dynamically change the session state provider.
it is not.. totally true.. you can do it like this using Reflection:
var privateFieldFlags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;
//Get session state section
var sessionStateSection = ConfigurationManager.GetSection("system.web/sessionState") as SessionStateSection;
var values = typeof(ConfigurationElement).GetField("_values", privateFieldFlags).GetValue(sessionStateSection);
var entriesArray = values.GetType().BaseType.GetField("_entriesArray", privateFieldFlags).GetValue(values);
//Get "Mode" entry (index: 2)
var modeEntry = (entriesArray as System.Collections.ArrayList)[2];
var entryValue = modeEntry.GetType().GetField("Value", privateFieldFlags).GetValue(modeEntry);
//Change entry value to InProc
entryValue.GetType()
.GetField("Value", privateFieldFlags)
.SetValue(entryValue, System.Web.SessionState.SessionStateMode.InProc);
references:
Dynamic session state provider
http://www.answerandquestion.net/questions/4447903/dynamic-session-state-provider
I have a really strange problem and I'm completely puzzled.
I have a piece of code that parses some data and stores the result in our webserver's HttpRuntime.Cache using the Insert method. This is stored for 10 seconds. There seem to be some problems so I created a test page that retrieves a simple object from the cache and displays if it is null or not. To add the object to the cache, I use:
HttpRuntime.Cache.Insert(CHECK_KEY, new object(), null, DateTime.Now.AddSeconds(10), System.Web.Caching.Cache.NoSlidingExpiration);
In a test page, I try to retrieve the object:
var isInCache = this.cacheService.Get<object>(CHECK_KEY) != null;
and the method of the cacheService is:
public T Get<T>(string key)
{
return (T)HttpRuntime.Cache.Get(key);
}
Now the strange part. If I call a URL that calls the 'Insert' method, and go to my test page to retrieve it, the value of isInCache is false in about 99% of the time. Sometimes it works correctly for the whole 10 seconds (e.g. I refresh my test page every second and I get true 10 times) but again, most of the time it just returns false.
Now, when I keep F5 pressed, I sometimes see true in my output, in the blink of an eye, which means that the key CAN be found! This is not some browser cache, because it will only flash true intermittetly for the 10 seconds of cache duration, after which is it will only display false (which is logical, since the key is expired). So my question is:
WHY will retrieving a simple object from the cache fail most of the time?
There are other items in the cache (on the same test page) that do get retrieved, just not that object.
To make things worse, this (of course!) works flawlessly on my local machine, on the test machine, just not production. I'm pretty clueless. Please help :-)
EDIT:
Ok so I'm now testing in two different browsers, IE9 and Chrome... and IE9 is correctly showing the items in the HttpRuntime.Cache but Chrome is NOT. It shows always false and no other cached data, except when keeping F5 pressed it will occassionally show it. Since when is HttpRuntime.Cache browser dependant???
Extra edit: IE9 shows no more cached data. So while it can differ across browsers, it's not that IE will always work and chrome not... it differs.
EDIT2:
So I'm passing the variables to my view using ViewData:
ViewData["machineName"] = machineName;
ViewData["isInCache"] = isInCache;
ViewData["A"] = A;
ViewData["B"] = B;
Machinename comes from Server.MachineName, isInCache is the object, variable A is not from the HttpRuntime.Cache, variable B does, which is also intermittently not present.
After much debugging and thought, it appeared that the hosting provider had the 'Maximum Worker Processes' in IIS 7 Application pool settings to a value larger than 1. The HttpRuntime.Cache is not shared in a web farm, thus it could well be that I hit the 'wrong' instance which did not have the object cached. Continuously pressing F5 would have me occassionaly hit the instance which did have the value cached.
private function loadGallery():void {
theSend.url = "http://localhost/userMana/file.xml";
theSend.send();
}
I am calling this XML in Flex Tree and its works fine, but when after an update in XML it does not update back in my TREE unless i compile my flex builder again.
I usually do this by appending the time to the XML url:
var now:Date = new Date();
theSend.url = "http://localhost/userMana/file.xml?" + now.getTime();
theSend.send();
non-cached every time.
You could try flushing your local cache after you update. Stopping and starting "World Wide Web Publishing Service" (which is what it's called under Vista; XP will have a different name) should do it.