Linq - Getting calendar events that have Start and Length properties - linq

My application has a built-in calendaring system and the database schema for them looks like this:
CalendarItem( CalendarItemId bigint, Start datetime, Length int, Blargh nvarchar(MAX) )
Start is the UTC datetime value when the event starts, and Length is the length of the event in seconds. An all-day event starts at 0000h and has a length of 86400.
I'm using Linq with Entity Framework, and I want to find events that fall within a date range. It's easy to find events that start between two datetimes, but I don't know how to find events that also end between two datetimes.
Here's my current code:
public IEnumerable<CalendarItem> GetCalendarItems(DateTime from, DateTime to) {
var events = from c in db.CalendarItems
where c.Start >= from && c.Start <= to
orderby c.Start
select c;
return events;
}
If I were using T-SQL, I would need to use DATEADD to add Length seconds to Start to give an End datetime, which would then work, but I don't think I can do this in Linq. What can I do?

EDITED with ToList() function included:
If I'm reading this correctly, you would want:
var events = (from c in db.CalendarItems
where c.Start >= from && c.Start <= to
orderby c.Start
select c).ToList();
events = events.Where(e => e.Start.AddSeconds(Length) <= to);
return events;
This will then give you the events that started and ended during the specified date range.
For more information about DateTime.AddSeconds(), just visit this link.

You will need to call ToList() first before you can use the DateTime.AddSeconds function. Otherwise the compiler will complain that it cannot find the AddSeconds function since your LINQ queries will be translated to SQL and SQL does not contain this DateTime.AddSeconds function.
var events = (from c in db.CalendarItems
where c.Start >= from && c.Start <= to
orderby c.Start
select c).ToList();
events = events.Where(e => e.Start.AddSeconds(Length) <= to);
return events;
EDIT: Corrected my logic, the answer is now identical to IronMan84's.

I evaluated the .ToList approaches, but they're inefficient because after modifying it to return events that occur (regardless of whether or not they started or ended within) a time period it fetches many irrelevant results from the database.
I also looked at the SqlFunctions methods, but they aren't present in EF1.0.
I ended up using a Sproc with a strongly-typed import on my Entity Context. It's not perfect, but it's better than the alternatives.
When the project is upgraded to .NET4 eventually, I'll switch to SqlFunctions. Thanks for all of the advice anyway!

Related

How to get top 10 customers based on values of orders

I have a list of Customers who each have a list of Orders. Each Order has a list of LineItems.
I would like to write a LINQ query that would get me the top 10 customers based on order value (i.e. money spent) and not the total number of orders.
One customer could have 2 orders but could have spent £10,000, but another customer could have 100 orders, and only spent £500.
Right now, I have this which gets me the top 10 customers by the number of orders.
var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
let activeCount = c.SaleOrders.Count(so => so.Status != SaleOrderStatus.Cancelled)
orderby activeCount descending
select c).Take(10);
UPDATE
Thanks to Jon Skeet's comment about doing a double Sum, I wrote the following query which compiles.
var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
let orderSum = c.SaleOrders.Where(so => so.Status != SaleOrderStatus.Cancelled)
.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
orderby orderSum descending
select c).Take(10);
But when I run this, I get the following error:
It seems LINQ doesn't recognise my .CalculateTotal() method which sit on my LineItem.cs entity.
The problem you were seeing is that CalculateTotal() is not something that Linq can translate into SQL (which is done at run-time, hence no complier error).
The essential problem here is that Linq doesn't really work on lambdas (Func<>), but actually Expressions (Expression<Func<>>), which is the code in a partial compiled state, which Linq then goes about disassembling and translating into SQL.
So, let assume CalculateTotal is a member function defined like this:
public decimal CalculateTotal()
{
return this.quantity * this.value;
}
We could define that as a local lambda function
Func<LineItem, decimal> CalculateTotal = (li => li.quantity * li.value);
Now, we have a lambda which takes a LineItem and returns a value, which is exactly what Sum() wants, so now we can replace:
.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
with
.Sum(so => so.LineItems.Sum(CalculateTotal))
But that will crash, just as it did before, because, as I said, it wants an Expression. So, we give it one:
Expression<Func<LineItem, decimal>> CalculateTotal = (li => li.quantity * li.value);

