0

I'm playing around with MVC3, trying to create small test projects which simulate problems that I anticipate I will encounter in a larger project I'm migrating from PHP. I keep getting a "NullReferenceException" on instantiating the "model" variable in the project with the elements below:

Model:

public class MainModel
{
    public IEnumerable<Chemical> chemicals { get; set; }

}

public class Chemical
{
    public int ChemicalId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Hazard> Hazards { get; set; }

}

public class Hazard
{
    public int HazardId { get; set; }
    public string Description { get; set; }

    public virtual ICollection<Chemical> Chemicals { get; set; }
}

Controller:

public class MainController : Controller
{

    public ActionResult Index()
    {

        var h1 = new Hazard { HazardId = 1, Description = "Flammable" };
        var h2 = new Hazard { HazardId = 2, Description = "Carcinogen" };
        var h3 = new Hazard { HazardId = 3, Description = "Water Reactive" };

        var model = new [] {
            new Chemical { ChemicalId = 1, Name = "Benzene", Hazards = {h1, h2}},
            new Chemical { ChemicalId = 2, Name = "Sodium", Hazards = { h3 } },
            new Chemical { ChemicalId = 3, Name = "Chloroform", Hazards =  { h2 } },
            new Chemical { ChemicalId = 4, Name = "Water" }
        }; //NULL EXCEPTION THROWN HERE

        return View(model);
    }

}

So, I have two questions:

  1. Why do I keep getting the NullReferenceException, and how could it be fixed?
  2. In writing this code, I used this StackOverFlow Page as a starting point. Why is this okay, but what I wrote is not? Granted, I didn't test this code, but the author seems very competent.

Thanks!

Community
  • 1
  • 1
ChemProfMatt
  • 139
  • 10
  • 1
    Post your Index view please. I bet your Index view expects a Model of type "MainModel" while you pass a simple array of "Chemicals" to your Index view. I think you meant to instantiate a "MainModel" in your Controller action? – Simon Sep 10 '11 at 19:40
  • 1
    I agree with @Simon too; you're going to have another problem once your view gets rendered, because you're calling `View()` with an array of `Chemical[]`, not a `MainModel`. Fixing it would look like: `var model = new MainModel(){chemicals = new [] { ... chemicals ... }};` – Scott Rippey Sep 10 '11 at 21:32

3 Answers3

2

Actual problem has nothing to do with MVC. You are instantiating ICollection Hazards with anonymous object, and I believe compiler does not resolve this code properly so that it fails at runtime. Here is the quick fix:

var model = new [] {
    new Chemical { ChemicalId = 1, Name = "Benzene", Hazards = new List<Hazard>{h1, h2}},
    new Chemical { ChemicalId = 2, Name = "Sodium", Hazards = new List<Hazard>{ h3 } },
    new Chemical { ChemicalId = 3, Name = "Chloroform", Hazards =  new List<Hazard>{ h2 } },
    new Chemical { ChemicalId = 4, Name = "Water" }
};

Note usage of List<Hazards>{h1, h2} instead of just {h1, h2}.

