Return multiple aggregate columns in LINQ - linq-to-sql

I would like to translate the following SQL into LINQ:
SELECT
(Select count(BidID)) as TotalBidNum,
(Select sum(Amount)) as TotalBidVal
FROM Bids
I've tried this:
from b in _dataContext.Bids
select new { TotalBidVal = b.Sum(p => p.Amount), TotalBidNum = b.Count(p => p.BidId) }
but get an error "Bids does not contain a definition for "Sum" and no extension method "Sum" accepting a first argument of type "Bids" could be found.
How can I do this in LINQ?
Thanks
CONCLUDING:
The final answer was:
var ctx = _dataContext.Bids;
var itemsBid = (from b in _dataContext.Bids
select new { TotalBidVal = ctx.Sum(p => p.Amount), TotalBidNum = ctx.Count() }).First();

You can write this query using GroupBy. The Lambda expression is as follows:
var itemsBid = db.Bids
.GroupBy( i => 1)
.Select( g => new
{
TotalBidVal = g.Sum(item => item.Amount),
TotalBidNum = g.Count(item => item.BidId)
});

You could try this out. The variable b is an entity (for every iteration) while ctx is an entityset which has the extension methods you need.
var ctx = _dataContext.Bids;
var result = ctx
.Select( x => new
{
TotalBidVal = ctx.Sum ( p => p.Amount ),
TotalBidNum = ctx.Count( p => p.BidId )
} )
.First();

here's an alternative to scartag's solution:
(from b in _dataContext.Bids.Take(1)
select new
{
TotalBidVal = _dataContext.Bids.Sum(p => p.Amount),
TotalBidNum = _dataContext.Bids.Count()
}).Single();
Although there's no real reason you can't just say:
var result = new
{
TotalBidVal = _dataContext.Bids.Sum(p => p.Amount),
TotalBidNum = _dataContext.Bids.Count()
};
It hits the database twice, but its very readable

You could do it using the Aggregate Clause.
Aggregate t In _dataContext.Bids
Into TotalBidNum = Count(BidID),
TotalBidVal = Sum(Amount)
If you're using Fx4+ or an extension dll for Fx2, you could also benfit from parallelism by using
Aggregate t In _dataContext.Bids.AsParallel

Related

Select in multiple tables using EFCore

I have the MySql tables schema below (resumed):
I need to Select only the category data in a Query using EFCore:
List<CategoryViewModel> viewModel = await _context.Category
.Join(_context.Product_Category, c => c.CategoryId, pc => pc.CategoryId, (c, pc) => new { c, pc })
.Join(_context.Product, cpc => cpc.pc.ProductId, p => p.ProductId, (cpc, p) => new { cpc, p })
.Where(cpcp => cpcp.p.EstablishmentId == paramEstablishmentId) //paramEstablishmentId comes via parameter
.Select(vm => new CategoryViewModel()
{
Id = vm.cpc.pc.category.CategortId,
Name = vm.cpc.pc.category.Name,
Image = vm.cpc.pc.category.ImagePath,
Description = vm.cpc.pc.category.Description
})
.ToListAsync();
But this query always result a list with zero models inside. I guarantee there are values in the database to be returned.
Any Ideia what i'm doing wrong?
Many Thanks!
You should use Include()function instead of join. For eg :
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ToList();
Based on #Flyzzx Answer (many thanks, friend), i've modify my query to:
List<CategoryViewModel> viewModel = await _context.Product_Category
.Where(pc => pc.Product.EstablishmentId == EstablishmentId)
.Include(pc => pc.Product)
.Include(pc => pc.Category)
.Select(c => new CategoryViewModel()
{
Id = c.Category.Id,
Name = c.Category.Name,
Image = c.Category.ImagePath,
Description = c.Category.Description
}).Distinct()
.ToListAsync();
Basically, instead of select Categories, now i select Product_Category and use Include to add Products and Categories, making possible to use the Where Clause.

How to convert this request to LINQ?

SELECT StepID, count() as nb FROM Question GROUP BY StepID ORDER by nb;
You should probably go through the basics of LINQ. Microsoft Docs has a whole section dedicated to LINQ: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/
If you have your data in a List called as questions of type, List<Question> then you should be able to convert your Query like this:
var ret = from q in questions
group q by q.StepId into grouped
let count = grouped.Count()
orderby count
select new { StepId = grouped.Key, nb = count };
Query comprehension syntax:
from q in questions
group q by q.StepId into g
select new { StepId = g.Key, Count = g.Count() } into stepCount
orderby stepCount.Count
select stepCount;
Exact same in method syntax (which I prefer, since it can all query syntax can plus more and also often is more compact):
questions
.GroupBy(q => q.StepId)
.Select(g => new { StepId = g.Key, Count = g.Count() })
.OrderBy(stepCount => stepCount.Count)
Variant using another GroupBy overload:
questions
.GroupBy(q => q.StepId, (key, values) => new { StepId = key, Count = values.Count() })
.OrderBy(stepCount => stepCount.Count);

