Why am I getting "Object reference not set to an instance of an object" after refactoring my code?
I have a table which has a few columns, one of which is an FK ID of the "owner".
In my delete statement, I have the following code:
public ActionResult DeleteEmailTemplate(int id = 0)
{
EmailTemplate EmailTemplate = db.EmailTemplates.Find(id);
Company _Company = SelectCompany();
if (_Company.id != EmailTemplate.CompanyID.id)
return Content("Security Error");
return Content("Passed... Do Delete stuff");
}
That works fine, but, I need to do this in many places, so I tried to extract the method and I now have:
public ActionResult DeleteEmailTemplate(int id = 0)
{
EmailTemplate EmailTemplate = db.EmailTemplates.Find(id);
if (!SecurityCheck(EmailTemplate.CompanyID.id))
return Content("Security Fail");
return Content("Passed... Do Delete stuff");
}
I think the code for SecurityCheck
is not relevant as I have been setting breakpoints and I know that the error is because when SecurityCheck
is called, EmailTemplate.CompanyID
is Null, but... I can't work out why it is Null.
In the first example, it is Null, but the moment I do SelectCompany()
and check EmailTemplate
, EmailTemplate.CompanyID
is set to the correct value, despite this method only populates the _Company
object and does not touch anything else.
In the second example, it is Null again but stays null.
So, the problems are that I need help with:
I can't figure out why it is Null to begin with when the database has the correct ID.
I can't figure out why it is getting the correct value in the first example after running SelectCompany()
.
And most importantly, what do I need to do to fix it?, I am guessing I have messed up somewhere big,开发者_开发百科 but I just don't see it.
I can't figure out why it is Null to begin with when the database has the correct ID.
This line ...
EmailTemplate EmailTemplate = db.EmailTemplates.Find(id);
... would never load a navigation property (CompanyID
in your case). Navigation properties are only eager loaded (with Include
), explicitely loaded or lazily loaded. You don't seem to have lazy loading enabled, otherwise accessing CompanyID
should load the property automatically. And you don't make use of the other two options.
I can't figure out why it is getting the correct value in the first example after running SelectCompany().
Probably because of relationship span. When you load the company into the context in your SelectCompany
method, Entity Framework fixes automatically references in other objects which are already loaded. For this reason EmailTemplate.CompanyID
gets populated automatically.
And most importantly, what do I need to do to fix it?,
Use one of the three options mentioned above:
Eager loading (one round trip):
EmailTemplate EmailTemplate = db.EmailTemplates.Include(e => e.CompanyID)
.Where(e => e.ID == id).SingleOrDefault();
Explicit loading (two round trips):
EmailTemplate EmailTemplate = db.EmailTemplates.Find(id);
db.Entry(EmailTemplate).Reference(e => e.CompanyID).Load();
Or enable lazy loading.
(In your special case I'd prefer eager loading.)
SelectCompany
must be exercising some code that populates CompanyID
. My guess is you are using an ORM with lazy loading and SelectCompany causes the CompanyID to get populated.
精彩评论