I currently have the following logic that builds a list of 4 integers, where each integer represents a sum of all votes for a certain item ID (1, 2, 3 or 4):
List<int> totals = new List<int>();
using (RepositoryEntities entityContext = new RepositoryEntities())
{
totals.Add(entityContext.ItemVotes.Count(v => v.Vote == 1));
totals.Add(entityContext.ItemVotes.Count(v => v.Vote == 2));
totals.Add(entityContext.ItemVotes.Count(v => v.Vote == 3));
totals.Add(entityContext.ItemVotes.Count(v => v.Vote == 4));
}
This works very well, but I'm questioning the efficiency of such querying, because this seems to actually generate and execute 4 separate queries. Ideally I'd want to have a single, efficient query that returns me the 4 sums.
Any ideas?
Thanks in advance.
You could wrap your logic into one query
totals.AddRange(entityContext.ItemVotes
.Where(iv => iv.Vote >= 1 && iv.Vote <= 4)
.GroupBy(iv => iv.Vote)
.OrderBy(grp => grp.Key)
.Select(grp => grp.Count());
(This code is untested and could be way off base but just throwing out an idea)
in LINQ the .Count() method forces execution of a query. If you need 4 different totals there is no other way to produce that in one statement.
Related
I have this:
var myResult = uow.GetRepository<SLItemsCustomersCard, long>()
.Query(x => x.CustomerId == customerId && x.SquareColor == squareColor)
.OrderBy(x => x.BranchMapRang)
.Select((r, i) => new { Row = r, Index = i })
.Where(x => x.Index == visitCounter - 1).ToList();
but I want to achive this in where clause:
.Where(x => x.Index.Cotains(visitCounter)).ToList();
How to do this?
You seem to be misunderstanding what the Contains method does. I'm basing this answer on your earlier usage of:
Where(x => x.Index == visitCounter - 1)
In other words, visitCounter is an integer (and so is Index). But then you want to use it like this:
Where(x => x.Index.Contains(visitCounter))
Which does not make syntactical sense. An integer (Index) does not have a Contains function. It's not fully clear to me what you are trying to achieve, but your comment clarifies it a bit more:
But I want to achieve something like IN clause in SQL server.
The IN clause in SQL requires a range of possibilities (a list of integers, in your case), and you're not working with a list of integers here. Furthermore, you have phrased it as Index.Contains(visitCounter) which would imply that you're expecting Index to be the list of integers?
That simply doesn't make sense. So I'll give you the answer that makes the most sense, on the assumption that you weren't accurate with your pseudocode:
List<int> visitorIds = new List<int>() { 1, 5, 99, 125 };
And then you can do the following:
.Where(x => visitorIds.Contains(x.Index))
To put it in words, this snippet basically tells the computer to "only give the items whose Index is mentioned in the visitorIds list".
You can use Contains like this:
int[] VisitorIds = new int[] {1,2,3}; //an array to check with
.Where(x => vivitorIds.Contains(x.Index)).ToList();
Let say you have list of items and you want to partition them, make operation on one partition and concatenate partitions back into list.
For example there is list of numbers and I want to partition them by parity, then reverse odds and concatenate with evens. [1,2,3,4,5,6,7,8] -> [7,5,3,1,2,4,6,8]
Sounds simple, but I've got stuck on merging back two groups. How would you do it with LINQ?
IEnumerable<int> result = Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
.??? // need to concatenate
Edit
[1,2,3] is the representation of array which I want to get as the result, not output, sorry if I confused you by that.
The GroupBy method returns an IEnumerable<IGrouping<TKey, TSource>>. As IGrouping implements IEnumerable, you can use SelectMany to concatenate multiple IEnumerable<T> instances into one.
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
.OrderByDescending(p => p.Key)
.SelectMany(p => p);
There are a few ways to achieve this,
so if we start with your function
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
you could then use an Aggregate
.Aggregate((aggrgate,enumerable)=>aggrgate.Concat(enumerable))
this will then go though your list of results and concat them all into a collection and return it, you just need to make sure that aggrgate and enumerable are the same type in this case a IEnumerable<int>
another would be to call SelectMany()
.SelectMany(enumerable=>enumerable)
this then likewise pulls all the enumerables together into a single enumerable, again you need to ensure the types are IEnumerable<int>
other options would be to hard code the keys as Tim is suggesting or pull out of linq and use a loop
You could use this approach using a Lookup<TKey, TElement>:
var evenOddLookup = numbers.ToLookup(i => i % 2);
string result = String.Join(",", evenOddLookup[1].Reverse().Concat(evenOddLookup[0]));
If you don't want a string but an int[] as result:
int[] result = evenOddLookup[1].Reverse().Concat(evenOddLookup[0]).ToArray();
You could do something like this.
var number = string.Join(",",
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2) // Separate even/odd numbers
.OrderByDescending(x=>x.Key) // Sort to bring odd numbers first.
.SelectMany(x=> x.Key ==1? // Sort elements based on even or odd.
x.OrderByDescending(s=>s)
: x.Where(s=> s!=0).OrderBy(s=>s))
.ToArray());
string output = string.Format("[{0}]", number);
Check this Demo
Just use OrderBy like this:
List<int> arr = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
var result = arr.OrderBy(i => i % 2 == 0 ? 1 : 0)
.ThenBy(i => i % 2 == 0 ? i : int.MaxValue)
.ThenByDescending(i => i);
This should give you your desired result as you want:
[1,2,3,4,5,6,7,8] will be converted into [7,5,3,1,2,4,6,8]
I am using linq to sql and my code looks like this:
var agendaLists =
dataContext.view_Agendas.Where(m => m.MeetingID == meetingID)
.OrderBy(n => n.Number)
.ThenBy(n => n.CaseNumber)
.ThenByDescending(s => s.MainYN)
.ToList();
So basicly our "n.Number" is the order number. everything works fine its sorted 1,2,3,4 etc which is correct. but if a object have no value in n.Number it will be displayed in the top but I want it to be placed last.
Today it sorts like this, lets say we get 4 objects back:
Null, 1, 2, 3
I want it sorted like this:
1, 2, 3, Null
Any kind of help is appreciated.
try the following:
n => n.Number ?? int.MaxValue
I dont remember if you actually have to check for DBNull, but it would be the same.
You can use this approach, presuming Number is an int?:
var agendaLists = dataContext.view_Agendas
.Where(m => m.MeetingID == meetingID)
.OrderBy(m => n.Number.HasValue ? 0 : 1)
.ThenBy(n => n.Number)
.ThenBy(n => n.CaseNumber)
.ThenByDescending(s => s.MainYN)
.ToList();
I have a List<Locations> that will be filtered to yield a set of results relevant to a search term.
At the moment, I tried these 'search results' by filtering with the following:
return locations.Where(o => o.Name.Contains(term)).Take(10).ToList();
Problem
If I were to enter 'Chester' as the search term, I will never see the item "Chester" despite it existing in the locations list. The reason for this is that there are 10 or more other items in the list that contain the String "Chester" in their name (Manchester, Dorchester etc.).
How can I use LINQ to first of all take the results that start with the search term?
What I've Got So Far
var startsWithList = list.Where(o => o.Name.StartsWith(term)).Take(10).ToList();
var containsList = list.Where(o => o.Name.StartsWith(term) && !startsWithList.Contains(o)).Take(10 - startsWithList.Count).ToList();
return startsWithList.AddRange(containsList);
I don't like the above code at all. I feel like this should be achieved in one Where as opposed to performing two Where and Take's and combining the two lists.
just order ascending before Take, putting a lower value for items starting with term.
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.StartsWith(term) ? 0 : 1)
//or OrderByDescending(m => m.Name.StartsWith(term))
.Take(10)
.ToList();
adapted with the improvement of MikeT (exact match before StartsWith), you could just do
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.StartsWith(term)
? (m.Name == term ? 0 : 1)
: 2)
.Take(10)
.ToList();
I have created a new github project that uses expression trees to search for text in any number of properties
It also has a RankedSearch() method which returns the matching items with the number of hits for each record meaning you can do the following:
return locations.RankedSearch(term, l => l.Name)
.OrderByDescending(x => x.Hits)
.Take(10)
.ToList();
If you wanted you could search accross multiple properties
return locations.RankedSearch(term, l => l.Name, l => l.City)
... or for multiple terms,
return locations.RankedSearch(l => l.Name, term, "AnotherTerm" )
... or for both multiple properties and multiple terms
return locations.RankedSearch(new[]{term, "AnotherTerm"},
l => l.Name,
l => l.City)
Checkout this post for more information on the SQL generated and other usages:
http://jnye.co/Posts/27/searchextensions-multiple-property-search-support-with-ranking-in-c-sharp
You can download this as a nuget package to:
https://www.nuget.org/packages/NinjaNye.SearchExtensions/
Raphaƫl's Solution will work but if you were say searching for Warwick you could find that it might not put Warwick the top of the list if Warwickshire is also a possible location,using the scores you can also extend this infinitely with more matching methods, as well as tweaking the score values to refine your search order
return locations.Select(l => New {SearchResult=l,
Score=(L.Name == Term ?
100 :
l.Name.StartsWith(term) ?
10 :
l.Name.Contains(term) ?
1 :
0
)})
.OrderByDescending(r=>r.Score)
.Take(10)
.Select(r => r.SearchResult);
note i would probably do this by making a Match method and do the logic in there rather than in the linq like i did above so it would just be
return locations.OrderByDescending(Match).Take(10);
All Solutions will work but the better score can gain more easier as below
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.IndexOf(term))
.Take(10)
.ToList();
as a result each name that contain term at nearest to start, show first.
What about this?
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Length)
.Take(10)
.ToList();
I have a method that returns data from an EF model.
I'm getting the above message, but I can't wotk our how to circumvent the problem.
public static IEnumerable<FundedCount> GetFundedCount()
{
var today = DateTime.Now;
var daysInMonth = DateTime.DaysInMonth(today.Year, today.Month);
var day1 = DateTime.Now.AddDays(-1);
var day31 = DateTime.Now.AddDays(-31);
using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
{
var r = new Repository<MatchHistory>(uow.Context);
return r.Find()
.Where(x =>
x.AppliedOn >= day1 && x.AppliedOn <= day31 &&
x.ResultTypeId == (int)MatchResultType.Accepted)
.GroupBy(x => new { x.BuyerId, x.AppliedOn })
.Select(x => new FundedCount(
x.Key.BuyerId,
x.Count() / 30 * daysInMonth))
.ToList();
}
}
FundedCount is not an EF enity, MatchHistory is, so can't understand why it is complaining.
All advice appreciated.
The reason it is complaining is because it doesn't know how to translate your Select() into a SQL expression. If you need to do a data transformation to a POCO that is not an entity, you should first get the relevant data from EF and then transform it to the POCO.
In your case it should be as simple as calling ToList() earlier:
return r.Find()
.Where(x => x.AppliedOn >= day1 && x.AppliedOn <= day31 &&
x.ResultTypeId == (int)MatchResultType.Accepted)
.GroupBy(x => new { x.BuyerId, x.AppliedOn })
.ToList() // this causes the query to execute
.Select(x => new FundedCount(x.Key.BuyerId, x.Count() / 30 * daysInMonth));
Be careful with this, though, and make sure that you're limiting the size of the data set returned by ToList() as much as possible so that you're not trying to load an entire table into memory.
Message is clear : linq to entities doesn't support objects without a parameterless ctor.
So
Solution1
enumerate before (or use an intermediate anonymous type and enumerate on that one)
.ToList()
.Select(x => new FundedCount(
x.Key.BuyerId,
x.Count() / 30 * daysInMonth))
.ToList();
Solution2
add a parameterless ctor to your FundedCount class (if it's possible)
public FundedCount() {}
and use
.Select(x => new FundedCount{
<Property1> = x.Key.BuyerId,
<Property2> = x.Count() / 30 * daysInMonth
})
.ToList();
It's complaining because it can't convert references to FundedCount to SQL statements.
All LINQ providers convert LINQ statements and expressions to operations that their target can understand. LINQ to SQL and LINQ to EF will convert LINQ to SQL, PLINQ will convert it to Tasks and parallel operations, LINQ to Sharepoint will convert it to CAML etc.
What happens if they can't do the conversion, depends on the provider. Some providers will return intermediate results and convert the rest of the query to a LINQ to Objects query. Others will simply fail with an error message.
Failing with a message is actually a better option when talking to a database. Otherwise the server would have to return all columns to the client when only 1 or 2 would be actually necessary.
In your case you should modify your select to return an anonymous type with the data you want, call ToList() and THEN create the FundedCount objects, eg:
.Select( x=> new {Id=x.Key.BuyerId,Count=x.Count()/30 * daysInMonth)
.ToList()
.Select(y => new FundedCount(y.Id,y.Count))
.ToList();
The first ToList() will force the generation of the SQL statement and execute the query that will return only the data you need. The rest of the query is actually Linq to Objects and will get the data and create the final objects
I had the same exception in GroupBy. I found that the exception "Only parameterless constructors and initializers are supported in LINQ to Entities" is not 100% accurate description.
I had a GroupBy() in my "Linq to EntityFramework query" which used a struct as a Key in GroupBy. That did not work. When I changed that struct to normal class everything worked fine.
Code sample
var affectedRegistrationsGrouped = await db.Registrations
.Include(r => r.Person)
.Where(r =>
//whatever
)
.GroupBy(r => new GroupByKey
{
EventId = r.EventId,
SportId = r.SportId.Value
})
.ToListAsync();
...
...
// this does not work
private struct GroupByKey() {...}
// this works fine
private class GroupByKey() {...}