Fetch data using LINQ query with left join and AsExpandable() - linq

I have following two tables: Images and Articles
Both the tables are linked by the ImageId column.
An image can be associated with multiple articles.
For example: Images table has 100 rows and Articles table has 200 rows.
Out of these 100 images assume that only 90 are used across the Articles. In this case some of the images are repeated across many articles.
Here I want to fetch the unused 10 images (from Images table) and also want to include the ones that are associated with articles not more than 2 times. I want to ignore those images which are associated with the articles more than 2 times.
I tried the below linq query but it is not working for me.
var predicate = PredicateBuilder.True<Image>();
if (type != null && type != 0)
{
predicate = predicate.And(c => c.ImageType == type);
}
if (!string.IsNullOrWhiteSpace(keyword))
{
predicate = predicate.And(c => c.Name.Contains(keyword) || c.Keyword.Contains(keyword));
}
int skip = numberofImages * (page - 1);
var images = (from imgs in context.Images
join art in context.Articles on imgs.ImageId equals art.ImageId into unusedImages
from img in unusedImages.DefaultIfEmpty()
group img by imgs.ImageId into grouped
.AsExpandable()
.Where(predicate)
orderby Guid.NewGuid(), imgs.ImageId descending
select imgs)
.Skip(skip).Take(numberofImages).ToList();
Can any one help me to fix this issue?

split your query something like this,i wont complicate what is preferably simple
var usedArticles = (from item in context.Articles
group item by imageid into newList
select new
{
imageid = newList.key,
count =newlist.count
}).ToList();
var unwaantedImageIds = usedArticles.where(x=>x.count>2).Select(y=>y.imageId).ToArray();
var unwantedImages =context.image.where(x=>unwaantedImageIds.contains(x.imageId));
var result = context.images.Except(unwaantedImageIds).ToList();

Related

LINQ - Where a list contains an element of another list

