I'm guessing this is impossible, but I'll throw it out there anyway. Is it possible to use CreateSourceQuery when programming with the EF4 CodeFirst API, in CTP4? I'd like to eagerly load properties attached to a collection of properties, like this:
var sourceQuery = this.CurrentInvoice.PropertyInvoices.CreateSourceQuery();
sourceQuery.Include("Property").ToList();
But of course CreateSourceQuery is defined on EntityCollection<T>
, whereas CodeFirst uses plain old ICollection
(obviously). Is there some way to convert?
I've gotten the below to work, but it's not quite what I'm looking for. Anyone know how to go from what's below to what's above (code below is from a class that inherits DbContext)?
ObjectSet<Person> OSPeople = base.ObjectContext.CreateObjectSet<Person>();
OSPeople.Include(Pinner => Pinner.Books).ToList();
Thanks!
EDIT: here's my version of the solution posted by zeeshanhirani - who's book by the way is amazing!
dynamic result;
if (invoice.PropertyInvoices is EntityCollection<PropertyInvoice>)
result = (invoices.PropertyInvoices as EntityCollection<PropertyInvoice>).CreateSourceQuery().Yadda.Yadda.Yadda
else
//must be a unit test!
result = invoices.PropertyInvoices;
return result.ToList();
EDIT2:
Ok, I just realized that you can't dispatch extension methods whilst using dynamic. So I guess we're not quite as dynamic as Ruby, but the example above is easily modifiable to comport with this restriction
EDIT3:
As mentioned in zeeshanhirani's blog post, this only works if (and only if) you have change-enabled proxies, which will get created if all of your properties are declared virtual. Here's another version of what the method might look like to use CreateSourceQuery with POCOs
public class Person {
public virtual int ID { get; set; }
public virtual string FName { get; set; }
public virtual string LName { get; set; }
public virtual double Weight { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
public class Book {
public virtual int ID { get; set; }
public virtual string Title { get; set; }
public virtual int Pages { get; set; }
public virtual int OwnerID { get; set; }
public virtual ICollection<Genre> Genres { get; set; }
public virtual Person Owner { get; set; }
}
public class Genre {
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual Genre ParentGenre { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
public class BookContext : DbContext {
public void PrimeBooksCollectionToIncludeGenres(Person P) {
if (P.Books is EntityCollection<Book>)
(P.Books as EntityCollection<Book>).CreateSourceQuery().Include(b => b.Genres).ToList();
}
It is possible to add a method to you derived context that creates a source query for a given navigation on an entity instance. To do this you need to make use of the underlying ObjectContext which includes a relationship manager which exposes underlying entity collections/references for each navigation:
public ObjectQuery<T> CreateNavigationSourceQuery<T>(object entity, string navigationProperty)
{
var ose = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(entity);
var rm = this.ObjectContext.ObjectStateManager.GetRelationshipManager(entity);
var entityType = (EntityType)ose.EntitySet.ElementType;
var navigation = entityType.NavigationProperties[navigationProperty];
var relatedEnd = rm.GetRelatedEnd(navigation.RelationshipType.FullName, navigation.ToEndMember.Name);
return ((dynamic)relatedEnd).CreateSourceQuery();
}
You could get fancy and accept a Func for the navigation property to avoid having to specify the T, but here is how the above function is used:
using (var ctx = new ProductCatalog())
{
var food = ctx.Categories.Find("FOOD");
var foodsCount = ctx.CreateNavigationSourceQuery<Product>(food, "Products").Count();
}
Hope this helps!
~Rowan
It is definately possible to do so. If you have marked you collection property with virtual
keyword, then at runtime, you actual concrete type for ICollection
would be EntityCollection
which supports CreateSourceQuery
and all the goodies that comes with the default code generator. Here is how i would do it.
public class Invoice
{
public virtual ICollection PropertyInvoices{get;set}
}
dynamic invoice = this.Invoice;
dynamic invoice = invoice.PropertyInvoices.CreateSourceQuery().Include("Property");
I wrote a blog post on something similar. Just be aware that it is not a good practice to rely on the inner implementation of ICollection
getting converted to EntityCollection
.
below is the blog post you might find useful
http://weblogs.asp.net/zeeshanhirani/archive/2010/03/24/registering-with-associationchanged-event-on-poco-with-change-tracking-proxy.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With