Watin OwnTableCellCells.Count index out of range sometimes - watin

I'm using 20.20.1089, and I'm getting an exception sometime while looping over
like this
foreach (TableRow row in table.TableRows)
{
for (int i = 0; i < row.OwnTableCells.Count ; ++i)
{
// EXCEPTION INDEX OUT OF RANGE
TableCell cell = row.OwnTableCells [i];
}
}
Under the debugger, row.OwnTableCells.Count is 0, but the loop index i has been
increments. I can get Exception:Object reference not set to an instance of an
object.
This happens sometimes only, which is the puzzling part.The webpage is always refreshing automatically after a few seconds. Could this
be the cause of it? Is there a way to disable the cache?
Robin
PS. I'm trying to get this posted on Watin mailing list but it seems the moderator is gone missing, and not approving any new registrations.

Hi your mail was send on the mailing list as well, but for record sake I'll post my answer here as well.
The webpage is always refreshing automatically after a few seconds. Could this
be the cause of it?
This indeed is your problem. Following one of the possible scenarios to show you how things can go wrong:
1 - calling row.OwnTableCells.Count => returns the number of elements currently on the page
2 - refresh timer triggers page refresh => page reloaded
3 - due to refresh the row.OwnTableCells collection will return no elements any more since the row which was referenced no longer exists on the page. You might still see the same row in the browser, but that is a new DOM row object created after the page refresh. In your code you/WatiN is still referencing the old row object.
So you should change your logic to take this refresh of the page into account.
HtH,
Jeroen

Related

Sharepoint 2013 Event Receiver ItemUpdating - detect Attachments

I've been looking for a way to detect changes on the attachments of an item using synchronous event receivers in SharePoint 2013 developed in C#.
The ItemAdding event is not relevant as it's not a problem if attachments are uploaded at the same moment the item is created, however, the event ItemUpdating is more relevant. Indeed, I would like to be able to update another field if one (or more) attachment is added or deleted during the synchronous event only. Asynchronous events are not an option.
I tried the solution provided here without any luck :
When an attachment gets added, Request.Files.Count is higher or equal to 1
When an attachment gets removed, it's equal to 1 if other attachments still exist on the item
When no changes are done, it's still equal to 1 if other attachments exist on the item.
Would you have any ideas how I can do it ?
Thank you in advance,
Kevin
EDIT 19 APRIL :
After further investigation, I detected that an empty file was always sent in the Request.File, so I know now the reason why Request.File.Count was always at least equal to 1.
With a small piece of code added, I can detect one or more valid files are being added :
int attachCount = 0;
HttpFileCollection fileCollection = curContext.Request.Files;
for (int j = 0; j < fileCollection.Count; j++)
{
HttpPostedFile file = fileCollection[j];
if (file.ContentLength > 0)
{
attachCount++;
}
}
Only thing left is how to detect that attachments has been removed when being in the ItemUpdating event, any ideas ?
Thank you in advance
After a lot of searching, I used the ItemUpdated event to solve the problem of the attachments deleted with the number of attachments in a dedicated field. All is working as expected. The only down side is the fact that the user might have to wait a few seconds before seeing the impact of the attachment removal.

NHibernate ArgumentOutOfRangeException