I have two SQL tables: Movies and Tags, while Movies contains a List<Tag>
Now I want to find all movies that have at least one of the tags of a List<Tag> argument. How can I do that in LINQ?
public IEnumerable<Group<Genre, Movie>> GetMoviesGrouped(string search, List<Tag> tags)
{
var movies = from movie in Movies
where ( movie.Name.Contains(search)) && movie.Tags.???contains any element of tags???
group movie by movie.genre into g
select new Group<Genre, Movie> { Key = g.Key, Values = g };
....
}
Movies where any of the tags is contains in tags:
var movies = from movie in Movies
where ( movie.Name.Contains(search))
&& movie.Tags.Any(t => tags.Contains(t))
group movie by movie.genre into g
select new Group<Genre, Movie> { Key = g.Key, Values = g };
However since this is comparing Tag instances and not strings:
It probably won't get translated to SQL (which means you will have to hydrate the query and do the extra filtering in Linq-to-Objects), and
You may want to compare the data of the tags rather than instances (unless you have overridden Equals on Tag
something like:
where movie.Tags.Any(mt => tags.Any(t => t.ID == mt.ID)) // or whatever property(ies) of `Tag` defines equality
You can intersect both list and see if there is any element like:
&& movie.Tags.Intersect(tags).Any()
Since Tag is an object, It will compare the references. Instead you can select unique identifier from Tag and then intersect that like:
&& movie.Tags.Select(r=> r.ID).Intersect(tags.Select(t=> t.ID)).Any()
Where ID is the primary key of Tag table.

Speed up LINQ query - EF5

I have the following LINQ query using EF5 and generic repository, unit of work patterns to a SQL Server 2008 db
var countriesArr = GetIdsFromDelimStr(countries);
var competitionsArr = GetIdsFromDelimStr(competitions);
var filterTeamName = string.Empty;
if (teamName != null)
{
filterTeamName = teamName.ToUpper();
}
using (var unitOfWork = new FootballUnitOfWork(ConnFooty))
{
// give us our selection of teams
var teams =
(from team in
unitOfWork.TeamRepository.Find()
where ((string.IsNullOrEmpty(filterTeamName) || team.Name.ToUpper().Contains(filterTeamName)) &&
(countriesArr.Contains(team.Venue.Country.Id) || countriesArr.Count() == 0))
select new
{
tId = team.Id
}).Distinct();
// give us our selection of contests
var conts = (
from cont in
unitOfWork.ContestRepository.Find(
c =>
((c.ContestType == ContestType.League && competitionsArr.Count() == 0) ||
(competitionsArr.Contains(c.Competition.Id) && competitionsArr.Count() == 0)))
select new
{
contId = cont.Id
}
).Distinct();
// get selection of home teams based on contest
var homecomps = (from fixt in unitOfWork.FixtureDetailsRepository.Find()
where
teams.Any(t => t.tId == fixt.HomeTeam.Id) &&
conts.Any(c => c.contId == fixt.Contest.Id)
select new
{
teamId = fixt.HomeTeam.Id,
teamName = fixt.HomeTeam.Name,
countryId = fixt.HomeTeam.Venue.Country.Id != null ? fixt.HomeTeam.Venue.Country.Id : 0,
countryName = fixt.HomeTeam.Venue.Country.Id != null ? fixt.HomeTeam.Venue.Country.Name : string.Empty,
compId = fixt.Contest.Competition.Id,
compDesc = fixt.Contest.Competition.Description
}).Distinct();
// get selection of away teams based on contest
var awaycomps = (from fixt in unitOfWork.FixtureDetailsRepository.Find()
where
teams.Any(t => t.tId == fixt.AwayTeam.Id) &&
conts.Any(c => c.contId == fixt.Contest.Id)
select new
{
teamId = fixt.AwayTeam.Id,
teamName = fixt.AwayTeam.Name,
countryId = fixt.AwayTeam.Venue.Country.Id != null ? fixt.AwayTeam.Venue.Country.Id : 0,
countryName = fixt.AwayTeam.Venue.Country.Id != null ? fixt.AwayTeam.Venue.Country.Name : string.Empty,
compId = fixt.Contest.Competition.Id,
compDesc = fixt.Contest.Competition.Description
}).Distinct();
// ensure that we return the max competition based on id for home teams
var homemax = (from t in homecomps
group t by t.teamId
into grp
let maxcomp = grp.Max(g => g.compId)
from g in grp
where g.compId == maxcomp
select g).Distinct();
// ensure that we return the max competition based on id for away teams
var awaymax = (from t in awaycomps
group t by t.teamId
into grp
let maxcomp = grp.Max(g => g.compId)
from g in grp
where g.compId == maxcomp
select g).Distinct();
var filteredteams = homemax.Union(awaymax).OrderBy(t => t.teamName).AsQueryable();
As you can see we want to return the following format which is passed across to a WebAPI so we cast the results to types we can relate to in the UI.
Essentially what we are trying to do is get the home and away teams from a fixture, these fixtures have a contest which relates to a competition. We then get the highest competition id from the grouping and then this is returned with that team. The country is related to the team based on the venue id, when I was originally doing this i had problems figuring out how to do OR joins in linq which is why i split it down to getting home teams and away team and then grouping them based on competition then unioning them together.
An idea of current table size is fixtures has 7840 rows, teams has 8581 rows, contests has 337 rows and competitions has 96 rows. The table that is likely to increase rapidly is the fixture table as this is related to football.
The output we want to end up with is
Team Id, Team Name, Country Id, Country Name, Competition Id, Competition Name
Using no filtering this query takes on average around 5 secs, just wondering if anybody has any ideas/pointers on how to make it quicker.
thanks in advance Mark
I can't judge whether it will speed up things, but your homemax and awaymax queries could be
var homemax = from t in homecomps
group t by t.teamId into grp
select grp.OrderByDescending(x => x.compId).FirstOrDefault();
var awaymax = from t in awaycomps
group t by t.teamId into grp
select grp.OrderByDescending(x => x.compId).FirstOrDefault();
Further, as you are composing one very large query it may perform better when you cut it up in a few smaller queries that fetch intermediary results. Sometimes a few more roundtrips to the database perform better than one very large query for which the database engine can't find a good execution plan.
Another thing is all these Distinct()s. Do you always need them? I think you can do without because you are always fetching data from one table without joining a child collection. Removing them may save a bunch.
Yet another optimization could be to remove the ToUpper. The comparison is done by the database engine in SQL and chances are that the database has a case-insensitive collation. If so, the comparison is never case sensitive even if you'd want it to be! Constructs like Name.ToUpper cancel the use of any index on Name (it is not sargable).

LINQ using Group with Count and Where, easy SQL, harder in LINQ

I'm trying to display cities names where a count is greater than 1. I can do it easy in SQL and am close in LINQ but can't figure out how to use group and also get a count and display a name
var query = (from c in Consumer
group c
by new { c.City, size = c.City.Count() }
into results
select new { Name = results.Key.City })
.Where(a => size > 0);
The size part doesn't work
try this query:
var list= Consumer.GroupBy(s=>s.City)
.Select(s=>new {
City = s.Key,
size = s.Count(),
})
.Where(s=>s.size>0).ToList();

Linq to SQL - Many to Many Predicates

I'm familiar with doing simple Many-to-Many relationships (i.e. simple joins) in Linq to SQL, but I'm having a hard time thinking right now.
I have three tables (and so, entities in my Linq-to-SQL model) representing a taxonomic system. Standard-issue really:
Products - ProductTags - Tags
I'm writing a method that returns a set of Products where the Tag they're in matches a query. So if someone searches for "foo" then all products assigned with the tags "foobar" or "fooqux" (but not "bazbar") would be returned.
I know I have to structure the query into two parts: first to get the matching Tags, and then to get the Products that have those tags. It's the second part I'm stumped on.
Here's what I've got so far:
var tags = from t in db.Tags
where t.Name.Contains( tagSearchQuery )
select t;
var products = from p in db.Products
// then a miracle happens
select p;
Assistance much appreciated :)
You can do it in one query if you just start with the ProductTags table. You'll probably also need a Distinct to avoid duplicate products matching multiple tags.
var products = (from pt in db.ProductTags
where pt.Tag.Name.Contains( tagSearchQuery )
select pt.Product).Distinct();
or here's another way:
var products = from p in db.Products
from pt in p.ProductTags
where pt.Tag.Name.Contains( tagSearchQuery )
select p
IQueryable<Tag> tags =
from t in db.Tags
where t.Name.Contains( tagSearchQuery )
select t;
IQueryable<Product> products =
from p in db.Products
where p.ProductTags.Any(pt => tags.Contains(pt.Tag))
select p;
OR
IQueryable<Product> products =
from p in db.Products
from pt in p.ProductTags
let t = pt.Tag
where t.Name.Contains( tagSearchFragment )
group t by p into g
select g.Key;

Linq query, how to build nested objects from single table

I have a single table and I need to build a bunch of nested objects based on the single table.
Data:
PointA PointB Month Time Price
1 2 11 11:00 10.99
1 2 12 11:00 9.99
Objects are
POINTS {PointA, PointB, Details}
Details {Month, ExtraDetails}
ExtraDetails {Time, Price}
I want to avoid having loads of loops and if statements, so should be able to use linq to do this. but its beyond my linq experience.
edit: These need grouping aswell
any help would be great.
Thanks
Just tried out a solution:
var nestedObjects = from row in data
select new {row.PointA, row.PointB, Details = new {
row.Month, ExtraDetails = new {
row.Time, row.Price
}
}};
This is assuming that you have already got your data into data.
Group by
If you want to group the Points together, you need 'Group By':
var nestedObjects = from row in data
group row by new { row.PointA, row.PointB } into Points
select new {
Points = Points.Key,
Details = from details in Points
select new { row.Month, ExtraDetails = new {
row.Time, row.Price
}}
};
A little more complicated - of course you might want to group by month as well, in which case, you need to follow the same pattern as for the Points bit. Note, this will not create tables, because the group by doesn't quite do that, but it at least creates the structure for you.
Assuming you got your classes defined for the objects you mentioned, and you have a constructor or properties so you can propery create the object in one line you could have a LINQ query returning a list of a POINTS.
If would go something lik this :
var res =
from item in table.AsEnumerable()
select new Points(){PointA = item["PointA"];
PointB = item["PointB"];
Details = from item2 in table.AsEnumberable()
where item["PointA"] = item2["PointA"] and item["PointB"] = item2["PointB"]
select new Details(){
month=item2["month"],
extraDetails = from item3 in table.AsEnumerable()...
}
};
At the end res will be a IEnumerable of Points
I am sorry for the code, I am not at a computer with .NET 3.5 so I cannot write a proper testable query

Resources