Creating a property that LINQ to Entities can translate - c#

I am curious on how to create a property that can be translated by LINQ. Below is a very simple example.
I have a table/class Category, that has a column ParentId linking to itself (So a category can have sub-categories)
EF automatically generates a property Category1, which is the parent category.
For the sake of clarity, I created another property
public partial class Category
{
public Category Parent
{
get { return Category1; }
}
}
The problem is, this works
var categs = ctx.Categories.Where(x => x.Category1 == null);
but this doesn't work
var categs = ctx.Categories.Where(x => x.Parent == null);
The specified type member 'Parent' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Is there any way to create a translatable property (LINQ to SQL) without doing .ToList()?
EDIT: I want to avoid touching Model.edmx because the database often changes during development and the .edmx often needs to be recreated

If you ask if it's possible to create a property with any C# code in the getters/setters and later have it understood by standard LINQ to Entities - then no, it can't be done. C# is much more expressive then SQL and it's unreasonable to expect Entity Framework to act as a general C# to SQL translator.
You can work around this is many cases though, see Using a partial class property inside LINQ statement for an example.
Update
It'd help if you told us what exactly you want to achieve, but here's an example:
public partial class Category
{
public static Expression<Func<Category, bool>> ParentIsNullExpression
{
get
{
return c => c.Category1 == null;
}
}
}
And then
var categs = ctx.Categories.Where(Category.ParentIsNullExpression);
All sorts of manipulations on Expressions are possible, some of them are supported by EF and as such translate to SQL.

in your Entity Data Model Designer (.edmx file), you can rename the Category1 to Parent

You have 4 options:
Use code first
Add it to the EDMX designer
Add your property to the CSDL section of the EDMX, add a column to the SSDL section of the EDMX, then map them to each other in the mapping section of the EDMX.
Bring the query into memory using .ToList() then use internal LINQ instead of LINQ to Entities.

Related

Project into a class with Private properties using Entity Framework

