在CTP4 Code First中使用CreateSourceQuery

时间:2010-08-03 16:10:09

标签: entity-framework-4

我猜这是不可能的,但无论如何我会把它扔出去。在CTP4中使用EF4 CodeFirst API进行编程时是否可以使用CreateSourceQuery?我想热切地加载附加到属性集合的属性,如下所示:

var sourceQuery = this.CurrentInvoice.PropertyInvoices.CreateSourceQuery();
sourceQuery.Include("Property").ToList();

但是当然CreateSourceQuery是在EntityCollection<T>上定义的,而CodeFirst是使用普通的ICollection(显然)。有没有办法转换?

我已经完成了以下工作,但这并不是我想要的。任何人都知道如何从下面的内容到上面的内容(下面的代码来自一个继承DbContext的类)?

ObjectSet<Person> OSPeople = base.ObjectContext.CreateObjectSet<Person>();
OSPeople.Include(Pinner => Pinner.Books).ToList();

谢谢!

编辑:这是我的版本,由zeeshanhirani发布的解决方案 - 顺便说一下,这本书很棒!

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:

好的,我刚刚意识到你在使用动态时无法调度扩展方法。所以我猜我们不像那样像Ruby一样充满活力,但上面的例子很容易修改以符合这个限制

EDIT3:

正如zeeshanhirani博客文章中所提到的,只有当(并且仅当)您拥有启用了更改的代理时,这才会生效,如果您的属性的所有被声明为虚拟,则会创建代理。这是使用带有POCO的CreateSourceQuery

的方法的另一个版本
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();
    }

2 个答案:

答案 0 :(得分:4)

可以向您派生的上下文添加一个方法,该方法为实体实例上的给定导航创建源查询。为此,您需要使用底层的ObjectContext,其中包含一个关系管理器,它为每个导航公开底层实体集合/引用:

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();
}

你可以得到想象并接受导航属性的Func以避免必须指定T,但以下是如何使用上述函数:

using (var ctx = new ProductCatalog())
{
    var food = ctx.Categories.Find("FOOD");
    var foodsCount = ctx.CreateNavigationSourceQuery<Product>(food, "Products").Count();
}

希望这有帮助!

〜罗文

答案 1 :(得分:3)

绝对有可能这样做。如果您使用virtual关键字标记了集合属性,那么在运行时,ICollection的实际具体类型将是EntityCollection,它支持CreateSourceQuery以及随附的所有好东西默认代码生成器。我就是这样做的。

public class Invoice
{
    public virtual ICollection PropertyInvoices{get;set}
}

dynamic invoice = this.Invoice;
dynamic invoice = invoice.PropertyInvoices.CreateSourceQuery().Include("Property");

我写了一篇关于类似内容的博文。请注意,依靠ICollection的内部实现转换为EntityCollection并不是一种好习惯。 以下是您可能会发现有用的博客文章

http://weblogs.asp.net/zeeshanhirani/archive/2010/03/24/registering-with-associationchanged-event-on-poco-with-change-tracking-proxy.aspx