StackOverflowException in setter with backing property - c#

UPDATE: I have fixed the problem I was experiencing but I don't know why the bug generated the stack trace that it did. The stack trace lead me in the completely wrong direction. If anyone can explain what was happening here I would appreciate it (and will mark your answer as accepted). Note that my original post has been deleted.
I had the following class. Non-relevant parts of it have been removed:
class ClassName {
private string[] _accountTypes = new string[2] {"ECOM", "MOTO"};
private Dictionary<string, string> _settleDueDateDictionary = new Dictionary<string, string>() {
{"0", "Process immediately."},
{"1", "Wait 1 day"},
{"2", "Wait 2 days"},
{"3", "Wait 3 days"},
{"4", "Wait 4 days"},
{"5", "Wait 5 days"},
{"6", "Wait 6 days"},
{"7", "Wait 7 days"},
};
private string _settleDueDate;
private string _accountTypeDescription;
public string SettleDueDate
{
get
{
DateTime today = DateTime.Today;
long settleDueDate = Convert.ToInt64(_settleDueDate);
return today.AddDays(settleDueDate).ToString("MM/dd/yyyy");
}
set
{
if (!_settleDueDateDictionary.ContainsKey(value)) {
// TODO - handle
}
_settleDueDate = value;
}
}
public string AccountTypeDescription
{
get {
//return AccountTypeDescription; // This would cause infinite recursion (not referring to backing property).
return _accountTypeDescription; // This fixed the StackOverflowException I was faxed with
}
set
{
if (!_accountTypes.Contains(value))
{
// TODO - handle
}
_accountTypeDescription = value;
}
}
}
I also had this class which took an instance of the class above and created an XML string using values from the instance:
class SecondClass
{
private ClassName classnameInstance;
public SecondClass(ClassName instance)
{
classnameInstance = instance;
}
public string PrepareRequest(XMLWriter writer)
{
writer.WriteElementString("accounttypedescription", classnameInstance.AccountTypeDescription);
}
}
Here is the client code that generated the stack trace:
STPPData STPP = new STPPData();
STPP.SiteReference = _secureTradingWebServicesPaymentSettings.SiteReference;
STPP.Alias = _secureTradingWebServicesPaymentSettings.Alias;
STPP.SettleDueDate = Convert.ToString(_secureTradingWebServicesPaymentSettings.SettleDueDate);
STPP.SettleStatus = _secureTradingWebServicesPaymentSettings.SettleStatus;
STPPXml STPPXml = new STPPXml(STPP);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Async = false;
var builder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
string xmlRequest = STPPXml.PrepareRequest(writer);
}
Finally, here is the stack trace:
mscorlib.dll!string.GetHashCode()
mscorlib.dll!System.Collections.Generic.GenericEqualityComparer<System.__Canon>.GetHashCode(SYstem.__Canon obj)
mscorlib.dll!System.Collections.Generic.Dictionary<string,string>.FindEntry(string key)
mscorlib.dll!System.Collections.Generic.Dictionary<System.__Canon,System.__Canon>.ContainsKey(System.__Canon key)
ClassName.SettleDueDate.set(string value)
ClassName.SettleDueDate.set(string value)
ClassName.SettleDueDate.set(string value)
// Infinite recursion of this call
This stack trace lead me to believe that I had incorrectly implemented the getter/setter for STPP.SettleDueDate. I checked them and the backing variable etc. was correct (the usual causes for loops in getters/setters, I understand). Further debugging showed me that the stack trace was actually generated when this line of PrepareRequest() was called:
writer.WriteElementString("accounttypedescription", STPPData.AccountTypeDescription);
I discovered that I had incorrectly implemented the getter for STPPData.AccountTypeDescription because I had created a backing property that I used in the setter but I was NOT using the backing property in the getter:
public string AccountTypeDescription
{
get {
//return AccountTypeDescription; // This would cause infinite recursion.
return _accountTypeDescription; // This fixed the StackOverflowException
}
// setter omitted for clarity (it is in the examples above)
}
My question is:
Why did the stack trace of the StackOverflowException point me to SettleDueDate.set() when the bug was actually inside AccountTypeDescription.get()?
Note: I'm new to C# and am coming from a LAMP background. I have simplified the code a little but I do not think I have removed anything important.

