views:

263

answers:

2

Hi, I just started playing around with the CTP4 and Code-First. I have the following setup for a possible dating site:

public class User
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string LoginName { get; set; }
    [Required]
    public string Firstname { get; set; }
    [Required]
    public string Lastname { get; set; }

    public string Street { get; set; }
    [Required]
    public string Zip { get; set; }
    [Required]
    public string City { get; set; }
    [Required]
    public bool Gender { get; set; }
    [Required]
    public int SoughtGender { get; set; }
    [Required]
    public string Password { get; set; }
    [Required]
    public double Latitude { get; set; }
    [Required]
    public double Longitude { get; set; }
}

 public class Vote
{
    [Key]
    public int ID { get; set; }
    [Required]
    public User Voter { get; set; }
    [Required]
    public User TargetUser { get; set; }
    [Required]
    public int Decision { get; set; }
    [Required]
    public DateTime Timestamp { get; set; }
}

 public class MySQLContext : DbContext
{
    public MySQLContext (string constring)
        : base(constring)
    { }

    public DbSet<User> Users { get; set; }
    public DbSet<Vote> Votes { get; set; }

    protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Vote>().HasRequired(b => b.Voter).WithMany();
        modelBuilder.Entity<Vote>().HasRequired(b => b.TargetUser).WithMany();
        base.OnModelCreating(modelBuilder);

    }
}

Now the framework does a nice job of creating the DB with all the proper keys. Now I inserted some dummy data and fired up the following query:

public override IEnumerable<Domain.Vote> FindVotes(Domain.User user)
    {
        var query = from v in context.Votes where v.Voter.Id == user.Id select v;
        return from v in query.AsEnumerable() select v;
    }

The query does return the proper Vote entities but the two User properties of the Vote object are Null. Shouldnt the framework populate those properties with the foreign keys of the users referenced in the Vote table?

+1  A: 

change your class to the follow

public class Vote {
    [Key]
    public int ID { get; set; }
    [Required]
    public virtual User Voter { get; set; }
    [Required]
    public virtual User TargetUser { get; set; }
    [Required]
    public int Decision { get; set; }
    [Required]
    public DateTime Timestamp { get; set; }
}

Notice I've added virtual to the Voter && TargetUser properties and you should be good to go.

BuildStarted
Works like a charm, thank you. What's the reason behind this?
Malkier
It's to handle lazy loading. When a property is marked as virtual the framework class generator creates a derived proxy class that overrides the property for lazy loading.
BuildStarted
Though after thinking about it I'm not entirely sure how it works with the code-first approach...something I'll have to look up :)
BuildStarted
http://msdn.microsoft.com/en-us/library/dd468057.aspx just explains the requirements for POCOs to work with EF...not exactly *why*...but I'm under the impression that it generates proxy classes on the model instantiation.
BuildStarted
Starting to make sense now, thank you.
Malkier
+2  A: 

Let me give you some background on EF so you can understand how this works. EF from day 1 only supported explicit load like one below

Customer.Orders.Load();

Hmm, the feedback was not welcomed by the community and developers wanted lazy loading. To support Lazy Loading EF team said you must mark your navigation property as virtual. So at runtime, Ef creates a proxy object that derives from your entity and overrides the virtual property. Below is an example of such code.

public class Customer
{
   public string Name{get;set;}
   public virtual ICollection<Order> Orders{get;set;}
}

At runtime there is a proxy that implements IEntityWithChangeTracker and concrete type of the collection is an entitycollection which has been around since version 1.

public class CustomerProxy:Customer,IEntityWithChangeTracker
{
private ICollection<Order> orders;
   public override ICollection<Order> Orders
   {
        if(orders == null)
       {
           orders = new EntityCollection<Order>();
           orders.Load();
       }
       return orders;
   }
}
zeeshanhirani
Thank you for providing additional insight.
Malkier