0

Question:
Why doesn't the dbSet.Add method work when I'm attempting to add an object that's linked to other objects in EF?

Details:
Some background on this: I'm a beginner with EF, and I'm learning how to use Code First to develop the data model.

I'm using VS2013 Express, with Entity Framework 6 added through NuGet to a basic MVC project.

I've created three classes for my data model: Class_oneOnly, Class_many, and Class_several. Class_oneOnly has a one-to-many relationship to the other two classes, and the other two classes have a many-to-many relationship to each other.

I'm using Code First, but for purposes of explaining the relationships, here's an entity relationship diagram (I drew this in a separate project, so there's no interference between Code-First and Model-First. Also, there is no pre-generated code...although perhaps I should have gone that way, I'm trying to learn Code First):
enter image description here

I can create new instances of each class.

However, when I try to perform the DbSet.Add method to add a new instance of class_many to the database, I get an error "NullReferenceException was unhandled by user code."

This is true, I don't have this in a try-catch block. But, even with a try-catch block, it still wouldn't explain the exception:
"An exception of type 'System.NullReferenceException' occurred in EF Test Code First.dll but was not handled in user code. Additional information: Object reference not set to an instance of an object."

The main question is: Why is it that a null reference is being thrown, if all three classes at this point have been defined? Shouldn't EF take care of assigning properties to classes that are linked by relationships defined in Code First?

Should I add something to my code to make this work?

Code Examples:
Setting up the EF DbSet:

using System.Data.Entity;

namespace EF_Test_Code_First.Models
{
    public class Context : DbContext
    {
        public Context()
            : base("name=MyContext")
        {
        }

        public DbSet<Class_many> Class_manys { get; set; }
    }
}

Class definitions:

public class Class_oneOnly
{
    [Key]
    public string SN { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Class_many> Class_manys { get; set; }
    public virtual ICollection<Class_several> Class_severals { get; set; }
}

public class Class_several
{
    public int ID { get; set; }
    public string Name { get; set; }

    public Class_oneOnly Class_oneOnly { get; set; }
    public virtual ICollection<Class_many> Class_manys { get; set; }
}

public class Class_many
{
    public int ID { get; set; }
    public string Name { get; set; }

    public virtual Class_oneOnly Class_oneOnly { get; set; }
    public virtual ICollection<Class_several> Class_severals { get; set; }
}

...and, last but not least, my test code that sets up the classes and attempts to use DbSet.Add method. Context dbContext is passed from the controller (and is a static Context, as defined in the class above):

public void myTest(Context dbContext)
{
    /* assign property values to oneOnly,
        * of which there is only one of these
        * in each many and each several.
        */

    Class_oneOnly oneOnly = new Class_oneOnly()
    {
        SN = "1",
        Name = "oneOnly1"
    };

    /* make sure ids are unique as the following
        * 'for' loop is iterated through
        */
    int startid;
    if (dbContext.Class_manys != null && dbContext.Class_manys.Count() > 0){
        startid = dbContext.Class_manys.Count();
    }
    else{
        startid = 0;
    }

    /* create and fill class_many and
        * class_several objects, using a new id.
        */
    for (int i = startid; i < 5; i++)
    {
        /* assign property values to many
            * and several.
            */
        Class_many many = new Class_many()
        {
            ID = i,
            Name = "many" + i.ToString()
        };

        Class_several several = new Class_several()
        {
            ID = i,
            Name = "several" + i.ToString()
        };

        /* assign further property values
            * that use navigation properties
            * to link between classes.
            */
        many.Class_oneOnly = oneOnly;
        several.Class_oneOnly = oneOnly;
        many.Class_severals.Add(several);

        /* add to EF
            */
        /*************************
            * Error occurs at this line
            *************************
            */
        dbContext.Class_manys.Add(many);
    }

    /* save
        */
    dbContext.SaveChanges();
}

I can provide code from other parts of the project on request.

7
  • Try creating the lists of the manys and the severals and assign them to the properties of the oneOnly, rather than the other way around. Then add the one to the context and save your changes. You can use the AddRange method to add lists, so you don't have to do it one by one. Commented Feb 20, 2015 at 17:01
  • @MattyM: doesn't solve problem, or address the question. I think the problem lies with something else. Commented Feb 20, 2015 at 18:19
  • When you debug, can you tell what is null? Commented Feb 20, 2015 at 20:37
  • @MattyM unfortunately debug doesn't provide much info on what is null... even the innerException is null, and can't be drilled into. The System.NullReferenceException Message is "Object reference not set to an instance of an object." Commented Feb 23, 2015 at 15:46
  • try adding a DbSet for your other two classes to your context. Commented Feb 23, 2015 at 22:05

1 Answer 1

3

Right, so I re-created your project and think I found the issue. First thing: I would recommend using your DbContext NOT as static, but rather in a using block. If you still have problems, that's what I did to try and troubleshoot, and what I always try to do. I think you will find it harder to track down problems later on if you've got a global, static context.

I hit a null reference error on many.Class_severals.Add(several); instead of where you said you did. I did use a quick Winforms project to troubleshoot, but I'm not sure why/how you would get a different error if you're using the code posted above... Anyhow, you have two options: you need to create a new collection if it's not already initialized, and you can do it in a constructor or (my preference) in the getter.

public class Class_many
    {
        public int ID { get; set; }
        public string Name { get; set; }

        private ICollection<Class_several> _severals;

        public virtual Class_oneOnly Class_oneOnly { get; set; }
        public virtual ICollection<Class_several> Class_severals { get
        {
            if (_severals == null) {_severals = new List<Class_several>();}
            return _severals;
        }
            set { _severals = value; }
        }
    }
Sign up to request clarification or add additional context in comments.

4 Comments

Excellent, the getter works! I guess I'm still learning about what EF scaffolds in the background, and what needs to be set up for it. As a side note, I appreciate the advice to put my context in a using block. asp.net/mvc/overview/getting-started/… shows using a static Context in the Controller, but msdn.microsoft.com/en-us/library/… and msdn.microsoft.com/en-us/data/jj193542 show using. Thanks again!
return _severals ?? new List<Class_several>(); is wrong, you need to set the value of _severals before you return.
@ScottChamberlain thank you, exactly correct. I ran into this issue with another question at stackoverflow.com/q/28767068/2658159. The general principle of this question is correct, but I think the way it is not executed well.
@ScottChamberlain you're right, must have missed your comment before. fixed it up now, thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.