Below are some simple debugging steps that should narrow down the problem
Open up the class with SettleDueDate property
Right click on the property name for SettleDueDate
Click on the menu item 'Find all references'
Every place that SettleDueDate is set ie 'SettleDueDate = "Something or other"' add a breakpoint
Run the application and keep continuing when breakpoints are hit till one gets hit multiple times in a row
When you found the offending point and the code is on a breakpoint instead of continuing use the Step Out command and Step over commands to trace your way back up the stack to find out where it is being assigned recursively

This code is very fragmentary and I'm not sure I quite understand all the connections. I'm assuming that ClassName == STPPData and SecondClass == STPPXml? Nonetheless, I tried reproducing this bug using VS2010 and .NET 4. I was unable to - the stack trace showed infinite recursion only within AccountTypeDescription.set(). There has to be something missing.
First of all, these lines in the stack trace are very interesting:
mscorlib.dll!string.GetHashCode()
mscorlib.dll!System.Collections.Generic.GenericEqualityComparer<System.__Canon>.GetHashCode(SYstem.__Canon obj)
mscorlib.dll!System.Collections.Generic.Dictionary<string,string>.FindEntry(string key)
mscorlib.dll!System.Collections.Generic.Dictionary<System.__Canon,System.__Canon>.ContainsKey(System.__Canon key)
They appear to definitively show the innards of SettleDueDate.set(), not just infinite calls to it. The dictionary and hash lookup are present. You definitely have a bug somewhere in there. However I have a hunch that your source code does not contain the bug. In reference to #Bryan's answer, did you set your breakpoints inside the SettleDueDate setter as opposed to places in your code where you call it? If you're using visual studio, you can also break on exceptions using this feature.
I saw that you are doing stuff with web services and that immediately makes me think of proxy classes. They can look an awful lot like code you write, but it isn't code you write. They can go stale. How sure are you that this exception was thrown by code you wrote and compiled?
Secondly, web services also make me think of serialization, a process that will often call property getters and setters without your knowledge. I've had issues in the past with WCF trying to serialize IEnumerables and failing horribly even though my code was just fine.
As an aside, on my system this line did not compile:
if (!_accountTypes.Contains(value))
This made me wonder if you are using Mono or a different IDE than me. You are a LAMP guy after all =)
I know this isn't a real answer (yet), but I'm wondering what you make of this? Any other details you can share?

Related

StackFrame behaving differently in release mode

