I'm not sure if this is the right thing to do, I'm sure someone will tell me if it's not.
I asked a question (Entity Framework - Include in sub query?) earlier this evening, which was answered very well and has solved my problem. But, I think there could be a better way, so I'm going to re-ask the question, but slightly differently.
Let's say I have 3 tables:
Restaurant 1.....M MenuCategory 1.....M MenuItem
I have a L2E query that looks something like this:
Restaurant = context.Restaurant
.Include(r => r.MenuCategory)
.FirstOrDefault(r => r.RestaurantId == resaurantId);
Which works to some extent, but it only pre-loads the menu categories.
What I really want to be able to do is something like:
Restaurant = context.Restaurant
.Include(r => r.MenuCategory)
.Include(r => r.MenuCategory.MenuItems)
.FirstOrDefault(r => r.RestaurantId == resaurantId);
But clearly this isn't available as r.MenuCategory is an enumerable
...the work around is to use the standard notation:
context.Restaurant.Include("MenuCategory.MenuItems");
...but this is not strongly typed. This question is about finding a strongly typed answer
This is the current extension method:
public static ObjectQuery<T> Include<T>(this ObjectQuery<T> query, Expression<Func<T, object>> path)
{
// Retrieve member path:
List<PropertyInfo> members = new List<PropertyInfo>();
EntityFrameworkHelper.CollectRelationalMembers(path, members);
// Build string path:
StringBuilder sb = new StringBuilder();
string separator = "";
foreach (MemberInfo member in members)
{
sb.Append(separator);
sb.Append(member.Name);
separator = ".";
}
// Apply Include:
return query.Include(sb.ToString());
}
How could this be adapted to allow a strongly typed form of:
context.Restaurant.Include("MenuCategory.MenuItems");
I have a Tip that allows exactly this: Tip 28 - How to implement an eager load strategy
Uses a nifty trick I think.
Alex
A far less elegant but much quicker to implement method might be as follows:
var list = dataContext.CartLists.Include(typeof(CartListItem).Name).Where(l => l.CustNum == customerNumber && l.ListTypeID == (int)ShoppingCartType.WebShoppingCart).Single();
. . . but you'll need to ensure that you haven't had the EF model generator pluralize your entity set names. This is kind of an ugly solution, but it should give you compile errors once you update your EF model if your table names ever change, as ours just did, and you won't have to actually execute your code to try to find places where your strings don't match actual entities.
Related
I have the following code.
var category = Office.Categories.FirstOrDefault(c => c.Description == name);
Office is a Framework Entity object that was read earlier.
Even though my database uses case-insensitive string comparisons, and one of the items matches exactly except for the case, this code is returning null.
My understanding of what is happening here is that Office.Categories is returning all related rows, and then those rows are being searched by regular LINQ, which is case-sensitive.
If so, that is horribly inefficient as I only want to return the row that matches my condition.
Can anyone confirm my understanding of this? And is it possible to force the filtering to take place in SQL so that I don't need to return the rows I'm not interested in? (And the text comparison will be case-insensitive?)
Thanks for any help.
If you already have the Office object loaded (also depends on lazy loading, but assuming you're outside the context) then you're basically doing a linq-to-object query which is case sensitive. If you want to do linq-to-sql then you should do it differently, something like:
context.Offices.FirstOrDefault(o=>o.OfficeId == someId)
.Categories.FirstOrDefault(c => c.Description == name);
or
context.Categories.FirstOrDefault(c => c.OfficeId == Office.OfficeId
&& c.Description == name);
Correct, so you can't:
Call the Categories property getter if using lazy loading because this causes EF to retrieve the full list of categories from the database
Use eager loading, because this also causes the full list of categories to be loaded from the database
To avoid this, you can use the following code (from this answer here: Using CreateSourceQuery in CTP4 Code First)
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();
}
Add it to your context and call it to create an ObjectQuery.. you can call FirstOrDefault on this and it won't retrieve all entities from the database:
var category = context.CreateNavigationSourceQuery<Category>(Office, "Categories").FirstOrDefault(c => c.Description == name);
Not sure if this is the best approach, however here are my thoughts
I am using Entity Framework Data Model v 4.1 - i'm trying to build a where statement dynamically so that I dont have to query the db everytime, instead i can build a "list of" conditionals, then apply them all at once so the db is only queryed once as opposed to everytime i add my new conditional - if that makes sense...
here is what i have
List<Func<Order, bool>> orderFunctions = new List<Func<Order, bool>>();
if (this.LoggedInUser.UserId == 9)
{
Func<Order, bool> deleg = o => o.Status.Equals(OrderStatus.NewOrder);
orderFunctions.Add(deleg);
}
else if (this.LoggedInUser.UserId == 22)
{
Func<Order, bool> deleg = o => o.AssignedToUserId.Equals(22);
orderFunctions.Add(deleg);
}
List<Orders> orders = Entities.Orders.Where( Some How Apply my Order Functions List Here ).ToList();
I'm not sure if i'm even taking the right approach here - hopefully this makes sense - any guidance, sample code would be fantastic, i've having a heck of a time finding examples / tutorials for this online
You can do this just by calling Where multiple times:
IQueryable<Order> query = Entities.Orders;
if (this.LoggedInUser.UserId == 9)
{
query = query.Where(o => o.Status.Equals(OrderStatus.NewOrder));
}
else if (this.LoggedInUser.UserId == 22)
{
query = query.Where(o => o.AssignedToUserId.Equals(22));
}
List<Order> orders = query.ToList();
Until you start trying to retrieve the results, it won't contact the database.
If you really want to create a list, you'd need to create a List<Expression<Func<Order, bool>>> - they have to be expression trees rather than delegates, so that the Entity Framework can deal with them. Then you could just do:
foreach (var predicate in predicates)
{
query = query.Where(predicate);
}
Or you could use PredicateBuilder to build up a single expression tree.
Jon gave - of course - the answer how to do this better.
But for your original point, and why this won't work:
It is not dificult to apply the hole list (just do
x => orderFunctions.All(f => f(x))
) but you will not get any SQL-love from this - meaning you will get an error because EF cannot know what to do with the all (or whatever you will code there).
You would have to query all entries (with ToList, or ToArray) and after this it would work ... but without performance ;)
I have two linq (to EF4) queries, which return different results. The first query contains the correct results, but is not formatted/projected right.
the second query is what i want but it missing some data.
Schema
alt text http://img220.imageshack.us/img220/9678/schema.png
Query 1
var xxxx = (from cp in _connectedClientRepository
.GetConnectedClients(new[] { "LogEntry", "LogEntry.GameFile" })
.AsExpandable()
.Where(predicate)
select cp)
.ToList();
alt text http://img231.imageshack.us/img231/6541/image2ys.png
Notice the property GameFile . It is not null. This is great :) Notice the linq query? I'm eager loading a LogEntry and then eager loading a GameFile (for each eager loaded LogEntry).
This is what i'm after -> for each LogEntry that is eager loaded, please eager load the GameFile. But this projection result is wrong...
Ok.. next...
Query 2
var yyy = (from cp in _connectedClientRepository
.GetConnectedClients(new[] { "LogEntry", "LogEntry.GameFile" })
.AsExpandable()
.Where(predicate)
select cp.LogEntry)
.ToList();
NOTE: the image above has a typo in it ... please note the include associations typed code is correct (ie. LogEntry.GameFile) while the image has it typo'd.
Correct projection now -> all LogEntries results. But notice how the GameFile property is now null? I'm not sure why :( I thought i correctly eager loaded the correct chain. So this is the correct projection but with incorrect results.
Obligatory Repository code.
public IQueryable<ConnectedClient> GetConnectedClients(
string[] includeAssociations)
{
return Context.ConnectedClients
.IncludeAssociations(includeAssociations)
.AsQueryable();
}
public static class Extensions
{
public static IQueryable<T> IncludeAssociation<T>(
this IQueryable<T> source, string includeAssociation)
{
if (!string.IsNullOrEmpty(includeAssociation))
{
var objectQuery = source as ObjectQuery<T>;
if (objectQuery != null)
{
return objectQuery.Include(includeAssociation);
}
}
return source;
}
public static IQueryable<T> IncludeAssociations<T>(
this IQueryable<T> source, params string[] includeAssociations)
{
if (includeAssociations != null)
{
foreach (string association in includeAssociations)
{
source = source.IncludeAssociation(association);
}
}
return source;
}
}
Updates
1 : Fixed some typo's in noticed in the code samples.
2 : Added repository code to help anyone who is confused.
I suspect Craig Stuntz' suggestion may work, but if it doesn't, the following should certainly work:
var xxxx =_connectedClientRepository
.GetConnectedClients(new[] { "LogEntry", "LogEntry.GameFile" })
.AsExpandable()
.Where(predicate)
.ToList() // execute query
.Select(cp => cp.LogEntry); // use linq-to-objects to project the result
Include() works on the query results, rather than the intermediate queries. You can read more about Include() in this post. So one solution is to apply the Include() to the whole query, like this:
var q = ((from cp in _connectedClientRepository.GetConnectedClients()
.AsExpandable()
.Where(predicate)
select cp.LogEntry)
as ObjectQuery).Include("GameFile").ToList();
That will probably work, but it's ugly. Can we do better?
I can think of two ways around this issue. Mostly, it depends upon whether or not you actually need entity types returned. I can't say whether this is the case without seeing the rest of your code. Generally, you need to return entity types when you are going to update (or otherwise modify) them. If you are selecting for display or calculation purposes, it's often a better strategy to return POCOs instead of entity types. You can do this with projection, and of course it works in EF 1. In this case, you would change your repository method to return POCO types:
public IQueryable<ClientInfo> GetConnectedClients()
{
return from cp in _context.Clients
where // ...
select new ClientInfo
{
Id = cp.Id,
ClientName = cp.ClientName,
LogEntry = new LogEntryInfo
{
LogEntryId = cp.LogEntry.LogEntryId,
GameFile = new GameFileInfo
{
GameFileId = cp.LogEntry.GameFile.GameFileId,
// etc.
},
// etc.
},
// etc.
};
}
Note that when you use projection there is no eager loading, no lazy loading, and no explicit loading. There is only your intention, expressed as a query. The LINQ provider will figure out what you need, even if you further compose the query outside the repository!
On the other hand, you might need to return entity types instead of POCOs, because you intend to update them. In that case, I would write a separate repository method for the LogEntries, as Tomas suggested. But I would only do this if I intended to update them, and I might write it as an update method, rather than a "Get" method.
It seems you need one more repository method, that will do this for you; _connectedClientsRepository.GetLogEntriesOfConnectedClients().
I have an entity framework 3.5 project where I'm using TPH inheritence. Two of my concrete types are in their own DAO class and contain a selector that projects the Entity Class into a DTO. However, both of these concrete classes have similar relations with another table which I use a let statement to identity clearly. My question is, can I somehow refactor this let statement because as I create more inherited concrete classes I feel I'm violating DRY.
StudentDAO.cs:
var myDTO = from x in context.Students
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new StudentDTO { id = x.id, studentname = x.studentname, address = a.address};
ProfessorDAO.cs:
var myDTO = from x in context.Professors
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address};
so is there a way I can refactor the address out of both of these queries?
Short answer: I'm afraid there isn't much you could easily do. The problem is that you need to parameterize the query. The query however needs to be represented as an expression tree (so that it can be translated to SQL or whatever EF uses). Unfortunately, C# don't provide any elegant ways for composing expression trees.
Long answer: Some time ago, I wrote a C# article that explains how to compose queries (using some tricks) - you can take expression tree and splice it into a parameterized query. So, in principle you could use the techniques from the article to create a method that takes the changing parts of the expression tree and then compose the query.
I'll use the types used in LINQ to SQL, because I'm more familiar with it, but I believe the principle should be the same:
IQueryable<R> GetInfo<T>(IQueryable<T> source, // e.g. context.Students
Expression<Func<T, IQueryable<Address>>> getAddr, // x => x.adresses
Expression<Func<T, Address, R>> getRes // (x, a) => new StudentDTO { ... }
return
from x in source.AsExpandable()
let address = (from a in getAddr(x).Expand() where a.active == true
select a).FirstOrDefault()
select getRes(x, a).Expand();
}
// The use would look like this
var res = GetInfo(context.Professors, x => x.addresses, (x, a) => new
ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address });
So a summary is that C# and LINQ do not provide any built-in support that would make it easier. However, with some effort you can write query that is parameterized by some parts of the expression tree. In your case, it makes the code a little shorter and less repetitive, but it makes it a lot more complex (and you also need to use library like the one I referenced that provides AsExpandable and Expand).
In this case I'm afraid the it's not worth the effort. But it would be nice if C# v(Next)^x provided some more elegant support for things like this :-)
I could have sworn that there was an extension method already built for the Queryable class that I just can't find, but maybe I'm thinking of something different.
I'm looking for something along the lines of:
IQueryable<Entity> en = from e in IDB.Entities select e;
en.ForEach(foo => foo.Status = "Complete");
en.Foreach() would essential perform:
foreach(Entity foo in en){
foo.Status = "Complete";
}
Is this already written? If not, is it possible to write said Extension Method, preferably allowing for any LINQ Table and any Field on that table. Where is a good place to start?
There's nothing in the base class library. Many, many developers have this in their own common library, however, and we have it in MoreLINQ too.
It sort of goes against the spirit of LINQ, in that it's all about side-effects - but it's really useful, so I think pragmatism trumps dogma here.
One thing to note - there's really no point in using a query expression in your example. (It's not entirely redundant, but if you're not exposing the value it doesn't matter.) The select isn't doing anything useful here. You can just do:
IDB.Entities.ForEach(foo => foo.status = "Complete");
Even if you want to do a single "where" or "select" I'd normally use dot notation:
IDB.Entities.Where(foo => foo.Name == "fred")
.ForEach(foo => foo.status = "Complete");
Only use query expressions where they actually make the code simpler :)
public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
{
foreach (var item in sequence)
{
action(item);
}
}
There is a foreach on a List<>. Roughly something along these lines:
IQueryable<Entity> en = from e in IDB.Entities select e;
en.ToList().ForEach(foo => foo.status = "Complete");