LINQ Group and SelectMany - linq

I have a great LINQ statement that Oracle doesn't like:
var result = from r in Context.Accounts
where Statuses.Contains(r.DEC_CD)
&& r.Deposit.Payments.Where(n => n.CreatedDate >= DateStart).Sum(n => n.Total - n.Fees) > 3000
select r;
Unfortunately the .Where(...).Sum(...) creates invalid SQL using the Oracle EF provider.
I have tried to rewrite it using group instead:
var result = from g in Context.Payment
where g.CreatedDate >= DateStart
group g by g.Total - g.Fees into grp
where grp.Key >= 3000
select g;
The above example does not compile.
var result = from g in Context.Payment
where g.CreatedDate >= DateStart
group g by g.Total - g.Fees into grp
where grp.Key >= 3000
select new { g };
Also does not compile
var result = from g in Context.Payment
where g.CreatedDate >= DateStart
group g by g.Total - g.Fees into grp
where grp.Key >= 3000
select grp.SelectMany(n => n);
Looks like it's going to work from Intellisense, but I get an error The type arguments for method SelectMany cannot be inferred from the usage
The only thing I am able to select is simply grp and if I select that I get Igrouping<decimal, Payment>' which has keys and multiple rows underneath. I just want the rows, hence the.SelectMany`
Any idea how to get a flattened IEnumerable<Payment>?

You probably just want this
var result = from g in Context.Payment
where g.CreatedDate >= DateStart
&& (g.Total - g.Fees) >= 3000
select g;
Right? All Payments where total - fees is gte 3000 and the date criteria. It seems the group is not intended or needed.

You have to add a from statement to re-select the group:
var result = from g in Context.Payment
where g.CreatedDate >= DateStart
group g by g.Total - g.Fees into grp
where grp.Key >= 3000
from i in grp
select i;

var result =
from p in Context.Payment
where p.CreatedDate >= DateStart
group p by p.Total - p.Fees into g
where g.Key >= 3000
select g; // select group here
Or better without grouping:
var result =
from p in Context.Payment
where p.CreatedDate >= DateStart &&
(p.Total - p.Fees) >= 3000
select p;

Related

Linq how to join tables with a where clause on each table and a count

The following LINQ query isn't allowed when I use multiple where elements - it stops liking the 'into':
var query =
from ph in _db.PlayHits
join ua in _db.UserAgents on ph.UserAgentId equals ua.UserAgentId
where (ph.VideoId == 1 && ua.AgentString.Contains("test"))
into hits
select new
{
ResultCount = hits.Count()
};
Any idea why or how I amend this?
The equivilent sql I want is:
select count(*) as ResultCount
from Playhits ph
join UserAgents ua on ph.UserAgentId = ua.UserAgentId
where ph.VideoId = 1
and ua.AgentString like '%test%'
To my understanding, counting of the results can be done using the extension methond Count as follows.
var query =
from ph in _db.PlayHits
join ua in _db.UserAgents on ph.UserAgentId equals ua.UserAgentId
where (ph.VideoId == 1 && ua.AgentString.Contains("test"));
int Result = query.Count();

Linq query joining with a subquery