I recently ran into an instance where I wanted to hit the database from a Task I have running periodically within a web application. I refactored the code to use the ThreadStaticSessionContext so that I could get a session without an HttpContext. This works fine for reads, but when I try to flush an update from the Task, I get the "Index was out of range. Must be non-negative and less than the size of the collection." error. Normally what I see for this error has to do with using a column name twice in the mapping, but that doesn't seem to be the issue here, as I'm able to update that table if the session is associated with a request (and I looked and I'm not seeing any duplicates). It's only when the Task tries to flush that I get the exception.
Does anyone know why it would work fine from a request, but not from a call from a Task?
Could it be because the Task is asynchronous?
Call Stack:
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at System.Collections.Generic.List`1.System.Collections.IList.get_Item(Int32 index)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
Session Generation:
internal static ISession CurrentSession {
get {
if(HasSession) return Initializer.SessionFactory.GetCurrentSession();
ISession session = Initializer.SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
return session;
}
}
private static bool HasSession {
get { return CurrentSessionContext.HasBind(Initializer.SessionFactory); }
}
Task that I want to access the database from:
_maid = Task.Factory.StartNew(async () => {
while(true) {
if(CleaningSession != null) CleaningSession(Instance, new CleaningSessionEventArgs { Session = UnitOfWorkProvider.CurrentSession });
UnitOfWorkProvider.TransactionManager.Commit();
await Task.Delay(AppSettings.TempPollingInterval, _paycheck.Token);
}
//I know this function never returns, I'm using the cancellation token for that
// ReSharper disable once FunctionNeverReturns
}, _paycheck.Token);
_maid.GetAwaiter().OnCompleted(() => _maid.Dispose());
Edit: Quick clarification about some of the types above. CleaningSession is an event that is fired to run the various things that need to be done, and _paycheck is the CancellationTokenSource for the Task.
Edit 2: Oh yeah, and this is using NHibernate version 4.0.0.4000
Edit 3: I have since attempted this using a Timer, with the same results.
Edit 4: From what I can see of the source, it's doing a foreach loop on an IList. Questions pertaining to an IndexOutOfRangeException in a foreach loop tend to suggest a concurrency issue. I still don't see how that would be an issue, unless I misunderstand the purpose of ThreadStaticSessionContext.
Edit 5: I thought it might be because of requests bouncing around between threads, so I tried creating a new SessionContext that combines the logic of the WebSessionContext and ThreadStaticSessionContext. Still getting the issue, though...
Edit 6: It seems this has something to do with a listener I have set up to update some audit fields on entities just before they're saved. If I don't run it, the commit occurs properly. Would it be better to do this through an event than OnPreInsert, or use an interceptor instead?
After muddling through, I found out exactly where the problem was. Basically, there was a query that was run to load the current user record called from inside of the PreUpdate event in my listener.
I came across two solutions to this. I could cache the user in memory, avoiding the query, but having possibly stale data (not that anything other than the id matters here). Alternatively, I could open a temporary stateless session and use that to look up the user in question.

How to prevent AJAX polling keeping Asp.Net sessions alive

We have a ASP.Net application that has been given a 20 minute sliding expiry for the session (and cookie).
However, we have some AJAX that is polling the server for new information. The downside of this of course is that the session will continue indefinitely, as it is being kept alive by the polling calls causing the expiry time to be refreshed. Is there a way of preventing this - i.e. to only allow the session to be refreshed on non-ajax calls?
Turning off sliding expiry is not really an option as this is an application that business users will be using for most of their day between telephone calls.
Other Stackoverflow discussions on this talk about maintaining 2 separate application (one for authenticated calls, one for unauthenticated. I'm not sure this will be an option as all calls need to be authenticated.
Any ideas?
As this question is old I am assuming it has been resolved or a workaround implemented. However, I wanted to mention that instead of AJAX polling the server to perform an operation we have utilized SignalR which allows both the client to communicate with the server via JQuery and/or the server to notify the client.
Check it out: Learn About ASP.NET SignalR
add below code to your controller action that you are reference for polling.Convert this into an attribute so it can be used everywhere. This line will not extend session timeout
[HttpPost]
public ActionResult Run()
{
Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
return Json("");
}
There is no way to stop the ajax from keeping the session and cookies alive!
However, there is a way to achieve what you want to do. That is if the process I will describe will be ok to you.
I think what you really want to achieve is first to refresh your page with ajax so that some processes will be active and running. Also to know when the user has stopped operating the program.
If that is what you want then there is a simple process to achieve this
You will have your ajax running for the things you want to run.
You will remove the session you want to check if user has stopped operation on the page and manage the session as a variable instead.
The variable can be a global variable or a class variable that will be set to initial value whenever the user clicks an element on the page.
(You will select the click event of an element and set the variable to initial value)
You will increment the variable every given time (say every time your ajax runs)
You will also have a function/method run to check the value of that variable if it is greater than the value you set as limit. This can run every time your ajax runs or every time you want it to run (timed event).
If the value of your variable is greater than the limit set it should invalidate or clear session/log user out.
This way if user stops operating (clicking elements) the system on any page that this is running will eventually log out the current user and stop running the program.
I have done this by creating a hidden page in an i-Frame. Then using JavaScript it posts back every 18 minutes to keep the session alive. This works really well.
This example is from a ASP.NET Forms project but could be tweaked for MVC.
Create a page called KeepSessionAlive page and add a meta refresh tag
meta id="MetaRefresh" http-equiv="refresh" content="21600;url=KeepSessionAlive.aspx"
In the code behind
protected string WindowStatusText = "";
protected void Page_Load(object sender, EventArgs e)
{
//string RefreshValue = Convert.ToString((Session.Timeout * 60) - 60);
string RefreshValue = Convert.ToString((Session.Timeout * 60) - 90);
// Refresh this page 60 seconds before session timeout, effectively resetting the session timeout counter.
MetaRefresh.Attributes["content"] = RefreshValue + ";url=KeepSessionAlive.aspx?q=" + DateTime.Now.Ticks;
WindowStatusText = "Last refresh " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();
}
Add the hidden iFrame in a master page
iframe ID="KeepAliveFrame" src="KeepSessionAlive.aspx" frameBorder="0" width="0" height="0"
Download example

NSFetchedResultsController inserts lock up the UI

I am building a chat application using web-sockets and core-data.
Basically, whenever a message is received on the web-socket, the following happens:
check if the message exists by performing a core-data fetch using the id (indexed)
if 1. returns yes, update the message and perform a core-data save. if 1. returns no, create the message and perform a core-data save.
update table view, by updating or inserting new rows.
Here's my setup:
I have 2 default managed-object-contexts. MAIN (NSMainQueueConcurrencyType) and WRITER (NSPrivateQueueConcurrencyType). WRITER has a reference to the persisten store coordinator, MAIN does not, but WRITER is set as MAIN's parent.
TableView is connected to a NSResultsFetchController, connected to MAIN.
Fetches are all performed using temporary contexts ("performBlock:") that have MAIN as their parent. Writes look like this: Save temporary context, then save MAIN, then save WRITER.
Problem:
Because the updates come in via web-socket, in a busy chat-room, many updates happen in a short time. Syncs to fetch older messages can mean many messages coming in rapidly. And this locks up the UI.
I track the changes to the ui using fetched-results-controller's delegate like this:
// called on main thread
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
NSLog(#"WILL CHANGE CONTENT");
[_tableView beginUpdates];
}
// called on main thread
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
NSLog(#"DID CHANGE CONTENT");
[_tableView endUpdates];
}
and here's an example of what I see in the Log-file:
2014-07-14 18:46:20.630 AppName[4938:60b] DID CHANGE CONTENT
2014-07-14 18:46:22.334 AppName[4938:60b] WILL CHANGE CONTENT
That's almost 2 seconds per insert!
Is it simply a limitation I'm hitting here with tableviews? (I'm talking about 1000+ rows in some cases) But I can't imagine that's the case. UITableViews are super-optimized for that sort of operation.
Any obvious newbie-mistake I might be commiting?
This is not logical:
Writes look like this: Save temporary context, then save MAIN, then save WRITER.
If writer is a child context of main the changes are not persisted until the next save.
Ok I figured it out.
The problem is tableView:heightForRowAtIndexPath:. The fetches to calculate the height for the rows take time and each time tableView.endUpdates gets called, the UITableView needs the heights for ALL rows.
tableView:estimatedHeightForRowAtIndexPath: is a possible way to go (iOS7+), or I might opt for caching the heights myself (since the rows don't change) or just displaying fewer rows altog

Get rid of invisible UltraGridRow instances

I've got a grid that show tens of thousands of rows in total, obviously the user is only looking at a small fraction of those rows at any point in time, generally somewhere around 10~20 rows.
I enabled LoadOnDemand, which ensures that UltraGridRow instances and a couple of other objects will only be created when they move into the visible area:
grid.DisplayLayout.LoadStyle = LoadStyle.LoadOnDemand;
This works great (according to my memory profiler), but when I scroll up and down the whole table, i.e. trigger creation of UltraGridRows for all rows, then all UltraGridRows will get created and stay in memory forever.
How can I (ideally automatically, but if not, manually) get rid off the UltraGridRow objects for rows that are out of view? (Or just get rid of all UltraGridRows - they'll be recreated automatically anyway)
One brute force way I figured out is this:
var tmp = grid.DataSource;
grid.DataSource = null;
grid.DataSource = tmp;
It causes some side effects though, so is there some other way to get rid of UltraGridRows?
Btw, I tried these two guys, without success (trying true and false for the bool params).
grid.Rows.Refresh(RefreshRow.ReloadData, false, true);
grid.Rows.DeallocateCells(true);
I'm trying to get memory consumption down, and UltraGridRows are currently the main consumer (UltraGridRows by themselves aren't huuuge, they consume under 200 bytes each, which in my case means a couple of megabytes, just so you know what we are talking about).
This is Infragistics 9.2.
Any ideas?
Well, there's always reflection...
var m = typeof(RowsCollection)
.GetMethod("InternalClearHelper", BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke(grid.Rows, null);
This actually works for me, but it will clear the selection, so you have to remember that before invoking and restore the selection afterwards.

Resources