I am not sure what the protocol is w.r.t. to changing your question but I guess it would be better if I change it as there is a lot of information and the original intent of asking the question has been lost.
I was originally having an issue while generating a database using EF (Code first approach / POCO classes). One of the issues was that I was making use of Constructors in my entity classes to initialize the members. After @RickStahl suggested that it is not required, I changed my implementation.
For the sake of readers, I didn't want the information to be lost as some of the old comments intend to adress that issue.
However, during the course of time since this thread was initially created, the situation has changed. I have been able to get over some of the issues.
I am having problem retrieving the content from the underlying database tables.
Exception: Object reference not set to an instance of an object.
foreach(PhoneNumber p in cd.Phones)
For the sake of ease in understanding the issue, I am pasting the whole source code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ConsoleApplication1
{
public class ContactData
{
[Key]
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<PhoneNumber> Phones { get; set; }
public IList<EmailAddress> Emails { get; set; }
}
public class PhoneNumber
{
[Key]
public int PhoneId { get; set; }
public int ContactId { get; set; }
public string Phone { get; set; }
public ContactData ContactData { get; set; }
public PhoneNumber()
{
}
}
public class EmailAddress
{
[Key]
public int EmailId { get; set; }
public int ContactId { get; set; }
public string Email { get; set; }
public ContactData ContactData { get; set; }
public EmailAddress()
{
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
namespace ConsoleApplication1
{
public class ContactContext : DbContext
{
public ContactContext()
: base("ContactDBContext")
{
Database.SetInitializer<ContactContext>(new DropCreateDatabaseIfModelChanges<ContactContext>());
}
public DbSet<ContactData> Contacts { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class ContactManager
{
ContactContext cDbContext = new ContactContext();
public IList<ContactData> GetContactList()
{
IQueryable<ContactData> cContactList = cDbContext.Contacts;
IList<ContactData> cListData = new List<ContactData>();
cListData = cContactList.ToList();
return cListData;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
ContactManager cMgr = new ContactManager();
foreach (ContactData cd in cMgr.GetContactList())
{
Console.WriteLine(cd.ContactId);
Console.WriteLine(cd.FirstName);
Console.WriteLine(cd.LastName);
foreach(PhoneNumber p in cd.Phones)
{
Console.WriteLine(p.Phone);
}
foreach (EmailAddress e in cd.Emails)
{
Console.WriteLine(e.Email);
}
}
Console.ReadKey();
}
I finally managed to find a solution to my own problem. But I want to extend my 'Thanks' to both @SteveGreene and @RickStahl for their valuable inputs.
@RickStahl gave a good input or recommendation of not making use of parameterized constructors in my Entity classes. Per Rick, the parameterized constructors do not work with EF.
@SteveGreene - I finally realized that you were pointing me into right direction. I was not able to get it at that time due to my lack of understanding of EF. However, after reading about the EF in detail and in particular about Eager loading and Lazy loading helped me finally.
The error related to 'Object reference not set to an instance of an object' was easy to resolve after I figured out that I had to instantiate the collection properties for 'Phones' and 'Emails' in the ContactData class constructor.
Please refer to the code change below.
public class ContactData
{
[Key]
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string BusinessName { get; set; }
public IList<PhoneNumber> Phones { get; set; }
public IList<EmailAddress> Emails { get; set; }
public ContactData()
{
//By instantiating Phones and Emails member collections below, resolved 'Object reference not set to an instance of an object' exception
Phones = new List<PhoneNumber>();
Emails = new List<EmailAddress>();
}
}
The next step was to populate data into related Entities as I was only able to populate the parent Entity Contact up to this point.
For that I had to discover from my research and understand about different approaches such as Eager loading, Lazy loading etc. towards loading data into related entities.
That is done by using the following statements in the ContactManager.GetContactList() method. I have used eager loading method as far as I understand. For lazy loading, you have to use virtual members for child classes such as Phone and Email in this case.
var phones = cDbContext.Contacts.Include("Phones").ToList();
var emails = cDbContext.Contacts.Include("Emails").ToList();
Here's a complete code of ContactManager class.
class ContactManager
{
ContactContext cDbContext = new ContactContext();
public IList<ContactData> GetContactList()
{
ContactData cd = new ContactData();
IQueryable<ContactData> cContactList = cDbContext.Contacts;
IList<ContactData> cListData = new List<ContactData>();
var phones = cDbContext.Contacts.Include("Phones").ToList();
var emails = cDbContext.Contacts.Include("Emails").ToList();
cListData = cContactList.ToList();
return cListData;
}
}
After learning about this I understood that @SteveGreene was pointing me into correct direction. Only issue that I ran into after all this was that I used a lambda expression with Include method. But I got a compile time exception 'Include method expects a string'. I tried using the following lambda expression with the include method. var phones = cDbContext.Contacts.Include(b => b.Phones).ToList(); // this was causing a compile time error 'Include method expects a string'.
After passing (as below) the name of the parameter as string with the name of 'Phones' and 'Emails' collection members of ContactData class, it worked fine.
var emails = cDbContext.Contacts.Include("Emails").ToList();
If any queries then please post a comment.