0

For my small web app project I've created a model called Company that includes basic company info & also a list of sales reps from different business partners. This is my Contact Model:

 public class Company
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Promo { get; set; } // Yes or No field
        public List<Contact> Contacts { get; set; }
        public class Contact
        {
            [Key]
            public int ContactID { get; set; }
            public int CompanyID { get; set; }
            public string ContactName { get; set; }
            public string ContactNumber { get; set; }
        }
    }

This is how I pass the data into my local database:

var companiesData = new Company[]
        {
            new Company
            {
             Name = "Some name", Promo="Y", Contacts = new List<Contact> { new Contact {ContactName="John Doe", ContactNumber="(828)292-2912", CompanyID=1}, 
           }}, // ... some more contacts
        };
        foreach (Company c in companiesData)
        {
            context.Companies.Add(c);
        }
        context.SaveChanges();

How do I load the contact list items onto the razor view? I'm looking at my database and it's showing a blank field for "Contacts". The rest of the data shows up just fine.

Vy Nguyen
  • 57
  • 1
  • 10
  • Your saving doesn't look right, `context.Companies.Add(c)` should be `context.Contacts.Add(c)`. And you need to save your new `Company` first so you have a `CompanyID` to save with the contact – user326608 Apr 26 '17 at 06:27
  • `Contacts` property is a `List` so it's not mapped to a field but rather to a foreign key in `Contacts` table that matches `ID` of the `Company`. Where do you see it as "blank field"? – granit Apr 26 '17 at 08:07
  • In the dbo.Companies table from the SQL Object Explorer. The column Contacts is blank while the rest has data (passed from the same object) – Vy Nguyen Apr 26 '17 at 08:10
  • Make sure when you are accessing the Companies Db Set - that you are using `Include`, such as `context.Companies.Include(p => p.Contacts).ToArray()`, then send that to the View as the model. If this resolves the issue then I can post this as the answer. – ColinM Apr 26 '17 at 12:52

3 Answers3

2

see https://dotnetfiddle.net/xb3g68

entities:

public class Company
{
    [Key]
    public int ID { get; set; }
    public string Name { get; set; }
    public string Promo { get; set; } 
    public virtual List<Contact> Contacts { get; set; }       
}

public class Contact
{
    [Key]
    public int ContactID { get; set; }
    [ForeignKey("Company")]
    public int CompanyID { get; set; }
    public virtual Company Company { get; set; }
    public string ContactName { get; set; }
    public string ContactNumber { get; set; }
}

test harness:

public static void Main()
{
    Console.WriteLine("Hello World");
    FakeDbContext context = new FakeDbContext();
    var companiesData = new Company[]
    {
        new Company
        {
            Name = "Some name", Promo="Y", Contacts = new List<Contact> { new Contact {ContactName="John Doe", ContactNumber="(828)292-2912", CompanyID=1}},
        },
        new Company
        {
            Name = "Another name", Promo="N",  Contacts = new List<Contact> { new Contact {ContactName="Jane Doe", ContactNumber="(828)292-2912", CompanyID=2}},
        },
    };
    foreach (Company c in companiesData)
    {
        context.Companies.Add(c);
        foreach (Contact contact in c.Contacts)
        {
            context.Contacts.Add(contact);  
        }
    }
    context.SaveChanges();
    Console.WriteLine("Save done.");
    var companies = context.Companies.ToList();
    Console.WriteLine("companies.Count: " + companies.Count);
    for (int i = 0; i < companies.Count; i++)
    {
        Console.WriteLine(string.Format("company[{0}].Name: {1}", i,     companies[i].Name));
        for (int j = 0; j < companies[i].Contacts.Count; j++)
        {               
Console.WriteLine(string.Format("company[{0}].Contacts[{1}].ContactName: {2}", i, j, companies[i].Contacts[j].ContactName));
        }
    }
    Console.WriteLine("Bye World");
}

output:

Hello World

Save done.

companies.Count: 2

company[0].Name: Some name

company[0].Contacts[0].ContactName: John Doe

company[1].Name: Another name

company[1].Contacts[0].ContactName: Jane Doe

Bye World

user326608
  • 2,210
  • 1
  • 26
  • 33
  • do you have the data saving to your db fine now - so contacts etc with the companyid reference set? – user326608 Apr 26 '17 at 08:17
  • 1
    just be careful with ID values - with a proper EF provider against a database you'll probably want `[DatabaseGenerated(DatabaseGeneratedOption.Identity)]` set below your `[Key]` attribute, and EF will create the ID value on context add. You'd then use that for the foreign key. Note that the db won't update until `SaveChanges` is called. The comment above about using `.Include()` is spot on as well. If you're using .NET Core, have a play with a SQLite InMemory tutorial project - I tried getting dotnetfiddle to run the .NET.4.5 versions of SQLite and Effort without a result – user326608 Apr 27 '17 at 02:18
