I posted this question, and from the replies, I get the impression that
I worded my posting very poorly, so let me try this again.
While debugging and stepping through this foreach loop
foreach(Student s in course.Students)
{
Console.WriteLine(s.StudentID);
}
I get this error:
******************************
An unhandled exception of type 'System.InvalidOperationException'
occurred in mscorlib.dll
Additional information: Collection was modified; enumeration operation
may not execute.
*******************************
Please note the following points
- This code RUNS FINE when run without debugging. NO error
- 2 of my friends can STEP THROUGH the foreach loop without running into
the error message with VS.NET 2003
- 2 other friends get the same error message again with VS.NET 2003
- I used to be able to step through fine with VS.NET 2003. This
instance of VS.NET 2003 is new. Never had a problem stepping through
before on my old machine.
Now let me show you the implentation of the course.Students property.
Please note that following code is just to demonstrate the concept, so
refrain from commenting on my style.
public ArrayList Students
{
get
{
if (students == null)
students = new ArrayList();
--here a call is made to the database to refresh
refreshStudentsFromDataBase();
return students;
}
}
I want it this way because course.Students can change, so everytime it's
called, I want it refreshed.
My understanding is that once course.Students is in a foreach loop, it
will not be called again. And when the code is run without debug, it
confirms my observation. But when I step through, I get the error. 2
people who ran the same code on their machine CAN STEP THROUGH.
Thanks.
*** Sent via Developersdex http://www.developersdex.com *** 25 3976
David C <no*******@nospam.com> wrote: I posted this question, and from the replies, I get the impression that I worded my posting very poorly, so let me try this again.
<snip>
I suspect that while you're in the debugger, it executes the Students
property accessor. That changes the collection, hence the problem.
The most obvious solution to this is *not* to use a property to
populate the collection - at least not if it's already populated.
--
Jon Skeet - <sk***@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too I suspect that while you're in the debugger, it executes the Students property accessor. That changes the collection, hence the problem.
The biggest issue is why the debugger behaves this way for some
instances of VS.NET and not for others.
*** Sent via Developersdex http://www.developersdex.com ***
You could also copy the contents of course.Students to a new array and
foreach() that instead. Then you can keep your refresh code in the
property, but ensure that it doesn't cause any problems.
As to why some people experience the same problem and some don't, it
could be that your debugging settings are different perhaps? Maybe with
variable evaluation or something?
David C <no*******@nospam.com> wrote: I suspect that while you're in the debugger, it executes the Students property accessor. That changes the collection, hence the problem.
The biggest issue is why the debugger behaves this way for some instances of VS.NET and not for others.
It may well depend on which tab the user happens to have visible.
--
Jon Skeet - <sk***@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too It may well depend on which tab the user happens to have visible.
What tab are you refering to?
*** Sent via Developersdex http://www.developersdex.com ***
David C <no*******@nospam.com> wrote: It may well depend on which tab the user happens to have visible.
What tab are you refering to?
Autos, locals, variables - any of the debugger tabs in VS.NET.
--
Jon Skeet - <sk***@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
>It may well depend on which tab the user happens to have visible.
YOU WERE RIGHT!!!!
It is the autos tab that was causing the problem.
Now if somebody can explain why that tab is causing the problem and
whether Microsoft intended it that way and why....
*** Sent via Developersdex http://www.developersdex.com ***
Wow. Good call. I would never have thought of that. Of course the
debugger calls your own properties to populate its displays, so just
looking at the property in the debugger while in the loop would crash
the loop.
David C <no*******@nospam.com> wrote: It may well depend on which tab the user happens to have visible. YOU WERE RIGHT!!!!
It is the autos tab that was causing the problem.
Now if somebody can explain why that tab is causing the problem
That's easy - when you execute a statement, the Autos tab needs to keep
you up to date on what the values you're looking at are - so it
executes properties...
and whether Microsoft intended it that way and why....
Microsoft didn't intend you to write properties which had the sort of
quantum property that observing the value also changed it.
--
Jon Skeet - <sk***@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
as I've explained to you last time this was posted, for debugger to display
your fields and properties, it has to invoke it.
"David C" wrote: It may well depend on which tab the user happens to have visible.
YOU WERE RIGHT!!!!
It is the autos tab that was causing the problem.
Now if somebody can explain why that tab is causing the problem and whether Microsoft intended it that way and why....
*** Sent via Developersdex http://www.developersdex.com *** That's easy - when you execute a statement, the Autos tab needs to keep you up to date on what the values you're looking at are - so it executes properties...
and whether Microsoft intended it that way and why....
Microsoft didn't intend you to write properties which had the sort of quantum property that observing the value also changed it.
actually, I have lots of quantum properties (lazy instantiation for example).
Daniel Jin <Da*******@discussions.microsoft.com> wrote: Microsoft didn't intend you to write properties which had the sort of quantum property that observing the value also changed it.
actually, I have lots of quantum properties (lazy instantiation for example).
Yes, that's fair enough - but I would hope that the values don't change
*after the initial "view"* on subsequent views.
--
Jon Skeet - <sk***@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
"Jon Skeet [C# MVP]" wrote: Daniel Jin <Da*******@discussions.microsoft.com> wrote: Microsoft didn't intend you to write properties which had the sort of quantum property that observing the value also changed it. actually, I have lots of quantum properties (lazy instantiation for example).
Yes, that's fair enough - but I would hope that the values don't change *after the initial "view"* on subsequent views.
fair enough.
-- Jon Skeet - <sk***@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet If replying to the group, please do not mail me too
On 15 Sep 2005 12:31:40 -0700, "gmiley" <gm****@gmail.com> wrote: You could also copy the contents of course.Students to a new array and foreach() that instead. Then you can keep your refresh code in the property, but ensure that it doesn't cause any problems.
You don't need a copy, just declare a new variable of the same type
and assign the property to the variable before enumeration:
ArrayList students = course.Students;
foreach(Student s in students)
Console.WriteLine(s.StudentID);
That way your property access happens outside the foreach and you
should be fine.
-- http://www.kynosarges.de
Christoph Nahr <ch************@kynosarges.de> wrote: On 15 Sep 2005 12:31:40 -0700, "gmiley" <gm****@gmail.com> wrote:
You could also copy the contents of course.Students to a new array and foreach() that instead. Then you can keep your refresh code in the property, but ensure that it doesn't cause any problems.
You don't need a copy, just declare a new variable of the same type and assign the property to the variable before enumeration:
ArrayList students = course.Students; foreach(Student s in students) Console.WriteLine(s.StudentID);
That way your property access happens outside the foreach and you should be fine.
No, that's not the problem - the Students property was only being
evaluated once in the foreach loop itself anyway. The problem was that
the debugger was running the Students properly independently. That was
modifying the returned ArrayList, so there'd be the same problem with
the code above.
--
Jon Skeet - <sk***@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
On Fri, 16 Sep 2005 07:39:11 +0100, Jon Skeet [C# MVP]
<sk***@pobox.com> wrote: No, that's not the problem - the Students property was only being evaluated once in the foreach loop itself anyway. The problem was that the debugger was running the Students properly independently. That was modifying the returned ArrayList, so there'd be the same problem with the code above.
Oh, you're right -- the property is changing the contents of the
already allocated array. True, you'd really need an array copy to
avoid the debugger hangup.
-- http://www.kynosarges.de
>Microsoft didn't intend you to write properties which had the sort of quantum property that >observing the
value also changed it.
So what exactly is the right way of doing this?
Let's take Course.Students as an example. The number of Students can
change from one minute to next as they enroll or drop the course.
So I want the Course.Students property to be refreshed from the database
everytime it is referenced.
What is wrong with that approach. Why should Course.Students be
expected to stay static over the life time?
*** Sent via Developersdex http://www.developersdex.com ***
"David C" <no*******@nospam.com> wrote in message
news:es**************@TK2MSFTNGP09.phx.gbl... What is wrong with that approach.
Well, the problem you reported for one thing. ;)
Why should Course.Students be expected to stay static over the life time?
I wouldn't expect it to stay static over the life time, but I would want
control over when it was refreshed instead of having it in the accessor.
What happens if you write some code that accesses the property in a tight
loop?
I don't see anything wrong with your approach. It is just that the debugger
gets in the way, as if two threads were updating the same object without
using locking. Why not code up a method, "getStudents()", and use that in
your code, replacing the "get" accessor. Then the debugger will be happy.
Alternatively, there might be some way to change the "get" accessor during
debugging, via #if DEBUG logic. But that would probably be too much of a
change between the debug and production code to be useful.
"David C" <no*******@nospam.com> wrote in message
news:es**************@TK2MSFTNGP09.phx.gbl... Microsoft didn't intend you to write properties which had the sort of quantum property that >observing the value also changed it.
So what exactly is the right way of doing this?
Let's take Course.Students as an example. The number of Students can change from one minute to next as they enroll or drop the course.
So I want the Course.Students property to be refreshed from the database everytime it is referenced.
What is wrong with that approach. Why should Course.Students be expected to stay static over the life time?
*** Sent via Developersdex http://www.developersdex.com *** as I've explained to you last time this was posted, for >debugger to
displayyour fields and properties, it has to invoke it.
As it was found out, not always when the debugger is running. Only when
the autos tab is active, which is not exactly what you said.
*** Sent via Developersdex http://www.developersdex.com ***
I would propose that you create a method, RefreshStudentCourses, or
something like that, and then call it at strategic points throughout
your application.
I find that you usually get cleaner, easier to read, easier to maintain
code by modeling exactly what's going on in real life, rather than
playing tricks behind the scenes.
For example, you could call RefreshFromDatabase(), which in turn calls
RefreshStudentCourses(), every time the user starts a new business
operation, a top-level action.
Another problem with your original solution is that you can't depend
upon your data being self-consistent throughout an entire top-level
operation. Half-way through doing something, you could call your
Course.Students accessor and the list could change, which might put it
out of synch with other data that you've gathered up to that point,
making your top-level operation crash, or, even worse, do subtly wrong
things.
If you have an explicit call to refresh from the database, and call it
at strategic points, you can assume that between those calls your data
will not shift under your feet, which will make your whole program
simpler.
>I would propose that you create a method, RefreshStudentCourses, or something like that, and then call it at strategic points throughout your application.
I hear you, but that will result in a lot of more methods. For example,
my Course object can have a lot of other collection properties
- Students
- Assigments
- Chairs
- Desks
- TeachingAssistants
- Exams
- Handouts
So are you suggesting a method for every one of those? Surely you don't
want one RefreshFromDataBase() to refresh all of them in one swoop,
because that will make a lot unnecessary trips to all of them only the
only thing you are interested in is, say, Exams.
I would rather do this when I use Students in case you need to retain
it.
students = course.Students;
//at this point, students does not get refreshed
//any more and I can do whatever I want.
Console.Write(students.Count); //no trip to database
Console.Write(course.Students.Count); //a refresh from database
*** Sent via Developersdex http://www.developersdex.com ***
It's not just a case of the debugger not working properly. Refreshing
data on access also makes the application more complex by removing
common assumptions about how code works. Take the following example.
You're looping through the courses for a student, and your loop does
some work (as most loops do). As part of that work, it calls methods
and reads properties on other classes. One the methods on some other
class needs to know what courses a student has, so naturally it invokes
the accessor for student courses. Boom! Your loop now dies randomly
because in the midst of all of this someone else added a new course for
the student to the underlying database.
The lesson here is that you want your internal data to remain stable
throughout a single top-level business operation. Most programmers
simply assume that lists won't be changing at random points in the
code, so making them do so makes the program harder to understand,
because it breaks those common assumptions. Therefore, it's far better
to separate the operation of refreshing internal state, and invoke it
just before you start a new top-level business operation. That way,
your business operation has information at least as recent as the
moment it started, and that information remains stable throughout the
operation.
I don't see that this latter approach introduces any new complexities,
either. True, someone could modify the database between the start of a
business operation and the time you try to write back to the database,
making your write fail (because it violates database constraints, such
as trying to add the same course again for the same student). However,
this would occur even if you refreshed your internal state up to the
moment before you did your update: someone else could still modify the
database in that short moment between your read and your update, so you
have to cope with that, anyway.
You must always assume that any write back to the database could fail
because the database is no longer in a valid state to receive that
update, but you would have to assume that regardless, so extending that
time period back to the beginning of the top-level operation doesn't
introduce any new complexity.
> So are you suggesting a method for every one of those?
Well, the code is there anyway, it's just not in a separate method. Is
it really that much more work to move the code into its own method?
In thinking over what you're doing, I would separate the data into
three groups:
1. Reference data that doesn't change. Read this once and cache it for
the duration of the program.
2. Data that could be changed for others, but which is needed for the
"main screen". You need to cache and refresh this.
3. Data that you need in order to do business operations, but which,
once the top-level operation is done, you don't need it any more. I
would read this once at the beginning of the business op, use it to
perform the operation, then throw it away.
So, yes, a method to fetch each kind of information from the DB, then
work with it in memory, then chuck it. Why would you hold it after
you're done one operation ("Add a course to a student," for example),
if you're worried about it going stale? Read it at the start, let it be
invariant throughout the operation, then toss it, and read it again
next time.
Can't think of any thing to disagree with you on. Would love to see your
code sample to see how you implement these different approaches. So are you suggesting a method for every one of those?
Well, the code is there anyway, it's just not in a separate method. Is it really that much more work to move the code into its own method?
In thinking over what you're doing, I would separate the data into three groups:
1. Reference data that doesn't change. Read this once and cache it for the duration of the program.
2. Data that could be changed for others, but which is needed for the "main screen". You need to cache and refresh this.
3. Data that you need in order to do business operations, but which, once the top-level operation is done, you don't need it any more. I would read this once at the beginning of the business op, use it to perform the operation, then throw it away.
So, yes, a method to fetch each kind of information from the DB, then work with it in memory, then chuck it. Why would you hold it after you're done one operation ("Add a course to a student," for example), if you're worried about it going stale? Read it at the start, let it be invariant throughout the operation, then toss it, and read it again next time. This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: Iulian Ionescu |
last post by:
I have seen the following block of code used by many
people to loop through the items in a collection:
IEnumerator e;
IDisposable disposable1;
Object obj1;
e = SomeCollection.GetEnumerator();...
|
by: Robert Sentgerath |
last post by:
Foreach is a relative handy construct to avoid having to create the classic
"for(int loop = 0; loop < collection.Length; loop++) construct. Though when
I use foreach instead I do not have access to...
|
by: TrintCSD |
last post by:
How can I reset the collections within a foreach to be read as a change from
within the foreach loop then restart the foreach after collections has been
changed?
foreach(string invoice in...
|
by: David C |
last post by:
This is very strange. Say I have code like this. I am simply looping
through a collection object in a foreach loop.
Course course = new Course();
foreach(Student s in course.Students)
{...
|
by: Peted |
last post by:
I know you can iterate through a collection of radio buttons in a
panel, using a "for each in control" type iteration that c# supports,
but is it possible to iterate through the radio buttons...
|
by: Geoffrey |
last post by:
Hello,
I work with .net remoting,to simplify, when the client connected, he send an
handle to an object, the object is used to exchange message and events. no
problem.
In my server, I keep a...
|
by: Mick Walker |
last post by:
I have a collection of objects populated from a SQL Server (sorted
descending). Each of these object has a date field.
I need a way to loop through these object starting from the newest
entry,...
|
by: Chad Scharf |
last post by:
Ok, as silly as it may sound, I have a situation where I am creating a
CompositeControl in ASP.NET 2.0, C#. I have the following code in the
CreateChildControls() method that build the control's...
|
by: gasfusion |
last post by:
Hey guys.
I'm building an object collection which will be a part of a Data Access Layer i am currently working on. However, i am having some issues iterating through a collection.
This is what...
|
by: CloudSolutions |
last post by:
Introduction:
For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
|
by: Faith0G |
last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
|
by: isladogs |
last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM).
In this session, we are pleased to welcome former...
|
by: ryjfgjl |
last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
|
by: taylorcarr |
last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
|
by: aa123db |
last post by:
Variable and constants
Use var or let for variables and const fror constants.
Var foo ='bar';
Let foo ='bar';const baz ='bar';
Functions
function $name$ ($parameters$) {
}
...
|
by: ryjfgjl |
last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
|
by: ryjfgjl |
last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
|
by: emmanuelkatto |
last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud.
Please let me know.
Thanks!
Emmanuel
| |