Log4Net - Add time with logging datetime - datetime

I developed a asp.net website where I log error information using log4net with format:
"%-5p %d - %m%n"
It logs datetime by current machine's datetime.
For example:
FATAL 2011-04-10 01:08:11,759 - message
But I want to convert datetime to another region or add additional time with it. For example I want to add 3 hours with previous example and want output as:
FATAL 2011-04-10 **04**:08:11,759 - message
Any idea on how to achieve this?

This might not answer your question, because I'm not sure exactly what you are trying to achieve. Maybe if you could provide more details on exactly why you want to do this, you might get a better answer.
If you are trying to correlate multiple log files (or other sources) that have been generated in different regions, it might help...
You could try log4net's utctime PatternLayout as described here.
This will get your log times in universal time, which might be easier for you to correlate. If you have control over the sources of timestampes (like your asp.net website), then by normalizing them to universal time, they should be easier to compare.
If you really do want to change the time to a different region or add/substract an arbitrary time span from the timestamp as it is logged, you might have to write your own custom PatternLayout or PatternLayoutConverter. That might be a little bit tricky as I think that neither the log4net DatePatternConverter nor the UtcDatePatternConverter is available for customization (i.e. they are declared internal so you can't subclass them and add your behavior).
You could write your own from scratch, using log4net's implementation from the log4net code repository, but that seems like a lot of trouble to me.
One more note, maybe it would be useful to log time again in a separate column using one of these Custom Date format specifiers: z, zz, zzz, K.
UPDATE:
See this answer for another idea that might help. The question asks for a way to capture username with log4net. Ultimately, the best solution for him was to write a very small class that will return the information the he needs (username). An instance of the class can be stored in the MDC (or GlobalDiagnosticContext) and referenced in the configuration. When log4net gets the value from the MDC (i.e. the object), it calls ToString and logs the result. This approach is a lot easier, if somewhat less flexible, than writing a whole new PatternLayoutConverter.
Towards the bottom of the answer is some sample code like this:
public class HttpContextUserNameProvider
{
public override string ToString()
{
HttpContext context = HttpContext.Current;
if (context != null &&
context.User != null &&
context.User.Identity.IsAuthenticated)
{
return context.Identity.Name;
}
return "";
}
}
You would store the object in the MDC/GlobalDiagnosticContext.Properties like this:
MDC.Set("user", new HttpContextUserNameProvider());
You could probably write something similar to return a different time. You could use this time instead of the log4net-provided time, or you could make this "custom" time an additional column. Your "custom time" object might look like this:
public class MyLocalTimeProvider
{
public override string ToString()
{
DateTime myLocalTime = GetUtcTimeInMyTimeZone(DateTime.UtcNow);
return myLocalTime;
}
}
Then you could reference it like this:
MDC.Set("myLocalTime", new MyLocalTimeProvider());
I'm not sure if you can apply formats to items from the MDC/GlobalDiagnosticContext.Properties (I think you can) or not, but you could try it and see.
You could always use a hardcoded format or add a format property to the object like this:
public class MyLocalTimeProvider
{
public MyLocalTimeProvider(string format)
{
Format = format;
}
public MyLocalTimeProvider()
: this ("G")
{
}
public string Format { get; set; }
public override string ToString()
{
DateTime myLocalTime = GetUtcTimeInMyTimeZone(DateTime.UtcNow);
return myLocalTime.ToString(Format);
}
}
You might take a look at this article for how to convert a UTC time to an arbitrary time zone.

If you need just "shift" date to your timezone, you can write your own ForwardingAppender, which will change DateTime of logged event:
namespace Olekstra
{
using System;
using log4net.Appender;
using log4net.Core;
public class TimeShiftForwardingAppender : ForwardingAppender
{
private TimeSpan shift;
private TimeSpan targetOffset;
public TimeShiftForwardingAppender()
{
TargetOffset = TimeZoneInfo.Local.BaseUtcOffset;
}
public TimeSpan TargetOffset
{
get
{
return targetOffset;
}
set
{
targetOffset = value;
shift = targetOffset.Subtract(TimeZoneInfo.Local.BaseUtcOffset);
}
}
protected override void Append(LoggingEvent loggingEvent)
{
var eventData = loggingEvent.GetLoggingEventData();
eventData.TimeStamp = eventData.TimeStamp.Add(shift);
base.Append(new LoggingEvent(eventData));
}
protected override void Append(LoggingEvent[] loggingEvents)
{
for (var i = 0; i < loggingEvents.Length; i++)
{
var eventData = loggingEvents[i].GetLoggingEventData();
eventData.TimeStamp = eventData.TimeStamp.Add(shift);
loggingEvents[i] = new LoggingEvent(eventData);
}
base.Append(loggingEvents);
}
}
}
And in .config
<log4net>
<appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
<!-- Your real appender here -->
</appender>
<appender name="TimeShiftAppender" type="Olekstra.TimeShiftForwardingAppender">
<targetOffset>06:00:00</targetOffset> <!-- your desired (local) UTC offset value -->
<appender-ref ref="FileAppender" /> <!-- real appender(s) -->
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="TimeShiftAppender" />
</root>
</log4net>

Related

How to change value(null to empty string) from query when using mybatis?

I am doing a project which uses Spring 3.1.1 and MyBatis3.0.
I am trying to change iBatis to MyBatis. However, I am struggling with resultmap.
When using iBatis, I can handle values from query like below with 'nullValue'.
<resultMap class="java.util.HashMap" id="ChannelData">
<result property="id" javaType="java.lang.String" column="CHANNEL_ID" nullValue=""/>
<result property="code" column="SELECTSCOPE" nullValue="Television"/>
</resultMap>
The problem is there no 'nullValue' in MyBatis. In addition, if the column is 'null' then mybatis never fills that element. for Example. if 'SELECTSCOPE' is null, it brings {id=aaa}. I need some data like this --> {id=aaa, code=''}. Is there anyway to handle this?
P.S.
I queries more than 20 columns. Some of them need "" when value is null, others has own default some string value.(If I use iBatis, 'nullValue' is magic keyword) I found some links which recommend to use customized type handler, but making handler more than 20 can be cause of future confusion to repair or maintaining. I need some simple way.
Thanks a lot:D
======================================================================================
I found the way to bring some null values. It needs some configuration.
make config.xml, which should contain some information about MyBatis Config DTD and Settings like below
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL MAP Config 3.1//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="callSettersOnNulls" value="true"/>
</settings>
</configuration>
now, I can get {id="aaa", code = null}. Here is additional question. How can I set default values for query? for example. if value of 'code' is null, then I want to put default String 'default'. So result should change form
{id="aaa", code=null} to {id="aaa",code="default"}. Is it possible?
Thanks~
I think I could suggest an answer for myself. But somehow it feels not efficient.
I made a TypeHandlerClass which implements the interface 'org.apache.ibatis.type.TypeHandler'.
Source code is below.
public class EmptyStringIfNull implements TypeHandler<String> {
#Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
return (rs.getString(columnName) == null) ? "" : rs.getString(columnName);
}
#Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
return (rs.getString(columnIndex) == null) ? "" : rs.getString(columnIndex);
}
#Override
public String getResult(CallableStatement cs, int columnIndex)
throws SQLException {
return (cs.getString(columnIndex) == null) ? "" : cs.getString(columnIndex);
}
#Override
public void setParameter(PreparedStatement ps, int arg1, String str,
JdbcType jdbcType) throws SQLException {
}
};
So I linked this in resultMap element 'typehandler' which looks like :
<resultMap type="map" id="channel">
<result property="id" column="CHANNEL_ID" typeHandler="StringHandler"/>
<result property="code" column="SELECTSCOPE" typeHandler="StringHandler"/>
</resultMap>
But I still got additional question. I see I can put some defaultValue in this .java code. But resultMap has many results. If every result has their own specific default value, How can handle this?
using 'if else' in java code fills inefficient, because some of them does not need to check value, they just only need to check null or not. Suggest your clever solutions :D Thanx
Indeed, the nullValue property of result map was very convenient.
If you map results to a custom type instead of a HashMap, the callSettersOnNulls setting that is false by defaults will help if concerned properties have default value:
public class ChannelData {
private String id;
private String code = "default";
}
Although it could be very tedious, you can also edit the SQL queries to handle default values using functions such as NVL / COALESCE:
SELECT channel_Id, NVL(selectScope, 'default') AS selectScope
The typeHandler you suggested has the drawback to apply to every column of the handled type and does not allow specifying different default values meeting the needs.
It can be achieved by implementing result handlers (but very verbose solution) which would consist in if-else on concerned properties (do not forget the custom result handler have to feed the result list if it is required).

