Why: LINQ to Entities does not recognize the method? - c#

I have two pieces of code which seems have same functionality but one of them lead to exception but another one is works like a charm. I want to know can you imagine why this happens?
I have below line in my web application which leads to this exception:
LINQ to Entities does not recognize the method 'System.Object
FilterDeliveryAddressFields(WebApplication1.Models.DeliveryAddress)'
dynamic deliveryAddresses = (from address in db.DeliveryAddress
select filterDeliveryAddressFields(address)).ToList();
And here is filterDeliveryAddressFields:
private dynamic filterDeliveryAddressFields(DeliveryAddress address)
{
return new { address.address, address.deliverTo, address.deliverToPhoneNumber, address.id };
}
And here is Linq-2-Sql generated Model for DeliveryAddress which have foreign key relationship with Subscriber:
public partial class DeliveryAddress
{
public int id { get; set; }
public int fkSubscriberId { get; set; }
public string address { get; set; }
public string deliverTo { get; set; }
public string deliverToPhoneNumber { get; set; }
public virtual Subscriber Subscriber { get; set; }
}
But when I change db.DeliveryAddress items to list first and then run code again as below everything goes well and no exception occurs again. I want to know what wrong with first code snippet which does not happen in below snippet?
List<DeliveryAddress> addresseList = db.DeliveryAddress.ToList(); //magic trick?!
dynamic deliveryAddresses =
(from address in addresseList
select filterDeliveryAddressFields(address)).ToList();

Actually, your method couldn't be translated to T-SQL, Linq to Entities couldn't recognize every method, the magic behind the .ToList() method which you are looking for is, after data is loaded, any further operation (such as select) is performed using LINQ to Objects, on the data already in memory.
However the performance is not guaranteed in this approach, as you have to load
your data into memory, so imagine you have a lot of data in your db, what will be happened next?

Agree with #Salah . In LINQ to Entities, it first try to convert your query to command tree and execute against your ORM. Please read here you can find more details.
In your first approach Linq tries to convert your filterDeliveryAddressFields(address)) method to command tree. That is why it complains that LINQ to Entities does not recognize the method.
In your second approach you execute against list or IEnumerable<T>, which means you use LINQ to Object. You can read more about it here.
For your first solution you can try another implementation. Simply try to use aggregate method to filter your result. Then you don't need filterDeliveryAddressFields(address)) method. You can find example here
Something like this, (Sorry I didn't try this my self. This is only for you to get an idea.)
from address in db.DeliveryAddress
select new { address.address, address.deliverTo, address.deliverToPhoneNumber, address.id };

Related

EF FromSql() with related entities

Disclaimer: these requirements are not set by me, unless this is an impossible task I cannot convince my boss otherwise.
Let's say we have two entities: Item and ItemTranslation.
public class Item
{
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<Item> Children { get; set; }
public virtual Item Parent { get; set; }
public virtual ICollection<ItemTranslation> Translations { get; set; }
}
public class ItemTranslation
{
public int Id { get; set; }
public string CultureId { get; set; }
public string Description { get; set; }
public virtual Item Item { get; set; }
}
The requirement is that Item.Description should be filled in based on a language selected by default, but also allowing it to be specified based on what the user wants. The Item.Description column doesn't actually exist in the database.
In SQL this would be easy: all you have to do is query both tables like so
SELECT [Item].[Id], [ItemTranslation].[Description], [Item].[ParentId]
FROM [Item]
LEFT JOIN [ItemTranslation] ON [Item].[Id] = [ItemTranslation].[ItemId]
WHERE [CultureId] = {cultureId}
Or use an OUTER APPLY depending on your implementation. I have added this query to the .FromSql() function built in Entity Framework.
Put this all together in an OData API and this all works fine for one Item. However as soon as you start using $expand (which behind the scenes is a sort of .Include()) it no longer works. The query being sent to the database for the related entities no longer holds the SQL which I specified in .FromSql(). Only the first query does. On top of this when you would query an Item from a different controller e.g. ItemTranslation this would also no longer work since .FromSql() is only applied in the other controller.
I could write a query interceptor which simply replaces the generated SQL by Entity Framework and replaces FROM [Item] with FROM [Item] LEFT JOIN [ItemTranslation] ON [Item].[Id] = [ItemTranslation].[ItemId] WHERE [CultureId] = {cultureId} but I wonder if there is a better implementation than that. Perhaps even a redesign in models. I'm open to suggestions.
FromSql has some limitations. I suspect this is the reason why Include won't work.
But once you use EF, why are you messing with SQL? What difficulties does that query have which prevents you from doing it in LINQ? Left join maybe?
from item in ctx.Items
from itemTranslation in ctx.ItemTranslations.Where(it => it.Item.Id == item.Id).DefaultIfEmpty()
where itemTranslation.CultureId == cultureId
select new { item.Id, itemTranslation.Description, ParentId = item.Parent.Id };
Update
Going over the issue again, I see a further problem. Include will only work on an IQueryable<T> where T is an entity whose navigation properties are mapped properly. Now, from this perspective, it doesn't matter if you use FromSql or LINQ if it produces an IQueryable of some projection instead of an entity, Include won't work for obvious reasons.
To be able to include ItemTranslation entities, your action method should look something like this:
[Queryable]
public IQueryable<Item> GetItems()
{
return db.Items;
}
So the framework can perform $expand on the IQueryable<Item> you return. However, this will include all item translations, not just the ones with the desired culture. If I get it correctly, this is your core issue.
It's obvious as well that you cannot apply this culture filter to an IQueryable<Item>. But you shouldn't do that as this is achieved by $filter in OData:
GET https://.../Items/$expand=Translations&$filter=Translations/CultureId eq culture

Linq to entities DbQuery object is failing hard

I wonder if anyone can shed some light on what may be happening here. I'm using C#, MVC, with entity framework.
So I run these two lines of code:
var booboo = _context.AppItems.Where(ai => ai.id == 101);
var sql = booboo.ToString();
And I get some strange behavior. The booboo.ToString() method hangs, thus failing. Nothing about the booboo DbQuery object works properly in fact.
I'm having similar problems all over the code with my AppItem entity (AppItems is DbSet as you might guess). Entity Framework appears to be unable to construct a query for the AppItem entity.
Edit:
I wasn't patient enough! After leaving it for a very long time, I do get the following exception:
"Message=Internal error: An expression services limit has been reached. Please look for potentially complex expressions in your query, and try to simplify them."
Interestingly that's a Sql.Client exception, which I wasn't expecting.
Here's what the AppItem class looks like:
public class AppItem : Domain.Item
{
public int? UserProfileId { get; set; }
public virtual UserProfile UpdatedByUser { get; set; }
[MaxLength(50)]
public String Type { get; set;}
public DateTime UpdatedDate { get; set;}
// flags
public virtual ICollection<ItemFlag> Flags { get; set; }
// actions
public virtual ICollection<ItemAction> Actions { get; set; }
// notes
public virtual ICollection<Note> Notes { get; set; }
}
Domain Item contains a primary key field (id) and a few other fields.
The Note / ItemAction / ItemFlag Classes there all inherit from AppItem, so perhaps some sort of circular referencing is to blame?
Other items can be queried just fine. For example, I have numerous classes that inherit from AppItem (like ItemFlag, ItemAction and Note) and I can query all of these just fine.
So, where Members is DbSet and Member inherits from AppItem:
var foofoo = _context.Members.Where(ai => ai.id = 101);
var sql = foofoo.ToString();
This Works fine; foofoo.ToString() returns the constructed SQL and everything appears to be in order.
It seems really bizarre to me, there's no error message or anything, the application just hangs when it tries to query AppItems. The table exists in the database, but that doesn't matter because we aren't getting as far as querying the database, we are failing to construct a query in the first place.
Thanks in advance for any help.
I found what the problem was.
I'm using Table-per-Type for inheritance. With AppItem being a base type, the SQL query it generates for querying it is huge (several thousand lines long in this case) and causes problems.
So basically, you need to avoid querying on base types that have more than a few types inheriting from them when using Table-per-Type.
I am assuming that your query is meant to return 1 item.
Add .FirstOrDefault(); onto the end of your query to only return one item (your current query returns an IQueriable of type AppItems)
The Entity Framework does not execute the query until needed, so in your code it will execute the query when the .ToString() method is called.
Try this:
var booboo = _context.AppItems.Where(ai => ai.id == 101).FirstOrDefault();
if (booboo != null)
{
var sql = booboo.ToString();
//etc
}

Linq: return List contained List

I have a class:
public class BigClass
{
public int Id { get; set; }
public string Name { get; set; }
...
public List<LittleClass> LittleClassList { get; set; }
}
Is it possible to return List of its instances in one request to DataBase? This approach invokes many requests to DataBase:
List<BigClass> data = context.Table1_Name ...
foreach(var item in data)
{
item.LittleClassList = context.Table2_Name ...
}
Lambda syntax is preferred. Thank you.
from big in context.TableA
from little in big.LittleClassList
select little;
And in extension format
Context.TableA.Where(...).SelectMany(x=>x.LittleClassList);
List<LittleClass> littleClassesOfListBigClasses = new List<LittleClass>();
context.BigClass.ToList().ForEach(e=>littleClassesOfListBigClasses.AddRange(e.LittleClassList));
Specialy for Dave A.
What I think OP wants:
He want to take all LittleClasses stored in db by one lambda-expression.
Explanation about how does my code works:
1) I use EF(entity framework). If you dont know what it is you can read information
here. In the question I see that the OP knows what is EF and how to use it, so I didnt find a need to explain.
2) I take all data from table BigClass from the DB and bring it as List(in 1 query)
3) I used ForEach(from Linq) to go through the returned BigClass list and add the all LittleClasses to the littleClassesOfListBigClasses
Notice also you can add Where before to ToList to get not all data stored in BigClass table but only a part, it will look something like this:
context.BigClass.Where(some lambda-expression).ToList()
So my answer is clear now?

IQueryable returns null on invoking Count c#

I have a problem trying to get the count out of the following query:
var usersView = PopulateUsersView(); //usersView is an IQueryable object
var foo = usersView.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));
Where UsersView is a class which is populated from an EF entity called users (refer to the first line in the code above)
This is the class definition for the UsersView class:
public class UsersView
{
public int UserId { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string PostCode { get; set; }
public string CountryName { get; set; }
public string WorkPlaceName { get; set; }
public string Gender { get; set; }
public string EMail { get; set; }
public string Company { get; set; }
public string RoleName { get; set; }
public string ConferenceRole { get; set; }
}
As I said trying to execute the line foo.Count() returns Null Exception and this might be because the ConferenceRole column allows Null in the database.
Now what I can't understand is that when I invoke the same query directly on the ObjectQuery the Count of records (i.e. invoking foo2.Count()) is returned without any exceptions.
var foo2 = entities.users.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));
Is it possible to the same query above but using the IQueryable usersView object instead?
(It is crucial for me to use the usersView object rather than directly querying the entities.users entity)
EDIT
Below is the code from the PopulateUsersView method
private IQueryable<UsersView> PopulateUsersView()
{
using (EBCPRegEntities entities = new EBCPRegEntities())
{
var users = entities.users.ToList();
List<UsersView> userViews = new List<UsersView>();
foreach (user u in users)
{
userViews.Add(new UsersView()
{
UserId = u.UserId,
Title = u.Title,
Name = u.Name,
Surname = u.Surname,
Street1 = u.Street1,
Street2 = u.Street2,
City = u.City,
PostCode = u.Post_Code,
CountryName = u.country.Name,
WorkPlaceName = u.workplace.Name,
Gender = u.Gender,
EMail = u.E_Mail,
Company = u.Company,
RoleName = u.roles.FirstOrDefault().Name,
ConferenceRole = u.ConferenceRole
});
}
return userViews.AsQueryable();
}
}
Thanks
UPDATE...
Thanks guys I finally found a good answer to the difference between the IQueryable and the ObjectQuery objects.
As a solution I am checking if the ConferenceRole is null and then checking with the contains method as many of you guys have said.
My guess is that your PopulateUsersView() method is actually executing a query and returning an IQueryable Linq-to-Objects object - while the foo2 line executes the query only in the SQL layer. If this is the case, the obviously PopulateUsersView() is going to be quite an inefficient way to perform the Count
To debug this:
can you post some code from PopulateUsersView()?
can you try running both sets of code through the EF tracing provider to see what is executed in SQL? (see http://code.msdn.microsoft.com/EFProviderWrappers)
Update
#Ryan - thanks for posting the code to PopulateUsersView
Looks like my guess was right - you are doing a query which gets the whole table back into a List - and its this list that you then query further using Linq2Objects.
#ntziolis has provided one solution to your problem - by testing for null before doing the ToLower(). However, if your only requirement is to Count the non-empty items list, then I recommend you look at changing the PopulateUsersView method or changing your overall design. If all you need is a Count then it would be much more efficient to ensure that the database does this work and not the C# code. This is espeically the case if the table has lots of rows - e.g. you definitely don't want to be pulling 1000s of rows back into memory from the database.
Update 2
Please do consider optimising this and not just doing a simple != null fix.
Looking at your code, there are several lines which will cause multiple sql calls:
CountryName = u.country.Name
WorkPlaceName = u.workplace.Name
RoleName = u.roles.FirstOrDefault().Name
Since these are called in a foreach loop, then to calculate a count of ~500 users, then you will probably make somewhere around 1501 SQL calls (although some roles and countries will hopefully be cached), returning perhaps a megabyte of data in total? All this just to calculate a single integer Count?
Try to check whether ConferenceRole is null before calling a method on it:
var foo = usersView.Where(fields => fields.ConferenceRole != null
&& fields.ConferenceRole.ToLower().Contains("role"));
This will enable you to call the count method on the user view.
So why does it work against the ObjectQuery?
When executing the query against the ObjectQuery, LinqToSql is converting your query into proper sql which does not have problems with null values, something like this (it's sample markup sql only the actual query looks much different, also '=' is used rather than checking for contains):
SELECT COUNT(*) from USERS U WHERE TOLOWER(U.CONFERENCEROLE) = 'role'
The difference to the :NET code is: It will not call a method on an object but merely call a method and pass in the value, therefore no NullReference can occur in this case.
In order to confirm this you can try to force the .NET runtime to execute the SQL prior to calling the where method, by simply adding a ToList() before the .Where()
var foo2 = entities.users.ToList()
.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));
This should result in the exact same error you have seen with the UserView.
And yes this will return the entire user table first, so don't use it in live code ;)
UPDATE
I had to update the answer since I c&p the wrong query in the beginning, the above points still stand though.

Custom object with nested collection

I've created an object that contains another collection in one of it properties.
This is the main object:
public class MeterPrevReadInfo
{
public int JobMeterID { get; set; }
public string PreviousJobReference { get; set; }
public FuelType MeterFuelType { get; set; }
public List<MeterPrevReadRegInfo> Regs { get; set; }
public DateTime DateMeterRead { get; set; }
}
This is the child object:
public class MeterPrevReadRegInfo
{
public string RegisterID { get; set; }
public string MeterRead { get; set; }
}
I need to bind this object to a repeater control, I would like to show the DateMeterRead property and all the MeterRead properties in the repeater.
Is this possible using Linq? I could easily do it using a t-sql query from the database, but I just figured it should be possible to do this in memory without the overhead of another trip to the database.
Don't get confused - LINQ isn't a data access layer or ORM (perhaps you're thinking of LINQ-to-SQL, or LINQ-to-Entities?)
You can absolutely query an in-memory collection using LINQ, although your questions seems to relate to database.
I could easily do it using a t-sql
query from the database, but I just
figured it should be possible to do
this in memory without the overhead of
another trip to the database.
You can retrieve all this data from the database in one query & then construct objects. You can do this with a stored procedure, LINQ-to-SQL, Entity Framework, or other tools. You should choose the best tool for your requirements. I expect this is a very small part of the requirement, so take a step back, choose the best tool, and make this work using that tool.
sure this is possible. It looks like you want something like this:
List<MeterPrevReadInfo> list = ...;
var result = from item in list
from info in item.Regs
select new {item.DateMeterRead, info.MeterRead};
This query defines a list of anonymous objects with the two properties you want.
You can access an anonymous object representing the model you want by using the Linq Select extension method as follows:
var readInfo = new MeterPrevReadInfo();
readInfo.Regs.Select(x => new {
x.RegisterID,
x.MeterRead,
readInfo.DateMeterRead
});

Categories

Resources