Here is my code:
public class UserPreferences
{
/// <summary>
/// The EMail signature.
/// </summary>
[UserPreferenceProperty(Category = "Email", DefaultValue = "My default value")]
public static string Signature
{
get
{
return UserPreferenceManager.GetValue();
}
set
{
UserPreferenceManager.SetValue(value);
}
}
}
public static string GetValue()
{
if (((VTXPrincipal)Thread.CurrentPrincipal).VTXIdentity.OperatorID == null)
{
throw new Exception("Missing Operator ID");
}
string value = string.Empty;
var frame = new StackFrame(1); ***** <------ problem here.....
var property = frame.GetMethod();
var propertyname = property.Name.Split('_')[1];
var type = property.DeclaringType; ***** <------ problem here.....
if (type != null)
{
var userPreference = typeof(UserPreferences).GetProperty(propertyname).GetCustomAttributes(true).FirstOrDefault() as UserPreferencePropertyAttribute;
if (userPreference != null)
{
string category = userPreference.Category;
string description = propertyname;
value = GetValue(category, description, ((VTXPrincipal)Thread.CurrentPrincipal).VTXIdentity.OperatorID);
if (value == null)
{
// always return something
return userPreference.DefaultValue;
}
}
else
{
throw new Exception("Missing User Preference");
}
}
return value;
}
Inside the GetValue method, StackFrame works differently in release mode vs. debug mode.
In debug mode, I correctly get the property name as signature
But in Release mode, property name is GetUserPreferenceValueTest because this is the test method that makes the calls as clients.
There fore my code works in debug mode but fails in release mode.
Q. How can I use StackFrame properly so it works in Debug vs. Release modes.
Q. Is there any other way to get calling property name and related information at run time?
I answered a similar question once, please read my answer here.
In short, this is a very bad design decision because your method is a hypocrite—it talks different to different callers but doesn't tell it in open. Your API should never, ever rely on who calls it. Also, the compiler can break the stack trace in an unexpected way due to language features like lambdas, yield and await, so even if this worked in Release mode, it would certainly break some day.
You're effectively building a complex indirection mechanism instead of using language feature designed for passing information to methods—method parameters.
Why do you use attributes? Do you read them elsewhere?
If you do, and you don't want to repeat "Email" both as parameter to GetValue call and attribute value, you may consider passing a property Expression<> to GetValue, which will extract the attribute. This is similar to your solution, but it is explicit:
[UserPreferenceProperty(Category = "Email", DefaultValue = "My default value")]
public string Signature
{
get { return GetValue (prefs => prefs.Signature); }
set { SetValue (prefs => prefs.Signature, value); }
}
This answer shows how to implement this.
I see you are checking Thread.CurrentPrincipal in your code. Again, this is not a really good practice because it is not obvious to client code that accessing a property can result in an exception. This is going to be a debugging nightmare for someone who supports your code (and trust me, your code may run for years in production, long after you move onto another project).
Instead, you should make VTXIdentity a parameter to your settings class constructor. This will ensure the calling code knows you enforce security on this level and by definition knows where to obtain this token. Also, this allows you to throw an exception as soon as you know something is wrong, rather than when accessing some property. This will help maintainers catch errors earlier—much like compile errors are better than runtime errors.
Finally, while this is a fun exercise, there are plenty performant and tested solutions for storing and reading configuration in C#. Why do you think you need to reinvent the wheel?
Assuming your problem survives the discussion of whether you could just use another library rather than rolling your own... if you find yourself using C# 5 &.NET 4.5, take a look at the CallerMemberName attribute. With CallerMemberName you can modify your GetValue() method signature to be
public static string GetValue([CallerMemberName] string callerName = "")
The property can then call GetValue() with no parameter and you'll get the property name passed into GetValue() as you want.

Current state object - C#

For my current 'testing the waters' project, I'm trying to not use any Try-Catch blocks but instead catch each error (other than fatal) in other ways.
Now, when I say catch errors, my very contrived program makes one error which is easy to avoid; It tries to divide by 0 and this can be prevented by an If statement. To keep it simple I have only 1 C# file, with 1 class and two methods. I guess this is like a template, where the Constructor starts a process:
public class myObject
{
public myObject()
{
Object objOne = methodOne();
methodThree(objOne);
}
public object methodOne()
{
//logic to create a return object
int x = 0;
//I've added a condition to ensure the maths is possible to avoid raising an exception when, for this example, it fails
if (x > 0)
int y = 5 / x;
return object;
}
public void procesObjects(Object objOne)
{
//logic
}
}
So, as you can see in methodOne() I've added the if statement to ensure it checks that the maths isn't dividing by 0. However, since I've caught it, my application continues which is not desired. I need a way to cease the application and log the failing for debugging.
So, this is what I think could work:
Create a class called Tracking which for this example, is very simple (or would a struct be better?).
public class Tracking
{
StringBuilder logMessage = new StringBuilder();
bool readonly hasFailed;
}
I can then update my code to:
public class myObject
{
Tracking tracking = new Tracking();
public myObject()
{
Object objOne = methodOne();
if (!tracking.hasFailed)
methodThree(objOne);
if (tracking.hasFailed)
ExteranlCallToLog(tracking);
}
public object methodOne()
{
//logic
int x = 0;
//I've added a condition to ensure the maths is possible to avoid raising an exception when, for this example, it fails
if (x > 0)
int y = 5 / x;
else
{
tracking.hasFailed = true;
tracking.logMessage.AppendLine("Cannot divide by 0");
}
//may also need to check that the object is OK to return
return object;
}
public void procesObjects(Object objOne)
{
//logic
}
}
So, I hope you can see what I'm trying to achieve but I have 3 questions.
Should my tracking object (as it is in this example) be a class or a struct?
I'm concerned my code is going to become very noisy. I'm wondering if when the system fails, it raises an event within the Tracking object which logs and then somehow closes the program would be better?
Any other ideas are very welcome.
Again, I appreciate it may be simpler and easier to use Try-Catch blocks but I'm purposely trying to avoid them for my own education.
EDIT
The reason for the above was due to reading this blog: Vexing exceptions - Fabulous Adventures In Coding - Site Home - MSDN Blogs
Seriously, Dave - try catch blocks are there for a reason. Use them.
Reading between the lines, it looks like you want to track custom information when something goes wrong. Have you considered extending System.Exception to create your own bespoke implementation suited to your needs?
Something along the lines of:-
public class TrackingException : System.Exception
{
// put custom properties here.
}
That way, when you detect that something has gone wrong, you can still use try/catch handling, but throw an exception that contains pertinent information for your needs.