Sum List of IQueryable

Given a list IQueryables, how can you sum the count of each, without having multiple statements executed in the database?
return queries
.Sum(qy=> qy.Count());
The above works, but hits the database for each query.
You can first use the Aggregate function with Concat to combine the IQueryable's and then Count the total like this:
return queries.Aggregate((x,y) => x.Concat(y)).Count()
Starting from this idea Sum(q1,q2) = q1.Concat(q2).Count() I've tested the following extensions:
public static class LinqExtensions
{
public static IQueryable<object> ConcatAny<T,R>(this IQueryable<T> q1, IQueryable<R> q2)
{
return q1.Select(c=>(object)null).Concat(q2.Select(c=>(object)null));
}
public static IQueryable<object> ConcatAll(this IEnumerable<IQueryable<object>> queries)
{
var resultQuery = queries.First();
foreach (var query in queries.Skip(1))
{
resultQuery = resultQuery.ConcatAny(query);
}
return resultQuery;
}
}
I assumed you have heterogeneous queries like IQueryable<T>, IQueryable<R> so on and you are interested in counting all rows no matter which the source is.
So you might use these extensions like
var q1 = Table1.AsQueryable();
var q2 = Table2.AsQueryable();
var q3 = Table3.AsQueryable();
var queries = new IQueryable<object>[] {q1,q2,q3}; // we use here the covariance feature
return queries.ConcatAll().Count();
The generated SQL might look like this
SELECT COUNT(*) AS [value]
FROM (
SELECT NULL AS [EMPTY]
FROM (
SELECT NULL AS [EMPTY]
FROM [Table1] AS [t0]
UNION ALL
SELECT NULL AS [EMPTY]
FROM [Table2] AS [t1]
) AS [t2]
UNION ALL
SELECT NULL AS [EMPTY]
FROM [Table3] AS [t3]
) AS [t4]
I don't think is very effective though
Ok, a few minutes late, but I got it!
Here is the code:
public static class LinqExtensions
{
public static int CountAll(this IEnumerable<IQueryable<object>> queries)
{
if (queries == null || !queries.Any())
{
throw new ArgumentException("Queries parameter cannot be null or empty");
}
Expression ex = Expression.Constant(0);
foreach (var qy in queries)
{
// create count expression
var expression = Expression.Call(
typeof(Queryable),
"Count",
new[] { qy.ElementType },
qy.Expression
);
ex = Expression.Add(ex, expression);
}
return queries.First().Provider.Execute<int>(ex);
}
}
You use it as queries.CountAll() where queries is an IEnumerable<IQueryable<object>> as in Adrian's answer or even simple IEnumerable<IQueryable>.
Here is a sample SQL result from the profiler:
exec sp_executesql N'SELECT #p0 + ((
SELECT COUNT(*)
FROM [A] AS [t0]
WHERE [t0].[i1] >= #p1
)) + ((
SELECT COUNT(*)
FROM [B] AS [t1]
WHERE [t1].[i2] >= #p2
)) + ((
SELECT COUNT(*)
FROM [C] AS [t2]
WHERE [t2].[i3] >= #p3
)) AS [value]',N'#p0 int,#p1 int,#p2 int,#p3 int',#p0=0,#p1=2,#p2=2,#p3=2
Which is the representation of
var a = db.GetTable<A>();
var b = db.GetTable<B>();
var c = db.GetTable<C>();
var q1 = a.Where(v => v.i1 >= 2);
var q2 = b.Where(v => v.i2 >= 2);
var q3 = c.Where(v => v.i3 >= 2);
var queries = new IQueryable<object>[] {
q1,q2,q3
};
Note that A, B and C are different objects/tables with different numbers of properties/columns and that the expressions are random Where filters.
If you are using Entity Framework you can use an extension called EntityFramework.Extended. There is a built in extension called Future Queries. This will allow you to specify that a query should be executed the next time a trip to the database is made.
NuGet command:
Install-Package EntityFramework.Extended
Sample code:
static void Main(string[] args)
{
using (var context = new MyDbContext())
{
var modelSet1 = context.Models.Where(x => x.ModelId < 25).FutureCount();
var modelSet2 = context.Models.Where(x => x.ModelId > 25 && x.ModelId < 32).FutureCount();
var modelSet3 = context.Models.Where(x => x.ModelId > 32).FutureCount();
var queries = new [] {modelSet1, modelSet2, modelSet3};
var countQueries = queries.Sum(x => x.Value);
Console.WriteLine(countQueries);
}
Console.ReadLine();
}