I'm using Entity Framework with a code-first approach to store data for a C# application I'm working with a SQL Server database. A challenge I'm currently running into involves a structure (approximately) like this:
public class MainEntity
{
// Data
public List<SubEntity> SubEntities { get; private set; }
}
public class SubEntity
{
// More Data
bool DoNotLoad { get; set; }
}
Now, I know that Entity Framework is able to "see" private property setters and populate the entities using reflection. That's why this works:
IEnumerable<MainEntity> Entities = MainEntities.Include(m => m.SubEntities).ToList();
And it will retrieve the MainEntity and all of its SubEntities from the database even though the setter for SubEntities is private.
I also know that Entity Framework supports more free-form projections, like so:
var projectedEntities = MainEntities.Select(m =>
new {
Main = m,
Sub = m.SubEntities.Where(s => !s.DoNotLoad)
}
);
And then I'll have an anonymous type with the main entity and its sub entities, with a filter applied to the sub entities.
However, I would like to combine the two methods and end up with MainEntity objects that have their SubEntity property populated, but filtered.
Unfortunately, this doesn't work:
var invalidEntities = MainEntities.Select(m =>
new MainEntity{
SubEntities = m.SubEntities.Where(s => !s.DoNotLoad)
}
);
C# doesn't let me use property initialization that way because SubEntities has a private setter, even though Entity Framework would work around that. Is there a way to make this work how I want it? My first priority is to avoid making two queries (e.g. get MainEntity, get filtered SubEntities, use specialized code to insert it), but I would also like to actually do the filtering in the database rather than getting everything and then filtering locally (e.g. MainEntity.FilterSubEntities()). Making the setter public isn't entirely impossible, but in order to use Property initialization I think I would need to change EVERY setter to public, which I would rather avoid.
I've been told that this is possible by projecting into an Anonymous type and if I name things in a certain way Entity Framework will "recognize" that it should project into MainEntity instead, but I haven't been able to find any references to this anywhere else. If that is possible then that would be my preferred method since it seems flexible enough to apply in various other situations where I need to filter in other ways.
I've found it
I've finally found an example of what I was told, and my testing indicates that it works. A few notes about the method:
It does not work to arbitrarily project into private properties or properties with private setters. It only allows for more complex filtering of sub-entities, which fortunately was the primary use I was looking for.
This may be undocumented behavior, or abuse of some other feature, so for all I know it could break at any time, and I can't necessarily state what would happen in any edge-cases that arise.
I don't have any evidence that this is better by any metric than simply filtering locally, and it could be significantly worse. I haven't measured it, and I recognize that this seems to be "odd behavior" that might mess with Entity Framework's normal optimizations. I think it's pretty cool though.
With that out of the way, this is the technique using the sample classes described in the question:
var queryResult = MainEntities.Select(m =>
new {
MainEntity = m,
m.SubEntities.Where(s => !s.DoNotLoad)
})
.ToList();
var finalList = queryResult.Select(q => q.MainEntity).ToList();
(Doing this on two lines and using a temporary variable isn't strictly necessary, but I think it clarifies that a DB query is executed at the first ToList() and then additional operations are applied locally.)
I believe that this works because Entity Framework populates navigation properties in a particularly eager manner. Essentially it populates the SubEntities List purely by adding every loaded SubEntity that has a foreign key to that MainEntity, regardless of what caused those entities to be loaded. That is speculation though, all I definitely know is that it currently works how I need it to.

How to Order By Dynamic value via Entity Framework?

I'm building an asp.net mvc app using Entity Framework, and I'm trying to order by a list. By a dynamic changed name, according to the name exists in the database.
bids = bids.OrderBy(s => s.PublisherName);
and the object:
public string PublisherName { get { db.Publishers.Find(pubid).Name; } }
But I'm getting an exception:
The specified type member 'PublisherName' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
What can I do? And how I can make it work?
Thanks.
Only initializers, entity members, and entity navigation properties
are supported.
db.Publishers.Find(pubid).Name; is neither initializer nor entity member, nor navigation property.
One possible way is to bring it on memory with AsEnumerable() :
bids = bids.AsEnumerable().OrderBy(s => s.PublisherName);
This will work perfectly as long as bids is small list of objects.
I believe Bidand Publisher are related, right? Maybe this will help you
var bids = from t in context.Bids
let u = t.Publishers.FirstOrDefault(i => i.Id == pubid)
orderby u.Name
select t;
Untested code, not sure if it will work for you!
When using Entity Framework via LINQ, all of the properties in the linq statement are translated in to SQL. EF only natively understands simple properties. It doesn't understand how to translate properties that contain actual code logic, which is what is causing the error you are seeing. The simplest way to fix this is to do the sort client side, outside of entity framework. The usual way to do this is to call .ToList on the unsorted result, and then sort the resulting list, which will happen client side.

How can we do object filtering in Entity Framework?

When defining an object context, using code first in entity framework, for example:
public class DomainContext : DbContext
{
public DomainContext() { }
public virtual DbSet<News> News { get; set; }
}
We all know that you can query "News" doing something like (for example, to get all news that were published today):
var ctx = new DomainContext();
ctx.News.Where(x => x.PublishedDate == DateTime.Now.Date)
But, and this is the question: Is there a way to apply a pre-defined filtering/condition to all queries that pass through ctx.News? Say that I wanted that all queries on ctx.News to have the "Published Today" filtering implicit applied?
There is no way to add automatic condition (filter) to querying news. All posted examples work but only if you query News directly. If you for example loads navigation property pointing to News examples will fail.
EDMX solve this by conditional mapping but this leads to other very bad disadvantages. Conditional mapping is fixed (cannot be changed without rebuilding model) and you can have only single condition for each type - it is like TPH degraded to single entity type. Moreover conditions defined in conditional mapping probably cannot work with "Today". Conditional mapping is not available in code-first approach.
I don't believe there's a way to add a filter to the DbSet<News> object as you're suggesting. But what you should be able to do is just write another function:
public virtual IEnumerable<News> TodaysNews
{
get { return News.WHere(n => n.PublishDate == DateTime.Today); }
}
And then, I think, if you did a query on top of that somewhere else, like:
var todaysGoodNews = from n in ctx.TodaysNews
where n.IsGood == true
select n;
then it would combine the queries when it sent it to the server rather than making it two separate queries. I'm not positive if that works when you use IEnumerable<> or if you need to return something else (IQueryable<>, perhaps?).
Edit:
I just saw your response to the other poster below. I guess I took too long to type/format. I don't know of any way to apply a filter like that, but aren't our solutions effectively doing that? You could even make TodaysNews be the only way to directly access that object through the context or something.
You could add a new property to your context:
public IEnumerable<News> TodaysNews
{
get
{
return this.News.Where(x => x.PublishedDate == DateTime.Now.Date);
}
}
Any further filtering/sorting/etc can then be applied to the property.
Update:
If you're not able to just use a pre-filtered query, another option is to create a view in your database and map your entity to that view. The view could be based on the current date.
I'm facing the same problem and I found this: EntityFramework.Filters. This post shows how to use it.

Self join not including children (entity framework 4.0)

I am creating a sort of family tree in entity framework 4.0. I have come across an issue where the Entity Framework is only loading the immediate children. It does not load the children of the children even though i have an include specified.
For example, this is my query :-
public IQueryable<TreeMember> GetTreeMembers(int userId)
{
return this.ObjectContext.TreeMembers.Include("RelatedTreeMembers").Where(u => u.UserId == userId && u.RelatedTreeMemberId == null);
}
This would load the 1st level of children. But it does not load the children of the children. If i have to include children of the children, i have to write :-
public IQueryable<TreeMember> GetTreeMembers(int userId)
{
return this.ObjectContext.TreeMembers.Include("RelatedTreeMembers.RelatedTreeMembers").Where(u => u.UserId == userId && u.RelatedTreeMemberId == null);
}
This is quickly getting to be frustrating because i don't know how many times should i have to write this RelatedTreeMembers as a family tree can extend upto N level. How do i get past this issue? If my question is not clear please let me know.
Thanks in advance :)
That is how EF works. You want to define recursive (hierarchical) query which is not possible with eager loading in EF. You always have specify exactly which navigation properties you want to load - obviously in this scenario you can't because you don't know how deep is your recursion.
I like the idea #Magnus suggested with CTE but I would not use DB View. I would use stored procedure. The reason is that you already have entity TreeMember mapped to a table. If you define the view you will not be able to map it to the same entity type. You will need new entity for the view. If you use stored procedure you can map its result to already existing entity type.
Another way is to use lazy loading.
Write a view with a recursive CTE and than use that with Linq.
http://msdn.microsoft.com/en-us/library/ms186243.aspx