Change object type at runtime maintaining functionality

Long story short
Say I have the following code:
// a class like this
class FirstObject {
public Object OneProperty {
get;
set;
}
// (other properties)
public Object OneMethod() {
// logic
}
}
// and another class with properties and methods names
// which are similar or exact the same if needed
class SecondObject {
public Object OneProperty {
get;
set;
}
// (other properties)
public Object OneMethod(String canHaveParameters) {
// logic
}
}
// the consuming code would be something like this
public static void main(String[] args) {
FirstObject myObject=new FirstObject();
// Use its properties and methods
Console.WriteLine("FirstObject.OneProperty value: "+myObject.OneProperty);
Console.WriteLine("FirstObject.OneMethod returned value: "+myObject.OneMethod());
// Now, for some reason, continue to use the
// same object but with another type
// -----> CHANGE FirstObject to SecondObject HERE <-----
// Continue to use properties and methods but
// this time calls were being made to SecondObject properties and Methods
Console.WriteLine("SecondObject.OneProperty value: "+myObject.OneProperty);
Console.WriteLine("SecondObject.OneMethod returned value: "+myObject.OneMethod(oneParameter));
}
Is it possible to change FirstObject type to SecondObject and continue to use it's properties and methods?
I've total control over FirstObject, but SecondObject is sealed and totally out of my scope!
May I achieve this through reflection? How? What do you think of the work that it might take to do it? Obviously both class can be a LOT more complex than the example above.
Both class can have templates like FirstObject<T> and SecondObject<T> which is intimidating me to use reflection for such a task!
Problem in reality
I've tried to state my problem the easier way for the sake of simplicity and to try to extract some knowledge to solve it but, by looking to the answers, it seems obvious to me that, to help me, you need to understand my real problem because changing object type is only the tip of the iceberg.
I'm developing a Workflow Definition API. The main objective is to have a API able to be reusable on top of any engine I might want to use(CLR through WF4, NetBPM, etc.).
By now I'm writing the middle layer to translate that API to WF4 to run workflows through the CLR.
What I've already accomplished
The API concept, at this stage, is somehow similar to WF4 with ActivityStates with In/Out Arguments and Data(Variables) running through the ActivityStates using their arguments.
Very simplified API in pseudo-code:
class Argument {
object Value;
}
class Data {
String Name;
Type ValueType;
object Value;
}
class ActivityState {
String DescriptiveName;
}
class MyIf: ActivityState {
InArgument Condition;
ActivityState Then;
ActivityState Else;
}
class MySequence: ActivityState {
Collection<Data> Data;
Collection<ActivityState> Activities;
}
My initial approach to translate this to WF4 was too run through the ActivitiesStates graph and do a somehow direct assignment of properties, using reflection where needed.
Again simplified pseudo-code, something like:
new Activities.If() {
DisplayName=myIf.DescriptiveName,
Condition=TranslateArgumentTo_WF4_Argument(myIf.Condition),
Then=TranslateActivityStateTo_WF4_Activity(myIf.Then),
Else=TranslateActivityStateTo_WF4_Activity(myIf.Else)
}
new Activities.Sequence() {
DisplayName=mySequence.DescriptiveName,
Variables=TranslateDataTo_WF4_Variables(mySequence.Variables),
Activities=TranslateActivitiesStatesTo_WF4_Activities(mySequence.Activities)
}
At the end of the translation I would have an executable System.Activities.Activity object. I've already accomplished this easily.
The big issue
A big issue with this approach appeared when I began the Data object to System.Activities.Variable translation. The problem is WF4 separates the workflow execution from the context. Because of that both Arguments and Variables are LocationReferences that must be accessed through var.Get(context) function for the engine to know where they are at runtime.
Something like this is easily accomplished using WF4:
Variable<string> var1=new Variable<string>("varname1", "string value");
Variable<int> var2=new Variable<int>("varname2", 123);
return new Sequence {
Name="Sequence Activity",
Variables=new Collection<Variable> { var1, var2 },
Activities=new Collection<Activity>(){
new Write() {
Name="WriteActivity1",
Text=new InArgument<string>(
context =>
String.Format("String value: {0}", var1.Get(context)))
},
new Write() {
//Name = "WriteActivity2",
Text=new InArgument<string>(
context =>
String.Format("Int value: {0}", var2.Get(context)))
}
}
};
but if I want to represent the same workflow through my API:
Data<string> var1=new Data<string>("varname1", "string value");
Data<int> var2=new Data<int>("varname2", 123);
return new Sequence() {
DescriptiveName="Sequence Activity",
Data=new Collection<Data> { var1, var2 },
Activities=new Collection<ActivityState>(){
new Write() {
DescriptiveName="WriteActivity1",
Text="String value: "+var1 // <-- BIG PROBLEM !!
},
new Write() {
DescriptiveName="WriteActivity2",
Text="Int value: "+Convert.ToInt32(var2) // ANOTHER BIG PROBLEM !!
}
}
};
I end up with a BIG PROBLEM when using Data objects as Variables. I really don't know how to allow the developer, using my API, to use Data objects wherever who wants(just like in WF4) and later translate that Data to System.Activities.Variable.
Solutions come to mind
If you now understand my problem, the FirstObject and SecondObject are the Data and System.Activities.Variable respectively. Like I said translate Data to Variable is just the tip of the iceberg because I might use Data.Get() in my code and don't know how to translate it to Variable.Get(context) while doing the translation.
Solutions that I've tried or thought of:
Solution 1
Instead of a direct translation of properties I would develop NativeActivites for each flow-control activity(If, Sequence, Switch, ...) and make use of CacheMetadata() function to specify Arguments and Variables. The problem remains because they are both accessed through var.Get(context).
Solution 2
Give my Data class its own Get() function. It would be only an abstract method, without logic inside that it would, somehow, translate to Get() function of System.Activities.Variable. Is this even possible using C#? Guess not! Another problem is that a Variable.Get() has one parameter.
Solution 3
The worst solution that I thought of was CIL-manipulation. Try to replace the code where Data/Argument is used with Variable/Argument code. This smells like a nightmare to me. I know next to nothing about System.reflection.Emit and even if I learn it my guess is that it would take ages ... and might not even be possible to do it.
Sorry if I ended up introducing a bigger problem but I'm really stuck here and desperately needing a tip/path to go on.
This is called "duck typing" (if it looks like a duck and quacks like a duck you can call methods on it as though it really were a duck). Declare myObject as dynamic instead of as a specific type and you should then be good to go.
EDIT: to be clear, this requires .NET 4.0
dynamic myObject = new FirstObject();
// do stuff
myObject = new SecondObject();
// do stuff again
Reflection isn't necessarily the right task for this. If SecondObject is out of your control, your best option is likely to just make an extension method that instantiates a new copy of it and copies across the data, property by property.
You could use reflection for the copying process, and work that way, but that is really a separate issue.