2

Hi on my final project i did the same with viewmodel because is data from two different tables and give more freedom to change thinks in the future.So i create a a view Model

public class ViewModelContactCompany
{
     public String Name { get; set; }
     List<Contact> contacts { get; set; }
    //etc just a sample
}

Controller

public class Controler
{
//param id is a id from company
//to do a relationship
public Action ControlerDetailsContact(int? id)
{
    ViewModelContactCompany x = new ViewModelContactCompany();
    //Do a linq, sqlquery,etc

    x.Name = "sample"; //get name of company by id;

    for (;;)
    {
        //where id = contact.CompanyID
        //add a new object typeof contact to the viewmodel
        //with the data get from the relationship
        x.contacts.Add(new Contact());
    }

    //and in the final return object viewmodel  to the view 
    return View(x);
 }

}

Now the View

@model /patch.ViewModelContactCompany

and here you get the data on the object return on the controler

What is ViewModel in MVC?

Community
  • 1
  • 1
J.Fernandes
  • 87
  • 2
  • 10
1

So based on your description, it sounds like you are receiving the model - but the Contacts collection is null or empty.

This is because EntityFrameworkCore needs child sets to be included explicitly using Include on your context Db Set. See here for more information

The code below is an example of how to include child collections in your model with eager loading

public class CompanyController : Controller
{
    // This import is needed for using the 'Include' method.
    using Microsoft.EntityFrameworkCore;

    private ApplicationDbContext _context;
    public CompanyController(ApplicationDbContext context)
    {
        _context = context;
    }
    // GET: /<controller>/
    public IActionResult Index()
    {
        List<Company> viewModelData = _context.Companies.Include(p => p.Contacts).ToList();

        return View(viewModelData);
    }
}

public class Company
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Promo { get; set; } // Yes or No field
    public List<Contact> Contacts { get; set; }
    public class Contact
    {
        [Key]
        public int ContactID { get; set; }
        public int CompanyID { get; set; }
        public string ContactName { get; set; }
        public string ContactNumber { get; set; }
    }
}

And your Database Context

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);
    }

    public DbSet<Company> Companies { get; set; }
    public DbSet<Contact> Contacts { get; set; }
}

View file should set the model like so

@model List<Company>

<h3>Do something with 'Model' here</h3>
ColinM
  • 2,622
  • 17
  • 29
  • Please mark it as the answer when necessary, glad it worked. – ColinM Apr 26 '17 at 18:29
  • 1
    Anything reading data will have to use the `Include`, Creating data should be okay because you're inserting into the collection and saving. Updating and Deleting may need an `Include` depending on how you're deleting the data. – ColinM Apr 26 '17 at 19:07
  • For the Edit Post method, how do I modify this so that update the contacts? My code now only allows me to update company info. – Vy Nguyen Apr 26 '17 at 20:22
  • 1
    You will have to `Include` the Contacts, and then iterate through the contacts to update the values, or get by id via a LINQ method such as `FirstOrDefault`, when you've finished then call `Update` on the context Db Set, and then `SaveChangesAsync` – ColinM Apr 26 '17 at 20:33
  • Can you answer my question here? http://stackoverflow.com/questions/43644490/allow-user-to-edit-list-items-in-mvc – Vy Nguyen Apr 26 '17 at 21:04
  • I'm having trouble modifying the CREATE method. I left the GET method unchanged & used Include in POST. I also added forms for inputting contact info in the Create view. I've tested the form. My input for Company shows up on the view but not for Contact. Note: I used @Model.Contacts to pass the list entity into my razor view (ex. – Vy Nguyen Apr 27 '17 at 00:35
  • The Label will only display something like `System.Collections.Generic.List'1[Contact]` which is the default behavior of ToString that returns type name of your collection, you'll have to loop through the Contacts and create a label for each. You'll also have to `Include` the Contacts collection in your GET context query. – ColinM Apr 27 '17 at 07:42
  • I did try to use a for loop (@for(var rep = 0; rep < Model.Contacts.Count(); ++rep) -- but it returned an error message saying "Object reference not set to an instance of an object." – Vy Nguyen Apr 27 '17 at 16:41
  • `Model.Contacts` is possibly null, ensure it is Included in your GET method, and that the model is being passed with populated data. – ColinM Apr 27 '17 at 20:43