Question

the dbcontext api seems to set navigation properties to ICollections (for the * ends of associations). The normal way to get queryable objects (like if you wanted a count) seems to be

int count = dbcontext.Entry(entry).Collection(c => c.navprop).Query().Count();

but that's inconvenient if you want to filter in the db often. More importantly, it's also easy to forget. If someone accidentally says

int count = entry.navprop.Count();

then it gets all of the data on the server and does the count there, which is slow.

The same thing is true for the EntityCollection type that ObjectContext uses by default.

int count = entry.navprop.CreateSourceQuery().Count();

Is there a way to set in the model or somewhere else that the default collection type for a navigation property is an IQueryable or an ObjectQuery or some kind of queryable type?

Note this is only a problem for navigation properties as the actual objectset and dbset items in the context seem to be queryable

Was it helpful?

Solution

I came up with a solution that solves most of my problems. What I ended up doing was using the ObjectContext API and in my model and making the navigation property that was taking a long time to access private (for getting and setting). You can do this in the edmx file by right clicking on the nav property.

Then I made a partial class file for the class that contained the private navigation property and added something along the lines of this.

public ObjectQuery<NavPropType> NavPropName
{
   get
   {
      if(privateNavProp != null) //in case lazy loading is disabled or something
         return privateNavProp.CreateSourceQuery();
      else
         return null;
   }
}

Now no user of the class can accidentally try to pull in all of the navprop items, which there are a lot of. And it's also easy to make a query on the nav prop instead of having to remember to call CreateSourceQuery every time.

I didn't add a setter because that navigation property is read only in my application. I'm sure there's a way to make one that fits in with this pattern, but I don't know enough about the ObjectContext API to say how to do it.

I only ended up doing this for the navigation properties that could have a ton of data behind them because leaving the other ones as is had no effect on performance.

EDIT: One drawback of making it private that I ran into later is that I could no longer do

db.EntryTable.OrderBy(e => e.privateNavProp.Count())

even though it would have been smart enough to not get all of the Entities

OTHER TIPS

No. IQueryable is not a collection; IQueryable is an interface whose implementation evaluates queries against a data source (a collection would be the data source).

You could load the count when you load the entity object, however, if inlining some innocuous method calls isn't your thing:

from e in EntityA
<optional where clause for entity>
select new
{
    Entity = e,
    filteredNavPropCount = e.navprop.Where( np => <optional where clause for collection> ).Count()
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top