Getting The Method That A Method Was Called From? [duplicate]

This question already has answers here:
How can I find the method that called the current method?
(17 answers)
Closed 6 years ago.
Is it possible to determine the calling method name "Eat Pizza" in PostError?
I guess I could pass "EatPizza" as one of the arguments, but that would require changes each time the method name changes (unnecessary maintenance). But then, I wasn't even able to find the method name "EatPizza" in the context of "EatPizza" (using stacktrace, getframe, getmethod).
public void EatPizza(Pizza p){
if(p==null){ //A arbitrary made up error
Utilities.PostError();
}
else{
p.Slices -= 1;
}
}
...
public void PostError(){
//Basically posting to database the name of the method
//Tried this, didn't work: (new StackTrace(true)).GetFrame(5).GetMethod().Name
//Is it possible to determine the calling method name "Eat Pizza" in this context?
}
When I try different values (0 to StackTrace.FrameCount-1) in StackTrace.GetFrame, I get the following values, when I just want "EatPizza":
.ctor
ThreadStart
Main
_nExecuteAssembly
RunUsersAssemblyDebugInZone
You were on the right track with creating a StackTrace object, but you seem to have misunderstood the argument to GetFrame. Frames are numbered from the bottom-most frame, so:
GetFrame(0) would return PostError
GetFrame(1) would return the caller of PostError
So just try this:
var trace = new StackTrace(true);
WriteToDB(trace.GetFrame(1).GetMethod().Name);
Personally, I would prefer to get the entire stack trace rather than just the caller, so I'd do this:
var trace = new StackTrace(true);
WriteToDB(trace.ToString());
Is it possible to determine the calling method name "Eat Pizza" in PostError? I guess I could pass "EatPizza" as one of the arguments, but that would require changes each time the method name changes (unnecessary maintenance).
Calling PostError in all the methods in which something could go wrong is also "unnecessary maintenance". It also complicates the execution flow of your program, because you will have to check for errors all over the place, and high-level processes will have to check if the low level processes completed successfully.
It is better to use the exception handling structures provided by the CLR and C#.
The exact location in which the error occured is stored in the exception's StackTrace property.
pubic void BigDinnerEatingProcess()
{
try
{
WhateverHappensAtTheTopLevel();
}
catch (PizzaNotDeliveredException ex)
{
Utilities.PostError(ex);
MessageBox.Show("Dinner was not eaten. Please make sure the pizza is delivered.");
}
}
public void EatPizza(Pizza p)
{
if (p == null)
throw new PizzaNotDeliveredException();
p.RemoveOneSlice();
}
private void PostError(Exception ex)
{
string errorLocation = ex.StackTrace;
//...
}