How to model POCO object from a multilanguage database with Entity Framework 4?

I am building a new project from scratch.
I created a db where I have consistently applied a db structure that I explain with a short self-explanatory example:
Table Item -> (Id, Name) -> Contains general information
Table ItemInfo -> (Item_Id, Language, Description) -> Contains the language dependent information.
Id and Item_Id are connected with a foreign key relationship.
My idea was to model it in a way that I would end up using only a single POCO object "Item" populated through Entity Framework. This object would contain only the public properties: Id, Name and Description.
The language will be hidden to the code using this object, the object itself should have the responsibility to give the correct description depending on a global variable that contains the language.
I have tried a few ways to do this and always ended up having problems because Entity Framework wouldn't allow this scenario. I always had to retrieve info for ALL languages and not only the current one or use 2 different queries.
So at the end the solution I started to use was to let a T4 template create both Item and ItemInfo and then I manually added a code similar to this:
public partial class Item
{
private ItemInfo _itemInfo = null;
private ItemInfo itemInfo
{
get
{
if (_itemInfo == null) _itemInfo = ItemInfoes.Single(p => p.Language == GlobalContext.Language);
return _itemInfo;
}
}
public Description
{
get { return itemInfo.Description; }
set { itemInfo.Description = value;}
}
}
With this code I added the additional properties from ItemInfo to Item and selected the correct language as per my requirements.
Do you think this is a good solution? How would you solve this problem instead?
However, running sql profiler I can see that 2 different sql queries are used to populate the Item object, one that queries the Item table and another that queries the ItemInfo.
Can the same scenario be achieved with a single query that does a join between the 2 tables? (I am afraid of the long term performance hit and also this is how I would do it without an ORM).
Any suggestion will be welcome, I have many years of programming experience but I am a newbie with Entity Framework and ORMs in general.
Please help.
You're not showing how you fetch the Item objects, but generally I don't see a problem with fetching everything in one query. You've got several options.
You can do a projection (but not onto a mapped entity - in this example I project onto an anonymous object):
context.
Items.
Select(item => new
{
Id = item.Id,
Name = item.Name,
Description = item.
ItemInfo.
Where(info => info.Language == YourGlobalLang).
Select(info => info.Description).
FirstOrDefault()
};
(This has been edited to use FirstOrDefault instead of Single - see comment discussion with #Craig Stuntz)
This will return a list of all Items - you can add a Where clause to filter.
Or you can fetch it the other way around (starting with ItemInfo):
ItemInfo itemInfo = context.
ItemInfoes.
Include(info => info.Item).
SingleOrDefault(info => info.Language == YourGlobalLang &&
info.Item.Id == itemIdToFetch);
After that you can access the item object itself:
Item item = itemInfo.Item;
I would say it's a reasonable approach. Also, I wouldn't worry about performance issues with two simple selects. If it turns out to be a problem in the future, you might change it to a view, for instance.
You may try to add the where clause dynamically.
Or as it was said use linq to sql directly.
How to append a where clause to an Entity Framework ObjectSet
Add the where clause dynamically in Entity Framework

Categories