I have a database which I access using entity framework. Here's a snippet.
As you can see a Tier can have multiple MatchNodes and each MatchNode can have multiple LenderMatchNode.
I have an integer variable fundedCount.
What I'm trying to do is filter a list of Tier/MatchNode/LenderMatchNode where fundedCount is between LenderMatchNode.MinFunded and LenderMatchNodeMaxFunded.
Essentially this should return a list of Tier which in turn should have a list of MatchNode which should have 1 LenderMatchNode.
Anyone help?
var result = Tiers.MatchNodes
.LenderMatchNodes
.FirstOrDefault( lmn => lmn.MinFunded <= fundedCount && lmnMaxFunded >= fundedCount )
.Where(rs => rs != null);
The result will be a hard list of LenderMatches which fall into the where criteria. From their you can group out what is needed such as the MatchNodes or tiers.
Related
I have two issues I'm struggling with LINQ. I appreciate if you could advise. I have 2 lists rawStates (storing rows of entity-downtime-uptime-eventtype) and rawData list storing products' in and out times from entity.
I want to select those elements from rawStates that occurred when still waiting for that entity to be processed
foreach(var t in rawData)
var s = rawStates
//I am not sure if this single logic clause in Where is enough;
.Where(o => o.Entity == t.Entity
&& o.DownDate > t.InTime
&& o.Update < t.OutTime)
.ToList();
If I group my rawData by productID (there are multiple rows with same ProductID), how can I revert back this "s" to these groups so that for a productID I can group by eventtype, and summarise durations by productID?
I'm trying to filter a list within a list from an entity framework entity.
I've managed to get the code working however, i'm not convinced it's the cleanest way of achieving the goal.
Here's the code I have so far:
foreach (var n1 in tier.MatchNodes)
{
n1.LenderMatchNodes = n1.LenderMatchNodes.Where(x => x.Commission == 0).ToList();
}
Effectively MatchNodes contains a collection of LenderMatchNodes, however I want to return only the nodes where the commission == 0.
Thanks in advance.
Try
tier.MatchNodes.ToList().ForEach(n1=>n1.LenderMatchNodes = n1.LenderMatchNodes.Where(x => x.Commission == 0).ToList());
Try using SelectMany():
var result = dataContext.Table<Tier>()
.Where(some condition to get you the tier)
.SelectMany(tier => tier.MatchNodes)
.SelectMany(node => node.LenderMatchNodes)
.Where(x => x.Commission == 0)
.ToList();
This has the additional benefit of being able to execute it a single SQL query.
If you're goal is to actually update the node list in the database, you can still minimize the number of queries using Include() (assuming you're using EF):
var nodes = dataContext.Table<Tier>()
.Where(some condition to get you the tier)
.SelectMany(tier => tier.MatchNodes)
.Include(node => node.LenderMatchNodes) // loads this eagerly
.ToList();
nodes.ForEach(n => n.LenderMatchNodes = n.LenderMatchNodes.Where(condition));
This seems to be about the most generic error I've come across - multiple SO posts about it are all referring to different issues - well here's a new one :)
I get the error above when the following IQueryable is enumerated:
N.B. items is an IQueryable<tblItem> and keywords is a string
items = items.Where(p => p.heading.ToLower().Contains(keywords) ||
p.description.ToLower().Contains(keywords));
This is confusing because, as the error suggests, it should work fine when you use a Contains - does anyone know how to fix this?
If keyword is a collection that supports enumeration, then it should be other way around:
items = items.Where(p => keywords.Contains(p.heading.ToLower()) ||
keywords.Contains(p.description.ToLower()));
If items is IQueryable then the error may be there and nothing to do with your where statement.
Can you try forcing enumeration before adding your where statement?
For example, suppose you are attempting to join an in memory list with a datatable you will get that error when the query is evaluated or enumerated
List<tblCategory> categories = tblCategory.ToList();
IQueryable<tblItem> items = (from r in tblItem
join c in categories on r.CategoryID equals c.Id select r);
// items = items.Where(p => p.heading.ToLower().Contains(keywords) ||
// p.description.ToLower().Contains(keywords));
var firstMatch = items.FirstOrDefault();
// The error will be generated here even if the where is remmed out
SOLVED
Thanks sgmoore for your input - it helped arrive at this solution:
Assgning the IQueryable list to an IEnumerable list, running a ToList on it and THEN using my filters on the list worked great.
IEnumerable<tblItems> temp = items.ToList();
temp = temp.Where(p => p.heading.ToLower().Contains(keywords) ||
p.description.ToLower().Contains(keywords));
items = temp.AsQueryable();
I'm using Entity Framework and have started writing queries using linq.
I have a Store table where each store has a name field. One of the searches I want to do is by initial letter and I'm trying to find the best way of achieving it. By best I mean most efficient.
Most searches only look for one key, 'A', 'B', 'C' etc but one of the searches is different in that the group '0-9' will contain a list of keys, one for 0, one for 1 etc. So my starting point is a list of some kind.
I then need to say if a Store name starts with any key in the list, because a Store does not store the initial letter in the table.
There are 2 things I'm looking for help with. Firstly how to get the linq working as I've outlined. Secondly, any advice as to whether there this is the best/only approach to bringing back the data or whether there may be a better way.
Your question is not very clear, but from what I understand you want to search for stores which begin with a key found in a list of keys. You can achieve that like this:
List<string> keys = new List<string>() { "A", "B", "M" };
var result = stores.Where(store => keys.Any(key => store.Name.StartsWith(key));
If the queries can differ, then for the letters:
stores.Where(s=>s.Name.First == c);
And for numerals:
stores.Where(char.IsDigit);
If they must be the same, then I suggest a character range:
stores.Where(s=> c1 <= s.Name.First && s.Name.First <= c2)
You can represent the ranges by Tuples if you want:
Tuple<char, char> range = Tuple.Create('A', 'A');
//Tuple<char, char> range = Tuple.Create('0', '9');
stores.Where(s=> range.Item1 <= s.Name.First && s.Name.First <= range.Item2)
EDIT: Using the function in the Entities Framework
Tuple<char, char> range = Tuple.Create('A', 'A');
//Tuple<char, char> range = Tuple.Create('0', '9');
stores.Where(s=> range.Item1 <= s.Name.Substring(0, 1) && s.Name.Substring(0, 1) <= range.Item2)
I asked a more specific question on another thread.
Here is the answer:
Linq to entities - first letter of string between 2 keys
I am trying to compare two tables (i.e values, count, etc..) in linq to sql but I am not getting the way to achieve it. I tried the following,
Table1.Any(i => i.itemNo == Table2.itemNo)
It gives error. Could you please help me?
Thanks in Advance.
how about
var isDifferent =
Table1.Zip(Table2, (j, k) => j.itemNo == k.itemMo).Any(m => !m);
EDIT
if Linq-To-Sql does not support Zip.
var one = Table1.ToList();
var two = Table2.ToList();
var isDifferent =
one.Zip(two, (j, k) => j.itemNo == k.itemMo).Any(m => !m);
if the tables are vary large this could cause performance problems. In that case you will need a much more sophisticated solution, if so, please ask.
EDIT2
If the tables are very large you don't want to get all the data from the server and hold it memory. Additionaly, Linq and SQL server do not garauntee the order of the rows unless you specify an order in the query. This becomes espcially relavent for large result sets returned by a multi processor server where the effects of parallelism are likely to come into play.
I suggest that Linq-to-Sql doesen't really cater well for your scenario so you will have to help it out using ExecuteQuery somthing like this.
string zipQuery =
#"SELECT TOP 1
1
FROM
[Table1] [one]
WHERE
NOT EXISTS (
SELECT * FROM [Table2] [two] WHERE [two].[itemNo] = [one].[itemNo]
)
UNION ALL
SELECT
1
FROM
[Table2] [two]
WHERE
NOT EXISTS (
SELECT * FROM [Table1] [one] WHERE [one].[itemNo] = [two].[itemNo]
)
UNION ALL
SELECT 0";
var isDifferent = context.ExecuteQuery<int>(zipQuery).Single() == 1;
This will do the select on the server without returning lots of data to the client but, I think you will agree is much more complicated.
EDIT3
Okay, the zip approach should be fine for 1000 rows. I've read your comment and I suggest changing the code accordingly.
var one = Table1.ToList();
var two = Table2.ToList();
var isDifferent =
one.Count != two.Count ||
one.Zip(two, (o, t) => o.itemNo == k.itemNo).Any(m => !m);
You should probably consider putting an order by on the list retrievers, like this.
var one = Table1.OrderBy(o => o.itemNo).ToList();
Strictly, the results of a Linq-to-Sql come back in any order unless an order is specified.