c# code seems to get optimized in an invalid way such that an object value becomes null

I have the following code that exhibits a strange problem:
var all = new FeatureService().FindAll();
System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null");
System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException
The signature of the FindAll method is:
public List<FeatureModel> FindAll()
Stepping through the code I have confirmed that the return value from FindAll is not null, and as you can see from the Assert, the "all" variable is not null, yet in the following line it appears to be null.
The issue is not specific to failing when the ToString() method is called. I simplified it down to this reproducible example while trying to trace the root cause.
This may be a clue: in the debugger, the variable "all" appears in the Locals window with a value of "Cannot obtain value of local or argument 'all' as it is not available at this instruction pointer, possibly because it has been optimized away."
I considered trying one of the approaches documented elsewhere for disabling code optimization but this wouldn't really solve the problem since the release version of the code will still be optimized.
I am using Visual Studio 2010 with .NET 4.0.
Any thoughts?
UPDATE: per request, here is the entire method:
protected override List<FeatureModel> GetModels() {
var all = new FeatureService().FindAll();
var wr = new WeakReference(all);
System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null");
System.Diagnostics.Debug.WriteLine(wr.IsAlive);
System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException
return all;
}
As an FYI, the original implementation was simply:
protected override List<FeatureModel> GetModels() {
return new FeatureService().FindAll();
}
I originally encountered the null exception in the calling method. The code I posted was after tracing the issue for a while.
UPDATE #2: As requested, here is the stack trace from the exception:
at FeatureCrowd.DomainModel.FeatureSearch.GetModels() in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.DomainModel\FeatureSearch.cs:line 32
at FeatureCrowd.DomainModel.FeatureSearch.CreateIndex() in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.DomainModel\FeatureSearch.cs:line 42
at FeatureCrowd.DomainModel.FeatureService.CreateSearchIndex() in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.DomainModel\FeatureService.cs:line 100
at Website.MvcApplication.BuildLuceneIndexThread(Object sender) in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.Website\Global.asax.cs:line 50
at Website.MvcApplication.Application_Start() in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.Website\Global.asax.cs:line 61
After looking over the code over TeamViewer, and finally downloading, compiling and running the code on my own machine, I believe this is a case of compiler error in C# 4.0.
I've posted a question with request for verification, after managing to reduce the problem to a few simple projects and files. It is available here: Possible C# 4.0 compiler error, can others verify?
The likely culprit is not this method:
protected override List<FeatureModel> GetModels() {
var fs = new FeatureService();
var all = fs.FindAll();
var wr = new WeakReference(all);
System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null");
System.Diagnostics.Debug.WriteLine(wr.IsAlive);
System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException
return all;
}
But the method it calls, FeatureService.FindAll:
public List<FeatureModel> FindAll() {
string key = Cache.GetQueryKey("FindAll");
var value = Cache.Load<List<FeatureModel>>(key);
if (value == null) {
var query = Context.Features;
value = query.ToList().Select(x => Map(x)).ToList();
var policy = Cache.GetDefaultCacheItemPolicy(value.Select(x => Cache.GetObjectKey(x.Id.ToString())), true);
Cache.Store(key, value, policy);
}
value = new List<FeatureModel>();
return value;
}
If I changed the call in GetModels from this:
var all = fs.FindAll();
to this:
var all = fs.FindAll().ToList(); // remember, it already returned a list
then the program crashes with a ExecutionEngineException.
After doing a clean, a build, and then looking at the compiled code through Reflector, here's how the output looks (scroll to the bottom of the code for the important part):
public List<FeatureModel> FindAll()
{
List<FeatureModel> value;
Func<FeatureModel, string> CS$<>9__CachedAnonymousMethodDelegate6 = null;
List<FeatureModel> CS$<>9__CachedAnonymousMethodDelegate7 = null;
string key = base.Cache.GetQueryKey("FindAll");
if (base.Cache.Load<List<FeatureModel>>(key) == null)
{
if (CS$<>9__CachedAnonymousMethodDelegate6 == null)
{
CS$<>9__CachedAnonymousMethodDelegate6 = (Func<FeatureModel, string>) delegate (Feature x) {
return this.Map(x);
};
}
value = base.Context.Features.ToList<Feature>().Select<Feature, FeatureModel>(((Func<Feature, FeatureModel>) CS$<>9__CachedAnonymousMethodDelegate6)).ToList<FeatureModel>();
if (CS$<>9__CachedAnonymousMethodDelegate7 == null)
{
CS$<>9__CachedAnonymousMethodDelegate7 = (List<FeatureModel>) delegate (FeatureModel x) {
return base.Cache.GetObjectKey(x.Id.ToString());
};
}
Func<Feature, FeatureModel> policy = (Func<Feature, FeatureModel>) base.Cache.GetDefaultCacheItemPolicy(value.Select<FeatureModel, string>((Func<FeatureModel, string>) CS$<>9__CachedAnonymousMethodDelegate7), true);
base.Cache.Store<List<FeatureModel>>(key, value, (CacheItemPolicy) policy);
}
value = new List<FeatureModel>();
bool CS$1$0000 = (bool) value;
return (List<FeatureModel>) CS$1$0000;
}
Notice the 3 last lines of the method, here's what they look like in the code:
value = new List<FeatureModel>();
return value;
here's what Reflector says:
value = new List<FeatureModel>();
bool CS$1$0000 = (bool) value;
return (List<FeatureModel>) CS$1$0000;
It creates the list, then casts it to a boolean, then casts it back to a list and returns it. Most likely this causes a stack problem.
Here's the same method, in IL (still through Reflector), I've stripped away most of the code:
.method public hidebysig instance class [mscorlib]System.Collections.Generic.List`1<class FeatureCrowd.DomainModel.FeatureModel> FindAll() cil managed
{
.maxstack 5
.locals init (
[0] string key,
[1] class [mscorlib]System.Collections.Generic.List`1<class FeatureCrowd.DomainModel.FeatureModel> 'value',
[2] class [System.Data.Entity]System.Data.Objects.ObjectSet`1<class FeatureCrowd.DomainModel.Feature> query,
[3] class [mscorlib]System.Func`2<class FeatureCrowd.DomainModel.Feature, class FeatureCrowd.DomainModel.FeatureModel> policy,
[4] class [mscorlib]System.Func`2<class FeatureCrowd.DomainModel.FeatureModel, string> CS$<>9__CachedAnonymousMethodDelegate6,
[5] class [mscorlib]System.Collections.Generic.List`1<class FeatureCrowd.DomainModel.FeatureModel> CS$<>9__CachedAnonymousMethodDelegate7,
[6] bool CS$1$0000,
[7] char CS$4$0001)
...
L_009f: newobj instance void [mscorlib]System.Collections.Generic.List`1<class FeatureCrowd.DomainModel.FeatureModel>::.ctor()
L_00a4: stloc.1
L_00a5: ldloc.1
L_00a6: stloc.s CS$1$0000
L_00a8: br.s L_00aa
L_00aa: ldloc.s CS$1$0000
L_00ac: ret
}
Here's a screencast showing the debug session, if you just want the Reflector output, skip to about 2:50.
After Lasse discovered that the FindAll method was generating the wrong IL, I then came across another method that was also generating the wrong IL -- I also found the root cause and resolution.
The relevant line in the second method is:
var policy = Cache.GetDefaultCacheItemPolicy(dependentKeys, true);
Cache is my own object. The GetDefaultCacheItemPolicy method returns a System.Runtime.Caching.CacheItemPolicy object. The generated IL, however, looked like this:
Func<Feature, FeatureModel> policy = (Func<Feature, FeatureModel>) base.Cache.GetDefaultCacheItemPolicy(dependentKeys, true);
There are two projects in play here. The methods that are generating the wrong IL are in one project called DomainModel, and the Cache object is in a Utilities project, which is referenced by the first. The second project contains a reference to System.Runtime.Caching but the first does not.
The fix was to add a reference to System.Runtime.Caching to the first project. Now the generated IL looks correct:
CacheItemPolicy policy = base.Cache.GetDefaultCacheItemPolicy(dependentKeys, true);
The first method (that Lasse posted about in his answer) now also generates proper IL.
Hooray!
Left for posterity, this is not the problem.
See my new answer.
Here's what I believe.
Contrary to what you're saying, I believe the program is not in fact crashing in any of the lines posted, but instead crashes on one of the lines following them, which you haven't posted.
The reason I believe this is that I also believe you're doing a Release-build, in which case both Debug lines will be removed, since they're tagged with a [Conditional("DEBUG")] attribute.
The clue here is that the all variable has been optimized away, and this should only happen during a Release-build, not a Debug-build.
In other words, I believe the all variable is actuall null after all, and the Debug lines aren't executed, because they're not compiled into the assembly. The debugger is dutifully reporting that the all variable no longer exists.
Note that all of this should be easy to test. Just place a breakpoint on the first of the two Debug-lines that you've posted. If the breakpoint is hit, my hypothesis is most likely wrong. If it doesn't (and I'm going to guess that the breakpoint symbol shows up as a hollow circle at runtime), then those lines aren't compiled into the assembly.

Categories

Resources