简体   繁体   中英

Filtering a graph of entity framework objects

I'm trying to filter down the results returned by EF into only those relevant - in the example below to those in a year (formattedYear) and an ordertype (filtOrder)

I have a simple set of objects

PEOPLE 1-M ORDERS 1-M ORDERLINES

with these relationships already defined in the Model.edmx

in SQL I would do something like...

select * from PEOPLE inner join ORDERS on ORDERS.PEOPLE_RECNO=PEOPLE.RECORD_NUMBER inner join ORDERLINE on ORDERLINE.ORDER_RECNO=ORDERS.RECORD_NUMBER where ORDERLINE.SERVICE_YEAR=@formattedYear and ORDERS.ORDER_KEY=@filtOrder

I've tried a couple of approaches...

        var y = _entities.PEOPLE.Include("ORDERS").Where("it.ORDERS.ORDER_KEY=" + filtOrder.ToString()).Include("ORDERLINEs").Where("it.ORDERS.ORDERLINEs.SERVICE_YEAR='" + formattedYear + "'");

        var x = (from hp in _entities.PEOPLE 
                 join ho in _entities.ORDERS on hp.RECORD_NUMBER equals ho.PEOPLE_RECNO
                 join ol in _entities.ORDERLINEs on ho.RECORD_NUMBER equals ol.ORDERS_RECNO
                 where (formattedYear == ol.SERVICE_YEAR) && (ho.ORDER_KEY==filtOrder)
                 select hp
                );

y fails with ORDER_KEY is not a member of transient.collection... and x returns the right PEOPLE but they have all of their orders attached - not just those I am after.

I guess I'm missing something simple ?

Imagine you have a person with 100 orders. Now you filter those orders down to 10. Finally you select the person who has those orders. Guess what? The person still has 100 orders!

What you're asking for is not the entity, because you don't want the whole entity. What you seem to want is a subset of the data from the entity. So project that:

var x = from hp in _entities.PEOPLE
        let ho = hp.ORDERS.Where(o => o.ORDER_KEY == filtOrder
                                      && o.ORDERLINES.Any(ol => ol.SERVICE_YEAR == formattedYear))
        where ho.Any()
        select new 
        {
            Id = hp.ID,
            Name = hp.Name, // etc.
            Orders = from o in ho
                     select new { // whatever 
        };

I am not exactly sure what your question is but the following might be helpful.

In entity framework if you want to load an object graph and filter the children then you might first do a query for the child objects and enumerate it (ie call ToList()) so the childern will be fetched in memory.

And then when you fetch the parent objects (and do not use .include) enitity framework will able to construct the graph on its own (but note that you might have to disable lazy loading first or it will take long to load). here is an example (assuming your context is "db"):

db.ContextOptions.LazyLoadingEnabled = false;

var childQuery = (from o in db.orders.Take(10) select o).ToList();

var q = (from p in db.people select p).ToList();


Now you will find that every people object has ten order objects


EDIT: I was in a hurry when I wrote the sample code, and as such I have not tested it yet, and I probably went wrong by claiming that .Take(10) will bring back ten orders for every people object, instead I believe that .Take(10) will bring back only ten overall orders when lazy loading is disabled, (and for the case when lazy loading is enabled I have to actually test what the result will be) and in order to bring back ten orders for every people object you might have to do more extensive filtering.

But the idea is simple, you first fetch all children objects and entity framework constructs the graph on its own.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM