LINQ to Entities: Using DateTime.AddMonth in Lambda Expression - c#

I've been reading some posts but I don't find a solution to a problem that I have with LINQ To Entities, Lambda Expressions and DateTime.AddMonth.
The problem is that I'm trying to use DateTime.AddMonth inside a Lambda Expression and I'm getting this error:
"LINQ to Entities does not recognize the method 'System.DateTime
AddMonths(Int32)' method, and this method cannot be translated into a
store expression"
when I execute this piece of code:
List<Orders> orders = context.Orders
.Where(o => o.IdOrderStatus == 1)
.Where(o => o.PaymentDate.Value.AddMonths(o.Products.ProductCategories.CommissionableMonths) > DateTime.Now)
.ToList();
Is there a way to avoid this exception but mantaining the same behavior?
I don't know a lot about Linq, Lambdas or Entity Framework.
Thank you very much in advance!
Gsus.

You could return the necessary information without filtering on the date, then filter on the date afterward. (Of course, I'm not sure what size your data will be, so this may be inefficient. If so, you'll need a SQL-based solution of some sort -- maybe a stored procedure.)
List<Orders> orders = context.Orders
.Where(o => o.IdOrderStatus == 1)
.ToList();
orders = orders.Where(o.PaymentDate.Value.AddMonths(o.Products.ProductCategories.CommissionableMonths) > DateTime.Now);
This turns the AddMonths portion of the query in to a Linq-to-Objects method instead of Linq-to-Entities call.

try this,
var today =DateTime.Now;
List<Orders> orders = context.Orders
.Where(o => o.IdOrderStatus == 1)
.Where(o => SqlFunctions.DateAdd("month" ,o.Products.ProductCategories.CommissionableMonths,o.PaymentDate) > today)
.ToList();

LINQ to Entities converts your linq expression into SQL code, sometimes this conversion is seamless such as integer addition, selects, groups, etc, but sometimes the abstraction leaks, as in date operations.

Related

Use skip and take inside a LINQ include

I have an object that has a property which is a collection of another object. I would like to load just a subset of the collection property using LINQ.
Here's how I'm trying to do it:
manager = db.Managers
.Include(m => m.Transactions.Skip((page - 1) * 10).Take(10))
.Where(m => m.Id == id)
.FirstOrDefault();
The code above throws an error that says
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.\r\nParameter name: path
What is the right way to do this in LINQ? Thanks in advance.
You cannot do this with Include. EF simply doesn't know how to translate that to SQL. But you can do something similar with sub-query.
manager = db.Managers
.Where(m => m.Id == id)
.Select(m => new { Manager = m,
Transactions = db.Transactions
.Where(t=>t.ManagerId == m.Id)
.Skip((page-1) * 10)
.Take(10)})
.FirstOrDefault();
This will not return instance of Manager class. But it should be easy to modify it to suit your needs.
Also you have two other options:
Load all transactions and then filter in memory. Of course if there are a lot of transactions this might be quite inefficient.
Don't be afraid to make 2 queries in database. This is prime example when that is probably the best route, and will probably be the most efficient way of doing it.
Either way, if you are concerned with performance at all I would advise you to test all 3 approaches and see what is the fastest. And please let us know what were the results!
Sometimes the added complexity of putting everything in a single query is not worth it. I would split this up into two separate queries:
var manager = db.Managers.SingleOrDefault(m => m.Id == id);
var transactions = db.Transactions
.Where(t => t.ManagerId == id)
// .OrderBy(...)
.Skip((page - 1) * 10).Take(10)
.ToList();
Note that after doing this, manager.Transactions can be used as well to refer to those just-loaded transactions: Entity Framework automatically links loaded entities as long as they're loaded into the same context. Just make sure lazy loading is disabled, to prevent EF from automatically pulling in all other transactions that you specifically tried to filter out.

The linq query taking too much time. Need to reduce the Time

Here i am using the below query and its taking lots of time around 14 to 15 seconds for retrieving the large amount of data.
In below Query the CreatedDate is of DateTimeOffset data type.
var naId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="NA").SalesPhaseId;
var rejectedId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="Rejected").SalesPhaseId;
var data = UnitOfWork.Leads.Query().AsEnumerable()
.Where(p =>(p.SalesPhaseId == naId || p.SalesPhaseId == rejectedId) &&
p.CreatedDate.Date >= fromDate && p.CreatedDate.Date <= toDate).Select(m =>
new
{
m.LeadId,
m.LeadOwnerId,
m.SalesPhaseId,
m.LeadActivities,
m.Employee,
m.SalesPhase,
m.CompanyName,
m.CreatedDate,
m.LeadHistories,
m.LeadAddresses
}).ToList();
I tried using the AsQueryable instead of the AsEnumerable but it gives the below error:
"The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."
Can you help me out to reduce the execution time of the query?
Your use of AsEnumerable is forcing the filtering to be done locally. It's pulling in all the data, then filtering it in your app. That's clearly very inefficient. Now, it seems that part of your query can't be directly expressed in LINQ to SQL. I see two options here.
Firstly you could do most of your filtering in SQL, but then do the date filtering locally:
var data = UnitOfWork.Leads.Query()
// Do this part of the query in SQL
.Where(p => p.SalesPhaseId == naId ||
p.SalesPhaseId == rejectedId)
.AsEnumerable()
// Do the rest of the query in-process
.Where(p => p.CreatedDate.Date >= fromDate &&
p.CreatedDate.Date <= toDate)
.Select(...)
That's suitable if the first part will filter it down massively, and then you only need to do local processing of a small set of data.
Alternatively, you could work out what your date filtering means in terms of DateTime. It looks like you could do:
// This may not be required, depending on the source.
fromDate = fromDate.Date;
// This will be, although you may be able to get rid of the ".Date" part.
toDate = toDate.Date.AddDays(1);
var data = UnitOfWork.Leads.Query()
// Do this part of the query in SQL
.Where(p => (p.SalesPhaseId == naId ||
p.SalesPhaseId == rejectedId) &&
p.CreatedDate >= fromDate &&
p.CreatedDate < toDate)
.Select(...)
That's created an equivalent query, but without using the Date property in the query itself.
Everything after AsEnumerable() is executed locally rather than on the server. See also
https://stackoverflow.com/a/2013876/141172
This means that all rows in the table are returned from the database, and then filtered in your C# code.
Remove that call so that the filtering happens server-side.
EDIT
Noticed Jon's comment and it reminded me that he reimplemented LINQ to Objects as a learning exercise. His comments about the AsEnumerable() reimplementation are worth reading
I can describe its behaviour pretty easily: it returns source.
That's all it does. There's no argument validation, it doesn't create another iterator. It just returns source.
You may well be wondering what the point is... and it's all about changing the compile-time type of the expression. I'm going to take about IQueryable in another post (although probably not implement anything related to it) but hopefully you're aware that it's usually used for "out of process" queries - most commonly in databases.
Now it's not entirely uncommon to want to perform some aspects of the query in the database, and then a bit more manipulation in .NET - particularly if there are aspects you basically can't implement in LINQ to SQL (or whatever provider you're using). For example, you may want to build a particular in-memory representation which isn't really amenable to the provider's model.
https://msmvps.com/blogs/jon_skeet/archive/2011/01/14/reimplementing-linq-to-objects-part-36-asenumerable.aspx
Your code should like this..
var naId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="NA").SalesPhaseId;
var rejectedId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="Rejected").SalesPhaseId;
var data = UnitOfWork.Leads.Query().AsQueryable()
.Where(p =>(p.SalesPhaseId == naId || p.SalesPhaseId == rejectedId) &&
p.CreatedDate>= fromDate.Date && p.CreatedDate <= toDate.Date).Select(m =>
new
{
m.LeadId,
m.LeadOwnerId,
m.SalesPhaseId,
m.LeadActivities,
m.Employee,
m.SalesPhase,
m.CompanyName,
m.CreatedDate,
m.LeadHistories,
m.LeadAddresses
}).ToList();
Firstly, You need to use .ToQueryable instead of .ToIEnumerable().
Secondly, you cannot use .Date to datetime properties inside a entity framework linq query. That only works for in-memory collections like list and arrays.

How do I translate this unsupported LINQ query to its native Mongo equivalent?

I have this chained LINQ query which MongoDB fails to execute:
RoleCollection.AsQueryable().Where(r => r.Users.Any(id => id == user.Id))
.Select(r => r.Name).ToArray();
This results in the following error:
Any is only support for items that serialize into documents. The current serializer is ObjectIdSerializer and must implement IBsonDocumentSerializer for participation in Any queries.
How can I translate the query into a native query which Mongo will support?
You should be able to replace .Any() with a combination of .Where() and a length check, like so:
RoleCollection.AsQueryable().Where(r => r.Users.Where(id => id == user.Id).Length > 0)
.Select(r => r.Name).ToArray();
Note however that there is a performance implication here since it will pull back all of the Users with that id to do a length on it.
I'm not sure what Mongo supports (Sorry I'm answering this purely from a linq perspective), but you could also use a combination of FirstOrDefault and perform a null check in your where. That might be better since you're only ever expecting one or nothing:
RoleCollection.AsQueryable().Where(r => r.Users.FirstOrDefault(id => id == user.Id) != null)
.Select(r => r.Name).ToArray();
I got the following query working, although I'm not entirely certain as to its semantics yet:
RoleCollection.Find(new QueryDocument("Users", user.Id))
.Select(r => r.Name).ToArray();

Linq Exception The expression must be a MemberExpression

i am working on a linq query and try to include entities upto multi level as per suggested in Mutilevel include in C# Linq.
So i write down a query like
query.Include(u => u.Stops.Select(d => d.Address).Select(c => c.City));
where query is
IQueryable<SomeEntity> query
and i get the exception
The expression must be a MemberExpression
Screenshot for my entities is
Kindly help, Thanks
This overload (extension?) of Include does not support inclusion through method chains. It does, however, support nesting of inclusion expressions:
query.Include(u => u.Stops.Select(d => d.Address.City));
// this would work too:
categories.Include(u => u.SubCategories.Select(c => c.Items.Select(i => i.Manufacturer)));
// equals
categories.Include("SubCategories.Items.Manufacturer");
The Select part is only for accessing members of collection type property items.

Using NHibernate LINQ, Is it possible to put a .Where() clause after a .ThenFetch()

I understand that in NHibernate LINQ, you need to have the fetches at the end of your code but how would you filter on something that is loaded via a ThenFetch?
Here is an example
IEnumerable<Project> list = Session.Query<Project>()
.FetchMany(r => r.ProjectSponsors)
.ThenFetch(r => r.Sponsor)
.Where(r => !r.Name == "Joe");
this above doens't work as it throws a NotSupportedException.
Any ideas on the right way to implement the above query?
No currently it isn't. Fetch can only be used as last statement.

Categories