linq select clause syntax

Linq Select method takes Func as input parameter. This means I can have multiple statements in selector for Select, such as
var myresult = sources.Select(s =>
{int x; if (s.val = high) {x=1} else if (s.val = med) {x=2} else {x=3}; return x;
}
)
How can I do this using Linq query syntax
var myresult = from s in sources
select ...
Here, the code in Func part (if ... else if .. else) is artificial. What I really want to know is the syntax of select clause, which may be described as
select select-expression
What is the syntax of
select-expression
I wouldn't want to see your first version in my code. If you need to have what is basically a full function in the lambda, I would rather see the lambda simply invoke a full function! In other words...
theQuery.Select(s => GetX(s)); // just define a GetX function
And that would also be a straightforward translation to query expression syntax
from s in sources
select GetX(s);
You would not be able to put your full code block into the query expression syntax. You could translate your given logic to something usable (yet messy), however I'm quite sure your snippet is just a general example. On the offhand change it isn't, you might try
select s.val == high ? 1 : (s.val == med ? 2 : 3); // totally messy
Instead of special-casing values, with the if/else equivalent of a switch statement, it is more Linq-friendly to group and filter your values:
var myResult = from s in sources
group by s.val into g
select new { Val = g.Key, Sources = g };
var groupHigh = myResult.Where(i => i.Val == high);
var groupMedium = myResult.Where(i => i.Val == medium);
var groupOther = myResult.Except(groupHigh.Concat(groupMedium));
Note that the code I've provided is just a starting place, and isn't the best way to achieve your specific goal. I'd address this in one of these ways:
Change how group by is used (use SomeFunction(s.Val) instead of directly using s.Val)
Change the code around this query to flow better with the natural groupings, so I didn't require the groups to be transformed
This is not possible.
If you really want to, you could create an Func<T> from an anonymous method and invoke it, but that would be horrible.
MSDN indicates select is a contextual keyword of C# 4.0. So I checked the C# Language Specififcation 4.0. Its Select clauses section (7.16.2.5) specifies that
A query expression of the form
from x in e select v
is translated into
( e ) . Select ( x => v )
except when v is the identifier x, the translation is simply
( e )
As the result, the syntax for
select select-expresion
select-expression should be anything that can be used as TResult in Select Method. So the functionality can be done using anonymous Func in Select method may not be able to achieved using select clause.
Conclusion is that you should stick with Method syntax as this is how the code really runs behind the scene.

Wait for DomainContext.Load<t> from an entityquery with joins to complete (returning new type via 'select new')