Andrei
  • 55,890
  • 9
  • 87
  • 108
  • 1
    To keep the code similar to the rest, you could use `new [] {h1, h2} ` instead of using `new List ` – Scott Rippey Sep 10 '11 at 20:02
  • @Scott Ripley: Thanks... that does make it easier. Can I use this with pretty much any type of array-like structure? In other words, if my model were, say ICollection or IQueryable, would this still work? I can test it for myself, but I think what I'm looking for is a good reference which shows the distinctions between all of these array-like interfaces. – ChemProfMatt Sep 10 '11 at 20:08
  • @ChemProfMatt, you can use such syntax with any property/variable that implements `IEnumerable`. Here is the [reference](http://msdn.microsoft.com/en-us/library/bb384062.aspx). – Andrei Sep 10 '11 at 20:12
  • @ChemProfMatt Actually, the `new[]{h1,...}` syntax is not "array-like" ... is simply the short-hand way to create an actual array of Hazards, equivalent to `new Hazard[]{h1,...}`. And `Hazard[]` implements `IList`, which includes `ICollection<>` and `IEnumerable<>`. However, it does NOT implement `IQueryable`. – Scott Rippey Sep 10 '11 at 21:15
  • I'm sorry ... I would like to retract my statement about using `new[]` instead of `new List`. Using `new[]` creates a fixed-size array, which will throw an exception if you try to use `ICollection.Add`! Since the original property is `ICollection`, it makes the most sense to use `new List`. I'm sorry I filled up the comments with such a trivial syntax difference. – Scott Rippey Sep 10 '11 at 21:23
1

Why do I keep getting the NullReferenceException, and how could it be fixed?

The ICollection property Hazards is null initially - you have to use a constructor when you create your model:

var model = new[] {
    new Chemical { ChemicalId = 1, Name = "Benzene", Hazards = new List<Hazard>() {h1, h2}},
    new Chemical { ChemicalId = 2, Name = "Sodium", Hazards = new List<Hazard>(){ h3 } },
    new Chemical { ChemicalId = 3, Name = "Chloroform", Hazards =  new List<Hazard>(){ h2 } },
    new Chemical { ChemicalId = 4, Name = "Water" }
};

The code you currently have will try to add i.e. {h1,h2} to Hazards - which is null and throw a NullReferenceException. An alternative is to create a new empty Hazards collection in the Chemical constructor - then you can leave your current code unchanged:

public class Chemical
{
    public Chemical()
    {
        _hazards = new List<Hazard>();
    }
    //..
}
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • Sorry, I didn't show the View because code execution doesn't even get that far. I put a breakpoint in the controller, and the exception is returned on the line before the return statement (I just edited the code to show this). I've also run the model before, commenting out the line for water, and it still returns the exception. – ChemProfMatt Sep 10 '11 at 19:42
  • 2
    @ChemProfMatt `new Chemical { ChemicalId = 1, Name = "Benzene", Hazards = {h1, h2}},..` should be `new Chemical { ChemicalId = 1, Name = "Benzene", Hazards = new {h1, h2}},....` For each Hazards collection... – Major Byte Sep 10 '11 at 19:50
  • @BrokenGlass: Awesome, that fixes the problem! I'd say about 70% of the problems I'm running into now -- getting used to C# -- is related to issues like this involving arrays, collections, enumerables, etc. After a few weeks I still find their behavior confusing. – ChemProfMatt Sep 10 '11 at 20:02
1

Since you're coming from PHP, perhaps it would be more helpful to give you an overview of CSharp's syntax shortcuts!
They're really confusing at first glance because the syntax is SO similar. But after a quick intro, you'll learn to love them.

Collection Initializers

A Collection Initializer adds items to a collection. The syntax is new SOMECOLLECTION { value, value, value }

The traditional way to populate a collection:

var items = new List<int>();
items.Add(1);
items.Add(2);
items.Add(3);

The Collection Initializer way - this compiles to nearly identical code:

var items = new List<int>(){ 1, 2, 3 };

Side note: Array vs List - I'd say List is more commonly used because its size can change. But it's mostly personal preference. The short-hand way to create an array is: var items = new int[]{ 1, 2, 3 };, but you can also omit the type and the compiler will figure it out (aka implicitly-typed array): var items = new []{ 1, 2, 3 };

Object Initializers

An Object Initializer sets the properties of a new object. The syntax is new SOMEOBJECT { Property = value, Property = value }

The traditional way to populate the properties of an object:

var item = new Item();
item.First = "Chuck";
item.Last = "Norris";

The Object Initializer way:

var item = new Item(){ First = "Chuck", Last = "Norris" };

Anonymous Objects

I'm only mentioning this because the syntax for an anonymous object is extremely similar to the others, but does something much, much different. They are extremely confusing if you aren't aware of them!

The syntax looks like a combination of the Collection and Object Initializers: new { AnyPropertyName = value, anyVariableNameBecomesAProperty, someOtherObject.SomeProperty }

Let me explain! The compiler will create an "anonymous object" that has 3 properties, AnyPropertyName, anyVariableNameBecomesAProperty, and SomeProperty, and initializes the values the same way as the Object Initializer does. If you don't specify a property name, the name is inferred from the source.

I hope this helps you understand why new { h1, h2 } (new anonymous object) is WAY different from new []{ h1, h2 } (new array).

Scott Rippey
  • 15,614
  • 5
  • 70
  • 85
  • Thanks so much Scott for your very clear explanation of this. It is very useful. The problem with C#, like so many other things, is that it seems so clear when you're just reading about it, but applying it is what really shows whether you know it or not. I'm still a StackOverflow newbie, but I can't tell you how helpful everyone on this site has been. – ChemProfMatt Sep 12 '11 at 08:21