Is there anything like 'update time' for ASP.NET-MVC application? - c#

My web-site returns information for items which it takes from disk (involving some logic located in the controller, so it is not just static assets). I tried to optimize it by returning 304 for items which are not changed, by getting file write time for the corresponding item. Now, after I update the code, my application still thinks that an item is not updated and returns 304 - it does not realize that application code is changed so the result would be different. Because of that users do not see the update immediately, only after they get rid of their cache. I would like to solve that problem by checking not only 'item update time' but also 'application update time'. Is there a way to get something like time when application was updated? By this time I would like to see kind of maximum of update times of all application files.
UPD:
As asked for example code, here is slightly simplified version:
public static DateTime? LastKnownDate(this HttpRequestBase request)
{
if (!string.IsNullOrEmpty(request.Headers["If-Modified-Since"]))
{
var provider = CultureInfo.InvariantCulture;
DateTime date;
if (DateTime.TryParse(
request.Headers["If-Modified-Since"],
provider,
DateTimeStyles.RoundtripKind,
out date)) return date;
}
return null;
}
public ActionResult Test(int id)
{
var path = #"C:\data\" + id;
var file = new FileInfo(path);
if (!file.Exists) return HttpNotFound();
var date = Request.LastKnownDate();
if (date != null && date >= file.LastWriteTimeUtc)
{
return Response.NotModified();
}
Response.AddHeader("Last-Modified", file.LastWriteTimeUtc.ToString("o"));
return File(path, "application/octet-stream");
}

I think you need something like HTTP conditional GET. More details in the spec.
This is how you can do that in ASP.NET : http://optimizeasp.net/conditional-get
Also take a loot at: Improving Performance with Output Caching

Related

C# OdataV4 Microsoft.OData.Client.DataServiceContext handle Timezone