I am trying to reproduce a SQL query using a LINQ to Entities query. The following SQL works fine, I just don't see how to do it in LINQ. I have tried for a few hours today but I'm just missing something.
SELECT
h.ReqID,
rs.RoutingSection
FROM ReqHeader h
JOIN ReqRoutings rr ON rr.ReqRoutingID = (SELECT TOP 1 r1.ReqRoutingID
FROM ReqRoutings r1
WHERE r1.ReqID = h.ReqID
ORDER BY r1.ReqRoutingID desc)
JOIN ReqRoutingSections rs ON rs.RoutingSectionID = rr.RoutingSectionID
Edit***
I was able to get this working after looking at other examples including the one provided her by Miki. Here is the code that works for me:
First I created a query called route to hold the top record I needed to join to
var route = (from rr in context.ReqRoutings
where rr.ReqID == id
orderby rr.ID descending
select rr).Take(1);
I was then able to join to my requisitions table and the ReqRoutings lookup table
var header = (from h in context.ReqHeaders
join r in route on h.ID equals r.ReqID
join rs in context.ReqRoutingSections on r.RoutingSectionID equals rs.ID
where h.ID == id
select {ReqID = h.ID,
RoutingSection = rs.RoutingSection}
I am using Northwnd sample database
Customers,Orders,Employees table
Here I am getting top 1 order group by customer and order's employeeid
Please let me know If this is matching with your requirement or not
var ord = from o in NDC.Orders
orderby o.OrderID descending
group o by o.CustomerID into g
select new {CustomerID=g.Key,Order=g.OrderByDescending(s=>s.OrderID).First() };
var res1 = from o in ord
join emp in NDC.Employees
on o.Order.EmployeeID equals emp.EmployeeID into oemp
select new {Order=o.Order,employee=oemp };
Response.Write(res1.ToList().Count);
foreach (var order in res1)
{
Response.Write(order.Order.CustomerID + "," +
order.Order.OrderID + ","+
order.Order.EmployeeID+"<br/>");
}
// Above code is working .I have tried to convert your query to linq and replace your datacontext name with 'NDC'
var ord = from rr in NDC.ReqRoutings
orderby rr.ReqRoutingID descending
group rr by rr.ReqID into g
select new
{
ReqID = g.Key,
ReqRoutings = g.OrderByDescending(s => s.ReqRoutingID).First()
};
var res1 = from o in ord
join emp in NDC.ReqRoutingSections on o.ReqRoutings.RoutingSectionID
equals emp.RoutingSectionID into oemp
select new { ReqRoutings = o.ReqRoutings, employee = oemp };
Response.Write(res1.ToList().Count);
foreach (var order in res1)
{
Response.Write(order.ReqRoutings.ReqID + "," +
order.ReqRoutings.ReqRoutingID + "," +
order.ReqRoutings.RoutingSectionID + "<br/>");
}
Please let know if it is help you or not

Linq group by a nullable timestamp (use only date part) and count

I am trying to convert this SQL to linq syntax, but I'm having some difficulties at that.
SELECT trans_date, count(*) FROM table1
GROUP BY trans_date
Result
01-01-2001 | 12
03-01-2001 | 45
var q = from a in table1
let dt = q.trans_date.value
group q by new {y = dt.Year.ToString(), m = dt.Month.ToString(), d = dt.Day.toString()}
into g
select new { int NumOf = g.Count(), DateTime TransDate = DateTime.Parse(g.Key.y + g.key.m + g.Key.d)}
This last "version" with the DateTime.Parse gives me a runtime error.
How to handle that the q.trans_date is a nullable DateTime and getting my resultset??
For one thing, you're not getting a runtime error with that code - you'll be getting a compile-time error as your code isn't valid C#.
Try this:
var q = from a in table1
group a by a.trans_date.value.Date into g
select new {
Count = g.Count(),
TransDate = g.Key
};

Joining queries with Entity Framework

I have the table
MOVIES_RATING:
MovieID int
MovieRating decimal
I'd like to get 2 values using one query:
select COUNT(*)
FROM [dbo].[MOVIES_RATING]
where [dbo].[MOVIES_RATING].[MovieID] = 78
and
select SUM([dbo].[MOVIES_RATING].Rating)
FROM [dbo].[MOVIES_RATING]
where [dbo].[MOVIES_RATING].[MovieID] = 78
That's what I got in LINQ:
(from p in ef.MOVIES_RATING.Where(r => r.MovieID== movie_id)
let movieRates = ef.MOVIES_RATING.Where(r => r.MovieID == movie_id)
let count = movieRates.Count()
let averageUserRating = movieRates.Sum(c => c.MOVIES_RATING)/count
select new MovieRating {AverageUserRating = averageUserRating, VoteCount = count})
.Take(1);
Looks awful as well as SQL being generated:
SELECT
[Limit1].[MovieID] AS [MovieID],
[Limit1].[C2] AS [C1],
[Limit1].[C1] AS [C2]
FROM ( SELECT TOP 1
[GroupBy1].[A1] AS [C1],
[Extent1].[MovieID] AS [MovieID],
[GroupBy2].[A1] / CAST( [GroupBy1].[A1] AS decimal(19,0)) AS [C2]
FROM [dbo].[MOVIES_RATING] AS [Extent1]
CROSS JOIN (SELECT
COUNT(1) AS [A1]
FROM [dbo].[MOVIES_RATING] AS [Extent2]
WHERE [Extent2].[MovieID] = 78 ) AS [GroupBy1]
CROSS JOIN (SELECT
SUM([Extent3].[Rating]) AS [A1]
FROM [dbo].[MOVIES_RATING] AS [Extent3]
WHERE [Extent3].[MovieID] = 78 ) AS [GroupBy2]
WHERE [Extent1].[MovieID] = 78
) AS [Limit1]
I'm not sure it is best solution, so any help is appreciated.
I know It can be done using stored procedure, but if its could be done using LINQ it would be better.
from r in ef.MOVIES_RATING
group r by r.MovieID into g
where g.Key == movie_id
select new
{
Count = g.Count(),
Sum = g.Sum(r => r.Rating)
}
(or perhaps filter first then group; it probably translates to the same SQL anyway)
Another approach, using Aggregate:
ef.MOVIES_RATING
.Where(r => r.MovieID == movie_id)
.Aggregate(
new { Count = 0, Sum = 0 },
(acc, r) => new { Count = acc.Count + 1, Sum = acc.Sum + r.Rating });
(not sure how it translates to SQL though)
What about simple query:
var query = from m in context.Movies
where m.Id == 78
select new
{
Count = m.MovieRatings.Count(),
Sum = m.MovieRatings.Sum(mr => mr.Rating)
};
var data = query.SingleOrDefault();
Moving average computation to your application code should reduce the SQL query complexity.

Filter a query by Date

I have the following query
var query =
from f in _db.Production
join g in _db.Run on f.show equals g.Production.show
select new ViewProductions {
Venuename = g.venue,
Showname = f.show,
StartDate = g.startDate,
EndDate = g.endDate
};
return View(query);
How would i add a where clause that would say
Where start date is in the next 3 months from today?
Thanks
Update
Working code
var now = DateTime.UtcNow;
var limit = now.AddDays(90);
var query =
from f in _db.Production
join g in _db.Run on f.show equals g.Production.show
where g.endDate >= now && g.startDate <= limit
select new ViewProductions
{
Venuename = g.venue,
Showname = f.show,
StartDate = g.startDate,
EndDate = g.endDate
};
return View(query);
Thanks again for all your help.
As you can't use any DateTime.Add* methods in linq-to-sql or entity framework, you'll have to use variables:
var now = DateTime.UtcNow;
var limit = now.AddDays(90);
var query = from f in _db.Production
join g in _db.Run on f.show equals g.Production.show
where g.StartDate >= now && g.StartDate <= limit
select new ViewProductions {
Venuename = g.venue,
Showname = f.show,
StartDate = g.startDate,
EndDate = g.endDate
};
Since this is Linq to Sql/Entities you will have to calculate the date first, then use it in the query:
DateTime futureDate = DateTime.Now.AddMonths(3);
from f in _db.Production
join g in _db.Run on f.show equals g.Production.show
where g.StartDate <= futureDate && g.StartDate >= DateTime.Now
...
Add this line yo your query:
var query =
from f in _db.Production
join g in _db.Run on f.show equals g.Production.show
where g.startDate > DateTime.Today &&
g.startDate < DateTime.Today.Add(TimeSpan.FromDays(90))
....

Resources