Where to insert 'orderby' expression in this linq-to-sql query

var result = db.PhotoAlbums.Select(albums => new PhotoAlbumDisplay
{
AlbumID = albums.AlbumID,
Title = albums.Title,
Date = albums.Date,
PhotoID = albums.Photos.Select(photo => photo.PhotoID).FirstOrDefault().ToString()
});
Wherever I try to put orderby albums.AlbumID descending I get error. Someone knows solution?
Thanks!
This should work:
var result = db.PhotoAlbums.Select(albums => new PhotoAlbumDisplay
{
AlbumID = albums.AlbumID,
Title = albums.Title,
Date = albums.Date,
PhotoID = albums.Photos.Select(photo => photo.PhotoID).FirstOrDefault().ToString()
})
.OrderByDescending(item => item.AlbumID);
In query syntax:
var result = from albums in db.PhotoAlbums
orderby albums.AlbumID descending
select new PhotoAlbumDisplay
{
AlbumID = albums.AlbumID,
Title = albums.Title,
Date = albums.Date,
PhotoID = albums.Photos.Select(photo => photo.PhotoID).FirstOrDefault().ToString()
};
If you want to use the query syntax, you'll have to start with "from X select" and work from that. In this case it'd be easier to just use the .OrderBy() method to order the results.
Is this what you're looking for?
var result = db.PhotoAlbums.Select(albums => new PhotoAlbumDisplay
{
AlbumID = albums.AlbumID,
Title = albums.Title,
Date = albums.Date,
PhotoID = albums.Photos.Select(photo => photo.PhotoID).FirstOrDefault().ToString()
})
.OrderByDescending(a=>a.AlbumID);

Help to build LINQ query

I have SQL database as follows
alt text http://img97.imageshack.us/img97/5774/dbimage.jpg
Now I want to filter the restaurant_detail table for the parameters:
1. cuisine 2. area
Can you help me to build LINQ query?
I presume you have a model generated either with LINQ to SQL or Entity Framework. Also, I'm assuming foreign key relationships have been set.
var details = db
.Cuisines
.Where(c => c.Cuisine=="something")
.SelectMany(c => c.RestaurantCuisines)
.Select(rc => rc.Restaurant.RestaurantDetails)
.Where(rd => rd.Area=="something")
;
Done with the linq query using following lines of code :
c = from q in dc.restaurant_cuisines
where q.cuisine.cuisine1.Contains(cuisine)
&& q.restaurant.price.ToString().Length == price.Length
select new NearBy { NearById = q.restaurant.id, NearByLongitude = (double)q.restaurant.longitude, NearByLatitude = (double)q.restaurant.latitude };
}
int[] ids = new int[c.Count()];
var lon = from q1 in dc.area_maps where q1.area.ToLower() == area.ToLower() select q1.longtitude;
var lat = from q1 in dc.area_maps where q1.area.ToLower() == area.ToLower() select q1.latitude;
foreach(NearBy n in c)
{
result = calcDistNew((double)lat.FirstOrDefault(), (double)lon.FirstOrDefault(), n.NearByLatitude, n.NearByLongitude);
ids[i++] = n.NearById;
}
var r = from q in dc.restaurant_details
where 1 == 1 &&
(ids).Contains(q.restaurant_id)
select new Restaurant
{
Restora_id = q.restaurant_id.ToString(),
Name = q.restaurant.name,
Foodtype = q.restaurant.foodtype.foodtype1,
Avg_rating = q.restaurant.avg_rating.ToString(),
Featured = q.restaurant.featured.ToString(),
CuisineList = getCuisine(q.restaurant_id),
Restora_type = q.type,
Distance = Math.Round(calcDistNew((double)lat.FirstOrDefault(), (double)lon.FirstOrDefault(), (double)q.restaurant.latitude, (double)q.restaurant.longitude), 2),
Newarrival = q.restaurant.newarrival.ToString(),
CountRecord = ids.Length.ToString()
};
var d = r.AsEnumerable().OrderBy(t => t.Distance);
var g = d.Take(recordSize + 10).Skip(recordSize);
return g.ToList();
Please note that above displayed code generated with some changes from the initial requirements.