How to pass a datetime parameter?

How to pass UTC dates to Web API?
Passing 2010-01-01 works fine, but when I pass a UTC date such as 2014-12-31T22:00:00.000Z (with a time component), I get a HTTP 404 response. So
http://domain/api/controller/action/2012-12-31T22:00:00.000Z
yields a 404 error response, while
http://domain/api/controller/action/2012-12-31
works fine.
How to pass UTC dates to Web API then - or at least specify date and time?
The problem is twofold:
1. The . in the route
By default, IIS treats all URI's with a dot in them as static resource, tries to return it and skip further processing (by Web API) altogether. This is configured in your Web.config in the section system.webServer.handlers: the default handler handles path="*.". You won't find much documentation regarding the strange syntax in this path attribute (regex would have made more sense), but what this apparently means is "anything that doesn't contain a dot" (and any character from point 2 below). Hence the 'Extensionless' in the name ExtensionlessUrlHandler-Integrated-4.0.
Multiple solutions are possible, in my opinion in the order of 'correctness':
Add a new handler specifically for the routes that must allow a dot. Be sure to add it before the default. To do this, make sure you remove the default handler first, and add it back after yours.
Change the path="*." attribute to path="*". It will then catch everything. Note that from then on, your web api will no longer interpret incoming calls with dots as static resources! If you are hosting static resources on your web api, this is therefor not advised!
Add the following to your Web.config to unconditionally handle all requests: under <system.webserver>: <modules runAllManagedModulesForAllRequests="true">
2. The : in the route
After you've changed the above, by default, you'd get the following error:
A potentially dangerous Request.Path value was detected from the client (:).
You can change the predefined disallowed/invalid characters in your Web.config. Under <system.web>, add the following: <httpRuntime requestPathInvalidCharacters="<,>,%,&,*,\,?" />. I've removed the : from the standard list of invalid characters.
Easier/safer solutions
Although not an answer to your question, a safer and easier solution would be to change the request so that all this is not required. This can be done in two ways:
Pass the date as a query string parameter, like ?date=2012-12-31T22:00:00.000Z.
Strip the .000 from every request, and encode the url, so replace all :'s with %3A, e.g. by using HttpUtility.UrlEncode().
in your Product Web API controller:
[RoutePrefix("api/product")]
public class ProductController : ApiController
{
private readonly IProductRepository _repository;
public ProductController(IProductRepository repository)
{
this._repository = repository;
}
[HttpGet, Route("orders")]
public async Task<IHttpActionResult> GetProductPeriodOrders(string productCode, DateTime dateStart, DateTime dateEnd)
{
try
{
IList<Order> orders = await _repository.GetPeriodOrdersAsync(productCode, dateStart.ToUniversalTime(), dateEnd.ToUniversalTime());
return Ok(orders);
}
catch(Exception ex)
{
return NotFound();
}
}
}
test GetProductPeriodOrders method in Fiddler - Composer:
http://localhost:46017/api/product/orders?productCode=100&dateStart=2016-12-01T00:00:00&dateEnd=2016-12-31T23:59:59
DateTime format:
yyyy-MM-ddTHH:mm:ss
javascript pass parameter use moment.js
const dateStart = moment(startDate).format('YYYY-MM-DDTHH:mm:ss');
const dateEnd = moment(endDate).format('YYYY-MM-DDTHH:mm:ss');
I feel your pain ... yet another date time format... just what you needed!
Using Web Api 2 you can use route attributes to specify parameters.
so with attributes on your class and your method you can code up a REST URL using this utc format you are having trouble with (apparently its ISO8601, presumably arrived at using startDate.toISOString())
[Route(#"daterange/{startDate:regex(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$)}/{endDate:regex(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$)}")]
[HttpGet]
public IEnumerable<MyRecordType> GetByDateRange(DateTime startDate, DateTime endDate)
.... BUT, although this works with one date (startDate), for some reason it doesnt work when the endDate is in this format ... debugged for hours, only clue is exception says it doesnt like colon ":" (even though web.config is set with :
<system.web>
<compilation debug="true" targetFramework="4.5.1" />
<httpRuntime targetFramework="4.5.1" requestPathInvalidCharacters="" />
</system.web>
So, lets make another date format (taken from the polyfill for the ISO date format) and add it to the Javascript date (for brevity, only convert up to minutes):
if (!Date.prototype.toUTCDateTimeDigits) {
(function () {
function pad(number) {
if (number < 10) {
return '0' + number;
}
return number;
}
Date.prototype.toUTCDateTimeDigits = function () {
return this.getUTCFullYear() +
pad(this.getUTCMonth() + 1) +
pad(this.getUTCDate()) +
'T' +
pad(this.getUTCHours()) +
pad(this.getUTCMinutes()) +
'Z';
};
}());
}
Then when you send the dates to the Web API 2 method, you can convert them from string to date:
[RoutePrefix("api/myrecordtype")]
public class MyRecordTypeController : ApiController
{
[Route(#"daterange/{startDateString}/{endDateString}")]
[HttpGet]
public IEnumerable<MyRecordType> GetByDateRange([FromUri]string startDateString, [FromUri]string endDateString)
{
var startDate = BuildDateTimeFromYAFormat(startDateString);
var endDate = BuildDateTimeFromYAFormat(endDateString);
...
}
/// <summary>
/// Convert a UTC Date String of format yyyyMMddThhmmZ into a Local Date
/// </summary>
/// <param name="dateString"></param>
/// <returns></returns>
private DateTime BuildDateTimeFromYAFormat(string dateString)
{
Regex r = new Regex(#"^\d{4}\d{2}\d{2}T\d{2}\d{2}Z$");
if (!r.IsMatch(dateString))
{
throw new FormatException(
string.Format("{0} is not the correct format. Should be yyyyMMddThhmmZ", dateString));
}
DateTime dt = DateTime.ParseExact(dateString, "yyyyMMddThhmmZ", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
return dt;
}
so the url would be
http://domain/api/myrecordtype/daterange/20140302T0003Z/20140302T1603Z
Hanselman gives some related info here:
http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
As a similar alternative to s k's answer, I am able to pass a date formatted by Date.prototype.toISOString() in the query string. This is the standard ISO 8601 format, and it is accepted by .Net Web API controllers without any additional configuration of the route or action.
e.g.
var dateString = dateObject.toISOString(); // "2019-07-01T04:00:00.000Z"
This is a solution and a model for possible solutions. Use Moment.js in your client to format dates, convert to unix time.
$scope.startDate.unix()
Setup your route parameters to be long.
[Route("{startDate:long?}")]
public async Task<object[]> Get(long? startDate)
{
DateTime? sDate = new DateTime();
if (startDate != null)
{
sDate = new DateTime().FromUnixTime(startDate.Value);
}
else
{
sDate = null;
}
... your code here!
}
Create an extension method for Unix time. Unix DateTime Method
It used to be a painful task, but now we can use toUTCString():
Example:
[HttpPost]
public ActionResult Query(DateTime Start, DateTime End)
Put the below into Ajax post request
data: {
Start: new Date().toUTCString(),
End: new Date().toUTCString()
},
As a matter of fact, specifying parameters explicitly as ?date='fulldatetime' worked like a charm. So this will be a solution for now: don't use commas, but use old GET approach.
One possible solution is to use Ticks:
public long Ticks { get; }
Then in the controller's method:
public DateTime(long ticks);
Since I have encoding ISO-8859-1 operating system the date format "dd.MM.yyyy HH:mm:sss" was not recognised what did work was to use InvariantCulture string.
string url = "GetData?DagsPr=" + DagsProfs.ToString(CultureInfo.InvariantCulture)
By looking at your code, I assume you do not have a concern about the 'Time' of the DateTime object. If so, you can pass the date, month and the year as integer parameters. Please see the following code. This is a working example from my current project.
The advantage is; this method helps me to avoid DateTime format issues and culture incompatibilities.
/// <summary>
/// Get Arrivals Report Seven Day Forecast
/// </summary>
/// <param name="day"></param>
/// <param name="month"></param>
/// <param name="year"></param>
/// <returns></returns>
[HttpGet("arrivalreportsevendayforecast/{day:int}/{month:int}/{year:int}")]
public async Task<ActionResult<List<ArrivalsReportSevenDayForecastModel>>> GetArrivalsReportSevenDayForecast(int day, int month, int year)
{
DateTime selectedDate = new DateTime(year, month, day);
IList<ArrivalsReportSevenDayForecastModel> arrivingStudents = await _applicationService.Value.GetArrivalsReportSevenDayForecast(selectedDate);
return Ok(arrivingStudents);
}
If you are keen to see the front-end as well, feel free to read the code below. Unfortunately, that is written in Angular. This is how I normally pass a DateTime as a query parameter in Angular GET requests.
public getArrivalsReportSevenDayForecast(selectedDate1 : Date): Observable<ArrivalsReportSevenDayForecastModel[]> {
const params = new HttpParams();
const day = selectedDate1.getDate();
const month = selectedDate1.getMonth() + 1
const year = selectedDate1.getFullYear();
const data = this.svcHttp.get<ArrivalsReportSevenDayForecastModel[]>(this.routePrefix +
`/arrivalreportsevendayforecast/${day}/${month}/${year}`, { params: params }).pipe(
map<ArrivalsReportSevenDayForecastModel[], ArrivalsReportSevenDayForecastModel[]>(arrivingList => {
// do mapping here if needed
return arrivingList;
}),
catchError((err) => this.svcError.handleError(err)));
return data;
}
Passing the date as a string and then parsing it worked for me. Probably want to add try catch on the parse, but this is the basic code.
[HttpGet("name={name}/date={date}", Name = "GetByNameAndDate")]
public IActionResult GetByNameAndDate(string name, string date) {
DateTimeOffset dto = DateTimeOffset.Parse(date);
}
Then the request can look like this
https://localhost/api/Contoller/name=test/date=2022-02-18T13:45:37.000Z
For external APIs (where you do not know what type of client will call your service), Unix Time should be used both on the input parameters and outputted date fields.
https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.tounixtimeseconds?view=net-6.0
.Net provides ToUnixtimeSeconds and FromUnixtimeSeconds to easily convert to DateTime or DateTimeOff
Unix Time should be preferred over ISO formats because it is just a integer and can be passed in the URL string without encoding.
The 'Ticks' property is similar to Unix time but (I believe) should only be use between a .net client and server.
Most well know APIs will use Unix Time, for example see Stripe's API:
https://stripe.com/docs/api
The obvious downsides of using Unix time are:
They are not human readable
They cannot be created by humans - making it difficult to call the API without code
Use binary format.
to send the info in url use dateTimeVar.ToBinary()
it will be something like
http://domain/api/controller/action/637774955400000000
when you reciebe the data will get like Long variable and use the static function of DateTime Class to transform to DateTime type again.
DateTime MyDateTime = DateTime.FromBinary(BinaryDateTime);
Cheers

Can somebody please explain this common binding pitfall to me? (using the wrong bindable event name)

I refer to this site link text
Using the wrong event name in the
[Bindable] tag can cause your
application to not bind your property,
and you will not even know why. When
you use the [Bindable] tag with a
custom name, the example below looks
like a good idea:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event=EVENT_CHANGED_CONST)]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
The code above assigns a static
property to the event name, and then
uses the same assignment to dispatch
the event. However, when the value
changes, the binding does not appear
to work. The reason is that the event
name will be EVENT_CHANGED_CONST and
not the value of the variable.
The code should have been written as
follows:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event="eventChangedConst")]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
I agree, the wrong example does look like a good idea and I would do it that way because I think it's the right way and avoids the possibility of a typing error. Why is the name of the constant used instead of it's value? Surely this can't be right?
I appreciate your insights
Because the standard Flex compiler isn't that clever at times... and I feel your pain! I've complained about this exact problem more than a few times.
If I remember correctly, it's because the compiler does multiple passes. One of the early passes changes the Metadata into AS code. At this point in the compiler it hasn't parsed the rest of the AS code, so its not capable of parsing Constants or references to static variables in other files.
The only thing I can suggest is sign up to the Adobe JIRA, vote for the bug, and hope that the compiler fixes in 4.5 bring some relief.

Flex: AMF and Enum Singletons – can they play well together?

I'm using Python+PyAMF to talk back and forth with Flex clients, but I've run into a problem with the psudo-Enum-Singletons I'm using:
class Type {
public static const EMPTY:Type = new Type("empty");
public static const FULL:Type = new Type("full");
...
}
When I'm using locally created instances, everything is peachy:
if (someInstance.type == Type.EMPTY) { /* do things */ }
But, if 'someInstance' has come from the Python code, it's instance of 'type' obviously won't be either Type.EMPTY or Type.FULL.
So, what's the best way to make my code work?
Is there some way I can control AMF's deserialization, so when it loads a remote Type, the correct transformation will be called? Or should I just bite the bullet and compare Types using something other than ==? Or could I somehow trick the == type cohesion into doing what I want?
Edit: Alternately, does Flex's remoting suite provide any hooks which run after an instance has been deserialized, so I could perform a conversion then?
Random thought: Maybe you could create a member function on Type that will return the canonical version that matches it?
Something like:
class Type {
public static const EMPTY:Type = new Type("empty");
public static const FULL:Type = new Type("full");
...
// I'm assuming this is where that string passed
// in to the constructor goes, and that it's unique.
private var _typeName:String;
public function get canonical():Type {
switch(this._typeName) {
case "empty": return EMPTY;
case "full": return FULL;
/*...*/
}
}
}
As long as you know which values come from python you would just convert them initially:
var fromPython:Type = /*...*/
var t:Type = fromPython.canonical;
then use t after that.
If you can't tell when things come from python and when they're from AS3 then it would get pretty messy, but if you have an isolation layer between the AS and python code you could just make sure you do the conversion there.
It's not as clean as if you could control the deserialization, but as long as you've got a good isolation layer it should work.

creating expiring ASP.NET session value

I like to create a session value that expires in 5 minutes.
How to do that, do I need to manually compare creation time when I read it?
Session("sessionval") = myvariable
Session("sessioncreated") = now
If you are asking how to do it on a single variable I do not think it is possible, but you can define the session timeout period using the web.config
<system.web>
<sessionState timeout="5" />
<system.web>
this would affect all of your session objects, not just a single one.
My suggestion for giving a custom expiration date to a single item is to create a cookie, that way you can actually set it's expiration value.
Response.Cookies.Add(New Web.HttpCookie("AdminID", admin.AdminID))
Response.Cookies("AdminID").Expires = Now.AddMinutes(5)
you can not define custom timeout to a session variable.
Maybe you can use cache for this, with a unique key dependent to session.
You can set timeout to a cached item.
Cache.Insert("key_dependent_to_session", value, Nothing,
DateTime.Now.AddMinutes(5), TimeSpan.Zero)
If you want to use the SessionState you could create your own wrapper so instead of caching the actual item, you can cache the wrapper. Here is a quick dirty untested example of a wrapper that checks if the item is null, or if it has expired it will call a Func that you can provide a refreshed item.
The Func takes the last set value so if you can determine if the value is still valid you could avoid reloading it.
public class TimeExpirationItem<T>
{
private T _item=default(T);
private TimeSpan _expirationDuration;
private Func<T> _getItemFunc;
private DateTime _expiresTime;
public TimeExpirationItem(TimeSpan expirationDuration, Func<T, T> getItemFunc)
{
this._getItemFunc = getItemFunc;
this._expirationDuration = expirationDuration;
}
public T Item
{
get
{
if (_item == null || ItemIsExpired())
{
_item = _getItemFunc(_item);
_expiresTime = DateTime.Now.Add(_expirationDuration);
}
return _item;
}
}
public bool ItemIsExpired()
{
return DateTime.Now > _expiresTime;
}
}
Again this code is provided as is with no warranty and it is untested but is an example of the things you can do.
Using it would be something like the following:
Session.Add("ObjectKey",new TimeExpirationItem<MyObject>(new TimeSpan(0,0,5),mo=>MyObjectRepository.GetItemByLogin(Request.User.Identity.Name));
That is correct. Session state doesn't have an Expiration concept.
If your data are not per-user, you can use Cache instead. You could even use Cache, but include the user name as part of the key, which would sort of give you an expiring session state.
I tend to agree with TheTXI.
Although a single Session item cannot be assigned a custom timeout, you should note that in InProc session mode, Sessionstate is itself added as an item to the ASP.NET cache. This enables it to have a "timeout".
In your particular scenario, a Cache object is the best suited solution.
Sorry to resurrect an old question, but I faced exactly the same problem and had to create an extension-class for the HttpSession object.
Here's my blog article on this
PS. the code:
public static class SessionExtender
{
public static void AddWithTimeout(this HttpSessionState session,
string name,
object value,
TimeSpan expireAfter)
{
session[name] = value;
session[name + "ExpDate"] = DateTime.Now.Add(expireAfter);
}
public static object GetWithTimeout(
this HttpSessionState session,
string name)
{
object value = session[name];
if (value == null) return null;
DateTime? expDate = session[name + "ExpDate"] as DateTime?;
if (expDate == null) return null;
if (expDate < DateTime.Now)
{
session.Remove(name);
session.Remove(name + "ExpDate");
return null;
}
return value;
}
}
1) Put this in web.config
<configuration>
<system.web>
<sessionState mode="InProc" timeout="5">
</sessionState>
</system.web>
</configuration>
2) Set System.Web.SessionState.HttpSessionState.Timeout = 5 mins.

Resources