views:

273

answers:

1

I have a few projects:

  • A database interface project that defines Thingo (main solution)
  • A logic project that defines ThingoChooser (plugin solution)
  • A GUI project that refers to the logic project (plugin solution
  • A test project that refers to the logic project (plugin solution)

I'm debugging the plugin, so I run the main solution with its current working directory set to the bin\Debug directory targeted by all of the plugin projects. The main executable finds the plugin class in the GUI assembly and displays its main form.

In the test project, this code works fine:

this.chooser = new ThingoChooser();
foreach (var thingo in this.chooser.AvailableThingos) {
    Console.WriteLine(release);
}

The same code, posted into my GUI project, fails with a StackOverFlowException when AvailableThingos returns.

ThingoChooser.AvailableThingos looks like this:

    public IEnumerable<Thingo> AvailableThingos {
        get {
            // Yes, it DEFINITELY case-matches the private variable,
            // NOT the public property. Oh, I wish this were that easy!
            return this.availableThingos;
        }

        private set {
            // ...
        }
    }

… and to what kind of IEnumerable<Thingo> do I set this.availableThingos?

It's a List<Thingo>.

Yes, I have a WinForms app that throws StackOverFlowException when trying to walk a List<T>. :)

VS2008 is perfectly happy to let me inspect this.availableThingos before it's returned. When I click the step button: StackOverflowException, every time. It also happens out of the debugger.

Fine waves of the dead chicken I've tried, most suggested by commenters, included:

  • Looking at the stack trace for loops
  • Changing the return type of AvailableThingos to List<Thingo>
  • Removing var in case implicit typing stuffed me up
  • Making the property's backing store variable public and hitting it directly
  • Changing the backing store to a List<T>
  • Removing LINQBridge and re-targeting to .NET 3.5

The changes don't help, and the stack trace doesn't show any loops. After one click of the “Step Into” button after the } of the getter, I get the exception warning float-over reading:

StackOverflowException was unhandled

An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

The bug is at its most dramatic when I switch to .NET 3.5 and remove LINQBridge entirely and change the type of the backing store to List<Thingo> and access it directly and simply try this from the WinForms code:

List<Thingo> thingos = this.chooser.availableThingos.ToList();

Yep: calling .ToList() on a List<Thingo> can blow up with StackOverflowException.

A: 

The problem turned out to be with the Copy Local settings on the references.

The GUI project's reference to the database interface project had Copy Local set True. Its reference to the logic project had Copy Local set False. I suspect the logic project was returning an enumeration of a Thingo from a different DLL than the GUI project was seeing.

(Perhaps necessary for the problem to occur: when debugging, I'm running a fifth project which dynamically loads the DLL for the GUI project. That fifth project has its own copy of the database interface project DLL.)

The test project had Copy Local for both projects set True. Either the match helped, or just the fact that there was only one copy of the database interface DLL in the current working directory or on the path.

I'll happily accept the best answer describing why this caused StackOverflowException. I'd have strongly preferred a DuplicateCopiesOfAssemblyYouDoofusException.

For reference, here's the map of references and their Copy Local settings:

Main command project (run under debugger, dynamically loads GUI project from its bin\Debug directory):

  • Has database interface DLL in same directory as the main executable. This is the same directory as all database interface references below target in their hint path.

GUI project (failing configuration):

  • ref→ Database project: Copy Local = True
  • ref→ Logic project: Copy Local = False

Logic project:

  • ref→ Database project: Copy Local = False

Test project:

  • ref→ Logic project: Copy Local = True
  • ref→ Database project: Copy Local = True

In all fairness to the commenters, I didn't describe the solution layout in the question until five minutes ago, so I can't expect them to have solved it.

Garth T Kidd