Entity Framework Lazy and Eager loading returns System.Data.Entity.DynamicProxies.PropertyName

I am learning Entity framework using Code First approach. I have two clases with a relationship of one to many:

public class Product
{
    [Key] 
    public int ProductID { get; set; } 
    public string ProductName { get; set; }

    //Foreign Key -----> To establish relationship btw two tables.
    public int CategoryID { get; set; }
    // A product belongs to a ----> Category
    public virtual ProductCategory Category { get; set; }

}

public class ProductCategory
{
    [Key] //PK
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
    // A Category ----> has many products (List of Products)
    public virtual List<Product> ProductList { get; set; }    
}

My DbContext class:

class ModelDBEntitiesContext2 : DbContext
{
    public ModelDBEntitiesContext2()
    {
        //turns off easy loading for all Entities---> use Eager loading to load related Entities by using "Include"
        this.Configuration.LazyLoadingEnabled = false;
    }

    public DbSet<Product> products { get; set; }
    public DbSet<ProductCategory> categories { get; set; }
}

I use linq method syntax to query for all the Products and all the Products with their category using the INCLUDE (Eager Loading) word as follow:

 ModelDBEntitiesContext2 db = new ModelDBEntitiesContext2();
 var pppp = db.products.Include(c => c.Category).ToList();

 //I bind it to a datagrid view            
 dataGridView.DataSource = pppp;

The result of both query on the dataGridView shows the Customer's column return System.Data.Entity.DynamicProxies.ProductCategory DataGridView Result

What am I really doing wrong? Why I can't retrieve the Category with all the Products even using the Include.


Solution 1:

By marking the child class navigation property as virtual, EF will wrap the reference with a proxy to enable lazy loading & change tracking, whether you eager load or not. If you remove the virtual declaration it will leave the reference as a "Category", however you will need to always eager-load that property or it will be returned as #null. However, in most cases you shouldn't do this because the point of using EF is for things like change tracking.

To expand on Peter & Robert's suggestion:

var pppp = db.products.Include(p => p.Category).ToList();
foreach(var p in pppp)
{
  if (p.Category != null)
    Console.WriteLine(p.Category.CategoryName); // not WriteLine(p.Category)
}

Either that or add the following to your Category class:

public override string ToString()
{
   return CategoryName;
}

When you pass an object into functions like WriteLine etc. where they expect to work with strings, that code will call .ToString() against other types. The default behaviour of .ToString() on a class is to return the type name.