My app consolidates data from other DBs for reporting purposes. We can't link the databases, so all the data processing has to be done in code - this is fine as we want to allow manual validation during the imports.
Certain users will be able to start an update through the Silverlight 4 front end.
I have 3 tables in database x that are fed from one EF4 Model (ModelX). I want to join those tables together, select specific columns and return the result as a new entity that exists in a different EF4 Model (ModelY). I'm using this query:
var myQuery = from i in DBx.table1 from it in DBx.table2 from h in DBx.table3 where (i.id==it.id && h.otherid == i.otherid) select new ModelYServer {Name = i.name,Thing = it.thing, Stuff = h.stuff};
The bit i'm stuck on, is how to execute that query, and wait until the Asynchronous call has completed. Normally, i'd use:
DomainContext.Load<T>(myQuery).Completed += (sender,args) =>
{List<T> myList = ((LoadOperation<T>)sender.Entities.ToList();};
but I can't pass myQuery (an IEnumerable) into the DomainContext.Load() as that expects an EntityQuery. The dataset is very large, and is taking up to 30 seconds to return, so I definitely need to wait before continuing.
So can anyone tell me how I can wait for the IEnumerable query to complete, or suggest a better way of doing this (there very likely is one).
Thanks
Mick
One simple way is just to force it to evaluate by calling ToList:
var query = from i in DBx.table1
join it in DBx.table2 on i.id equals it.id
join h in DBx.table3 on i.otherid equals h.otherid
select new ModelYServer {
Name = i.name,
Thing = it.thing,
Stuff = h.stuff
};
// This will block until the results have been fetched
var results = query.ToList();
// Now use results...
(I've changed your where clause into joins on the earlier tables, as that's what you were effectively doing and this is more idiomatic, IMO.)

Linq filter collection with EF

I'm trying to get Entity Framework to select an object and filter its collection at the same time. I have a JobSeries object which has a collection of jobs, what I need to do is select a jobseries by ID and filter all the jobs by SendDate but I can't believe how difficult this simple query is!
This is the basic query which works:
var q = from c in KnowledgeStoreEntities.JobSeries
.Include("Jobs.Company")
.Include("Jobs.Status")
.Include("Category")
.Include("Category1")
where c.Id == jobSeriesId
select c;
Any help would be appreciated, I've been trying to find something in google and what I want to do is here:http://blogs.msdn.com/bethmassi/archive/2009/07/16/filtering-entity-framework-collections-in-master-detail-forms.aspx
It's in VB.NET though and I couldn't convert it to C#.
EDIT: I've tried this now and it doesn't work!:
var q = from c in KnowledgeStoreEntities.JobSeries
.Include("Jobs")
.Include("Jobs.Company")
.Include("Jobs.Status")
.Include("Category")
.Include("Category1")
where (c.Id == jobSeriesId & c.Jobs.Any(J => J.ArtworkId == "13"))
select c;
Thanks
Dan
Include can introduce performance problems. Lazy loading is guaranteed to introduce performance problems. Projection is cheap and easy:
var q = from c in KnowledgeStoreEntities.JobSeries
where c.Id == jobSeriesId
select new
{
SeriesName = c.Name,
Jobs = from j in c.Jobs
where j.SendDate == sendDate
select new
{
Name = j.Name
}
CategoryName = c.Category.Name
};
Obviously, I'm guessing at the names. But note:
Filtering works.
SQL is much simpler.
No untyped strings anywhere.
You always get the data you need, without having to specify it in two places (Include and elsewhere).
No bandwith penalties for retrieving columns you don't need.
Free performance boost in EF 4.
The key is to think in LINQ, rather than in SQL or in materializing entire entities for no good reason as you would with older ORMs.
I've long given up on .Include() and implemented Lazy loading for Entity Framework

Linq to EF Expression Tree / Predicate int.Parse workaround

I have a linq Entity called Enquiry, which has a property: string DateSubmitted.
I'm writing an app where I need to return IQueryable for Enquiry that have a DateSubmitted within a particular date range.
Ideally I'd like to write something like
IQueryable<Enquiry> query = Context.EnquirySet.AsQueryable<Enquiry>();
int dateStart = int.Parse("20090729");
int dateEnd = int.Parse("20090930");
query = (from e in query
where(enq => int.Parse(enq.DateSubmitted) < dateEnd)
where(enq => int.Parse(enq.DateSubmitted) > dateStart)
select e);
Obviously Linq to EF doesn't recognise int.Parse, so I think I can achieve what I want with an Expression method that returns a predicate???
I've been playing around with PredicateBuilder and looking all over but I've successfully fried my brains trying to work this out. Sure I could add another property to my Entity and convert it there but I'd really like to understand this. Can anyone explain or give an example/link that doesn't fry my brains?
Thanks in advance
Mark
If you know your date strings are valid, and they're really in that order (which is a natural sort order) you might be able to get away with string comparisons:
IQueryable<Enquiry> query = Context.EnquirySet.AsQueryable<Enquiry>();
string dateStart ="20090729";
string dateEnd = "20090930";
query = (from e in query
where(enq => enq.DateSubmitted.CompareTo(dateEnd)) < 0)
where(enq => enq.DateSubmitted.CompareTo(dateStart)) > 0)
select e);

Resources