As stated in the title, i need to know how i can set the timezone to use in an OdataV4 Client.
In our Database we are storing DateTime-Values as GMT+1 since ever.
For quite some time we were using a WebApi which was working on OdataV3.
As long as we were on OdataV3 we hadn't had issues related to TimeZones.
After switching to OdataV4, we are now facing some real, almost showstopping issues regarding the fact, that even if we set the TimeZone on the server to GMT+1, the client is now converting DateTimes to UTC.
This is the output from the Server:
As you can see, the times are identical. The +02:00 is related to summertime!
Now for whatever reason, the client displays this timestamp while debugging:
I was trying to find a method, which tells the DataServiceContext to not use UTC but couldnt find any. The closest i got was this post but it seems not applicable to me.
There is quite a lot code, dealing with DateTimes and we cannot afford to refactor this all.
Also switching the Server back to UTC is not an option since every application has to be adjusted then.
Question
How can i set the DataServiceContext or an impacting component (JsonSerializer f.e.) to a TimeZone of my choice?
First thing that I check is that the OData configuration is set to UTC on the server, the following is a standard Registration method I use in my OData v4 APIs, I'll leave the other entries in there to help you identify where in the pipeline to implement the call to SetTimeZoneInfo
public static void Register(HttpConfiguration config)
{
// To enable $select and $filter on all fields by default
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
config.SetDefaultQuerySettings(new Microsoft.AspNet.OData.Query.DefaultQuerySettings() { EnableCount = true, EnableExpand = true, EnableFilter = true, EnableOrderBy = true, EnableSelect = true, MaxTop = null });
config.AddODataQueryFilter(new EnableQueryAttribute());
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
// Set the timezone to UTC
config.SetTimeZoneInfo(System.TimeZoneInfo.Utc);
// Register the odata routes and other config
...
}
The above code is specifying UTC for all DateTimes that do not specify a timezone. The following variations show how you could set other timezones:
config.SetTimeZoneInfo(System.TimeZoneInfo.Utc);
config.SetTimeZoneInfo(System.TimeZoneInfo.Local);
config.SetTimeZoneInfo(System.TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time"));
If you wanted to affect the serialiser directly then you will need to register your own customised ODataSerializerProvider as the OData v4 framework uses this to deserialise the http request. That can be pretty involved, so try the simple option first.
I think this is a workaround that could perhaps solve your issue:
It does not override deserialization but it fixes the dates by modifying the response.
The response is deserialized to UTC by default, and this will convert all DateTime objects to the local timezone(you can modify the code to change to any timezone you want). I assume your server and clients are in the same timezone.
You can set the server timezone to whatever you want, but I assume it will be +1 (most likely local).
/// <inheritdoc />
public class YourDataServiceContext: DataServiceContext
{
/// <inheritdoc />
protected YourDataServiceContext(Uri uri) : base(uri)
{
//add any code if you need here
Configurations.ResponsePipeline.OnEntityMaterialized(ConvertDatesToLocalZone);
}
private void ConvertDatesToLocalZone(MaterializedEntityArgs obj)
{
var entity = obj?.Entity;
if (entity == null) return;
var props = entity.GetType()
.GetProperties()
.Where(it =>
it.PropertyType == typeof(DateTime)
|| it.PropertyType == typeof(DateTime?));
foreach (var prop in props)
{
//get value and check if it isn't null
var value = prop.GetValue(entity);
if (!(value is DateTime oldValue)) continue;
//check if property has setter
var setMethod = prop.SetMethod;
if (setMethod == null) continue;
//convert to local time
value = oldValue.ToLocalTime();
//set the new value
setMethod.Invoke(entity, new[] { value });
}
}
}

Add expiration to C# windows application

i am trying to make make app expire after some days using the registry option, i have successfully written and read from registry, my issue is to check for expiration, below is my code:
regKey = Registry.CurrentUser.OpenSubKey("Systemfiles");//subkeyname
if (regKey == null)
{
regKey = Registry.CurrentUser.CreateSubKey("Systemfiles");
regKey.SetValue("tere", Encrypt("4/16/2017"));
}
else
{
regKey = Registry.CurrentUser.OpenSubKey("Systemfiles");//subkeyname
string decryptDateValue = Decrypt(regKey.GetValue("tere").ToString()); //Keyname
DateTime mainDate = Convert.ToDateTime(decryptDateValue);
DateTime expiringDate = mainDate.AddDays(1);
if (mainDate > expiringDate)
{
expire = true;
}
else
{
//Continue execution
}
}
from my code i assumed the user's first run is the 04/16/2017, i also assumed i want the user to run the application for one day, which supposed to expire on the 04/17/2017, meaning if the user tries to start the application after 04/17/2017 the if part should execute, but i am not really getting it right, the else part always execute, i will appreciate a better way of doing it. Thanks
You've got this in your code:
DateTime expiringDate = mainDate.AddDays(1);
if (mainDate > expiringDate)
So,expiringDate would always be bigger than mainDate (one day exactly).
What you want to check is the actual Date, so it should be:
if (DateTime.Now>expiringDate)

How to detect if WSFederationSession has expired or ended?

I have a several methods in controller:
public ActionResult Issue()
{
var message = WSFederationMessage.CreateFromUri(HttpContext.Request.Url);
// sign in
var signinMessage = message as SignInRequestMessage;
if (signinMessage != null)
{
return ProcessWSFederationSignIn(signinMessage, ClaimsPrincipal.Current);
}
// sign out
var signoutMessage = message as SignOutRequestMessage;
if (signoutMessage != null)
{
return ProcessWSFederationSignOut(signoutMessage);
}
return View("Error");
}
And the most valuable for me in this question:
private ActionResult ProcessWSFederationSignOut(SignOutRequestMessage message)
{
FederatedAuthentication.SessionAuthenticationModule.SignOut();
var mgr = new SignInSessionsManager(HttpContext, _cookieName);
// check for return url
if (!string.IsNullOrWhiteSpace(message.Reply) && mgr.ContainsUrl(message.Reply))
{
ViewBag.ReturnUrl = message.Reply;
}
return View("Signout");
}
All works fine, but, there are interesting moment.
This thing works in both cases, if I ended session by myself, or session simply expired. Its fine but actually, I need to tell the difference between those cases, write in ViewBag something like "You are singed out" or "Session expired" depends on result and show it oy the View.
Is there are some kind of way to detect session expired situations or should it be something different?
P.S Sorry for my bad English.
Since you changed the topic I will update my answer. I haven't used WSFederatinSession but maybe you could store the inf about how session ended (in a cookie for example) and during the next request (in a global asax for example) read this inf and do what you want to do.

Caching attribute for method?

Maybe this is dreaming, but is it possible to create an attribute that caches the output of a function (say, in HttpRuntime.Cache) and returns the value from the cache instead of actually executing the function when the parameters to the function are the same?
When I say function, I'm talking about any function, whether it fetches data from a DB, whether it adds two integers, or whether it spits out the content of a file. Any function.
Your best bet is Postsharp. I have no idea if they have what you need, but that's certainly worth checking. By the way, make sure to publish the answer here if you find one.
EDIT: also, googling "postsharp caching" gives some links, like this one: Caching with C#, AOP and PostSharp
UPDATE: I recently stumbled upon this article: Introducing Attribute Based Caching. It describes a postsharp-based library on http://cache.codeplex.com/ if you are still looking for a solution.
I have just the same problem - I have multiply expensive methods in my app and it is necessary for me to cache those results. Some time ago I just copy-pasted similar code but then I decided to factor this logic out of my domain.
This is how I did it before:
static List<News> _topNews = null;
static DateTime _topNewsLastUpdateTime = DateTime.MinValue;
const int CacheTime = 5; // In minutes
public IList<News> GetTopNews()
{
if (_topNewsLastUpdateTime.AddMinutes(CacheTime) < DateTime.Now)
{
_topNews = GetList(TopNewsCount);
}
return _topNews;
}
And that is how I can write it now:
public IList<News> GetTopNews()
{
return Cacher.GetFromCache(() => GetList(TopNewsCount));
}
Cacher - is a simple helper class, here it is:
public static class Cacher
{
const int CacheTime = 5; // In minutes
static Dictionary<long, CacheItem> _cachedResults = new Dictionary<long, CacheItem>();
public static T GetFromCache<T>(Func<T> action)
{
long code = action.GetHashCode();
if (!_cachedResults.ContainsKey(code))
{
lock (_cachedResults)
{
if (!_cachedResults.ContainsKey(code))
{
_cachedResults.Add(code, new CacheItem { LastUpdateTime = DateTime.MinValue });
}
}
}
CacheItem item = _cachedResults[code];
if (item.LastUpdateTime.AddMinutes(CacheTime) >= DateTime.Now)
{
return (T)item.Result;
}
T result = action();
_cachedResults[code] = new CacheItem
{
LastUpdateTime = DateTime.Now,
Result = result
};
return result;
}
}
class CacheItem
{
public DateTime LastUpdateTime { get; set; }
public object Result { get; set; }
}
A few words about Cacher. You might notice that I don't use Monitor.Enter() ( lock(...) ) while computing results. It's because copying CacheItem pointer ( return (T)_cachedResults[code].Result; line) is thread safe operation - it is performed by only one stroke. Also it is ok if more than one thread will change this pointer at the same time - they all will be valid.
You could add a dictionary to your class using a comma separated string including the function name as the key, and the result as the value. Then when your functions can check the dictionary for the existence of that value. Save the dictionary in the cache so that it exists for all users.
PostSharp is your one stop shop for this if you want to create a [Cache] attribute (or similar) that you can stick on any method anywhere. Previously when I used PostSharp I could never get past how slow it made my builds (this was back in 2007ish, so this might not be relevant anymore).
An alternate solution is to look into using Render.Partial with ASP.NET MVC in combination with OutputCaching. This is a great solution for serving html for widgets / page regions.
Another solution that would be with MVC would be to implement your [Cache] attribute as an ActionFilterAttribute. This would allow you to take a controller method and tag it to be cached. It would only work for controller methods since the AOP magic only can occur with the ActionFilterAttributes during the MVC pipeline.
Implementing AOP through ActionFilterAttribute has evolved to be the goto solution for my shop.
AFAIK, frankly, no.
But this would be quite an undertaking to implement within the framework in order for it to work generically for everybody in all circumstances, anyway - you could, however, tailor something quite sufficient to needs by simply (where simplicity is relative to needs, obviously) using abstraction, inheritance and the existing ASP.NET Cache.
If you don't need attribute configuration but accept code configuration, maybe MbCache is what you're looking for?

MethodInfo.Invoke sometimes returns null and sometimes returns value

I'm working on an asp.net MVC application.
I have a class that wraps a repository that fetches data from a db using simple linq statement. I've written a decorator class to add caching logic (using caching application block).
since I have several methods that I want to decorate, and the logic is all the same for each one (check if exists in cache, if not invoke real getter and store in cache), I wrote something like this:
a helper method that does the common logic of checking if exists in cache and so on:
public object CachedMethodCall(MethodInfo realMethod, params object[] realMethodParams)
{
object result = null;
string cacheKey = CachingHelper.GenereateCacheKey(realMethod, realMethodParams);
// check if cache contains key, if yes take data from cache, else invoke real service and cache it for future use.
if (_CacheManager.Contains(cacheKey))
{
result = _CacheManager.GetData(cacheKey);
}
else
{
result = realMethod.Invoke(_RealService, realMethodParams);
// TODO: currently cache expiration is set to 5 minutes. should be set according to the real data expiration setting.
AbsoluteTime expirationTime = new AbsoluteTime(DateTime.Now.AddMinutes(5));
_CacheManager.Add(cacheKey, result, CacheItemPriority.Normal, null, expirationTime);
}
return result;
}
this all works fine and lovely. in each decorated method I have the following code:
StackTrace currentStack = new StackTrace();
string currentMethodName = currentStack.GetFrame(0).GetMethod().Name;
var result = (GeoArea)CachedMethodCall(_RealService.GetType().GetMethod(currentMethodName), someInputParam);
return result;
the problem is that sometimes the line where realMethod.Invoke(...) is happening returns null. If I put a breakpoint right after and then return the execution to that line, result is not null and data is fetched from the DB. all the input variables are correct, data exists in the DB, 2nd run gets the data, so what goes wrong in the first run?!
thanks :)
I think I managed to solve this problem by updating the code as follows:
public object CachedMethodCall(MethodInfo realMethod, params object[] realMethodParams)
{
string cacheKey = CachingHelper.GenereateCacheKey(realMethod, realMethodParams);
object result = _CacheManager.GetData(cacheKey);
if (result == null)
{
result = realMethod.Invoke(_RealService, BindingFlags.InvokeMethod, null, realMethodParams, CultureInfo.InvariantCulture);
// TODO: currently cache expiration is set to 5 minutes. should be set according to the real data expiration setting.
AbsoluteTime expirationTime = new AbsoluteTime(DateTime.Now.AddMinutes(5));
_CacheManager.Add(cacheKey, result, CacheItemPriority.Normal, null, expirationTime);
}
return result;
}
I noticed that the previous _CacheManager.Contains call was sometimes returning true even though the cache did not contain the data. I suspect threads causing the problems, but I'm not sure...

Categories