Linq query run over list of parameters - c#

I have a requirement to create a linq query that selects data from a database table where a certain functional result is true.
I am using linq-to-sql and the function works well and I can write a basic query to get the data.
My issue is that I have an in-memory list of parameters and I essentially need to run the linq query multiple times (once for every parameter list item) and aggregate the results.
I have tried using the .Any() as a join but linq doesn't like joining non-database result sets with database result sets.
Some Sample Code:
Parameter list: // lets call it "l"
{
One,
Two,
Three
}
Query
From w in words where funcWord(l.item) == true select w;
So I would require a query that can run the above query once for every item in l and aggregate the results.
Any help is appreciated.
Thanks.

Multiple from statements will go through every combination, sort of like nested foreach statements:
from item in list
from w in words
where funcWord(item, w.name) == true
select w;
Interestingly, this gets translated to a SelectMany statement that is similar to Arithmomaniac's answer.

Try SelectMany, which aggregates the result of a one-to-many function, applied to each member.
In this case, the members are the elements of list, and the one-to-many function is your above, l-dependent query (though I rewrote it as a lambda function.)
list.SelectMany(l => words.Where(w => funcWord(l.item, w.name)));

Related

How to filter and order collection in C#?

Here is the code:
public ActionResult Index()
{
var crView = db.CRCases.ToList();
return View(crView);
}
Would like to filter like we have in sql statement using WHERE and ORDER BY.
Is there any good reference on how to apply it?
linq is your friend here.
you can do
crView = crView.Where(x => x.yourPredicate).OrderBy( y => y.OrderClause);
CRCases should be a DBSet<T>. You can use LinQ like this :
db.CRCases.Where(x => x.TheProperty =xxx).ToList()
You can use LINQ = Language Integrated Query.
This have two different notations:
Functional: you can change several function calls beginning on an IEnumerable.
db.CrCases.Where(predicate).OrderBy(sortExpression)
"SQL like": it really looks like an "unordered SQL".
from crCase in db.CrCases where condition orderby sortExpression select crCase
The condition and sort expression in the first notation require lambda expressions (you can see them in action here). In the second case the condition is expressed.
Most of the queries can be written either way.
You'll usually use var to store the query, and materialize it later.
var query = from crCase in db.CrCases
where condition orderby sortExpression
select crCase
CrCase firstCase = query.Firts();
List<CrCase> cases = query.ToList();
There are several flavors of LINQ. They all look equal on the surface, but are translated into another thing. For example:
LINQ to objects: it's the more general, an allows to make queries on collections. Tis is what you'll use for this particular case.
LINQ to SQL: this allows to write queries in C# which are transalated to SQL queries in the underlying database
LINQ to Entities: similar to the previous, but you make your queries to an Entity Framework model, and the queries are finally transalated to the DB
There are some others flavors of LINQ. And, in fact, you can create your own, but that's not easy.
Finally, you have to take into account that a LINQ query isn't executed until you materialize it. I.e. it's not executed until you try to get data from it (converting it ToList(), ToArray() or whatever, enumerating it, or accesing any of it's data First(), Sum(), Count()). Depending on the flavor, the executing can be running C# code (LINQ to objects) or run a SQL Query in a DB (LINQ to SQL) or whatever.
Here you have a great place to learn how to use LINQ:
LINQ 101

Are different IQueryable objects combined?

I have a little program that needs to do some calculation on a data range. The range maybe contain about half a millon of records. I just looked to my db and saw that a group by was executed.
I thought that the result was executed on the first line, and later I just worked with data in RAM. But now I think that the query builder combine the expression.
var Test = db.Test.Where(x => x > Date.Now.AddDays(-7));
var Test2 = (from p in Test
group p by p.CustomerId into g
select new { UniqueCount = g.Count() } );
In my real world app I got more subqueries that is based on the range selected by the first query. I think I just added a big overhead to let the DB make different selects.
Now I bascilly just call .ToList() after the first expression.
So my question is am I right about that the query builder combine different IQueryable when it builds the expression tree?
Yes, you are correct. LINQ expressions are lazily evaluated at the moment you evaluate them (via .ToList(), for example). At that point in time, Entity Framework will look at the total query and build an SQL statement to represent it.
In this particular case, it's probably wiser to not evaluate the first query, because the SQL database is optimized for performing set-based operations like grouping and counting. Rather than forcing the database to send all the Test objects across the wire, deserializing the results into in-memory objects, and then performing the grouping and counting locally, you will likely see better performance by having the SQL database just return the resulting Counts.

Split string with LINQ

I want to order by my results with the matches count in my string line.
So here is code
.ThenByDescending(p => p.Title.ToLower()
.Split(' ')
.Count(w => words.Any(w.Contains)));
But it bring me error and says that LINQ can't parse Split into SQL.
LINQ to Entities does not recognize the method 'System.String[]
Split(Char[])' method, and this method cannot be translated into a
store expression.
How can I implement Split via LINQ?
For example, for this array it must order in this way
words = { "a", "ab" }
ab a ggaaag gh //3 matches
ba ab ggt //2 matches
dd //0 matches
it means that Linq to entities failed to find translation of split method that can be written as sql query. if you want to perform split functions you have to bring the record in memory by calling ToList(), AsEnumerable() etc.
var result = (from t in db.Table
select t).AsEnumerable().OrderBy(x=>x.Column).ThenByDescending(p=>p.Title.ToLower.Split(' ')....);
You will need to perform the sorting in LINQ to Objects because LINQ to Entities cannot translate the C# code into SQL (or the language of whatever DB you're using).
You can do it like this.
var results =
objectContext
.Where(a => a == b) //Whatever
.AsEnumerable()
.ThenByDescending(p=>p.Title.ToLower().Split(' ').Count(w=>words.Any(w.Contains)));
AsEnumerable() (along with ToArray() or ToList()) turn the LINQ to Entities back into LINQ to Objects.
One can't expect LINQ to Entities to be able to convert that to SQL.
The best solution is to change the schema such that each word in a post's title is stored as a separate row in a separate table (with the appropriate associations). The query can then use explicit (group) join operations or the FK association property.
If you can't do this and yet want the query to run on the database, you'll have to look into writing a user-defined function to work with delimited strings. Read this question for more info. This will be a lot of work though.
The easiest solution (if you can afford to do it) of course is to pull everything back to the client just use LINQ to Objects for that part of the query, as mentioned by #Muhammad Adeel Zahid.

Linq to SQL problem

I have a local collection of record Id's (integers).
I need to retrieve records that have every one of their child records' ids in that local collection.
Here is my query:
public List<int> OwnerIds { get; private set; }
...
filteredPatches = from p in filteredPatches
where OwnerIds.All(o => p.PatchesOwners.Select(x => x.OwnerId).Contains(o))
select p;
I am getting this error:
Local sequence cannot be used in Linq to SQL implementation of query operators except the Contains() operator.
I understand that .All() isn't supported by Linq to SQL, but is there a way to do what I am trying to do?
Customers where
OrderIds in the child collection are a subset of the IDs in the in-memory collection.
from c in myDC.Customer
where c.Orders.All(o => myList.Contains(o.ID))
select c;
Customers where
OrderIds in the in-memory collection are a subset of the IDs in the child collection.
from c in myDC.Customers
where (from o in c.Orders
where myList.Contains(o.ID)
group o.ID by o.ID).Distinct().Count() == myList.Count()
select c;
Customers where
OrderIds in the in-memory collection are set-equal to the IDs in the child collection.
from c in myDC.Customers
let Ids = c.Orders.Select(o => o.ID).Distinct()
where Ids.Count() == myList.Count()
&& Ids.All(id => myList.Contains(id))
select c;
All of these generated sql for me.
PS - these presume the IDs are already distinct in myList. If they aren't yet, use:
myList = myList.Distinct().ToList();
PSS - good for lists up to ~2000 items. Higher than that will get translated to sql, and then sql server will barf at the number of parameters.
I don't know of a way to do it with Linq to SQL. The problem is that you need to get your list over to the server so that it can query against it. (your list is in memory on your machine, SQL Server needs to do the filtering on the server)
With straight SQL, you could use a regular SELECT statement with the "in()" operator to do that. (don't go over 1,000 items in the "in")
You could insert all of the ID's into a temp table in SQL, and then join to the table (you could use LINQ with this solution, but it requires 2 steps - the insert (assuming you have a "sets" table), and then the joined query (and then a cleanup query to remove your set).
You could LINQ query without the filter condition and then filter on your in-memory set (not recommended if the unfiltered result set could be large).
what the compiler says is...
OwnerIds.Contains(someVariable)
is supported and it will be translated as:
WHERE someVariable IN (OwnerId1, OwnerId2, OwnerIdN)
now, we don't have all the informations of you query but if you can reformulate what you're trying to do to use Contains, you'll be ok.
Could you do a join OwnerIds?
Error said "Local sequence (means OwnerIds) cannot be used in Linq to SQL implementation of query operators except the Contains() operator."
So you can do:
1) load ALL filteredPatches rows from SQL
var loadedData = filteredPatches.Select(i => i).ToList();
2) filter data as simple local sequence
var result = loadedData.Where(i => i.PatchesOwners.All(o => OwnerIds.Contains(o.ID)));

Entity Framework SQL Query Execution

Using the Entity Framework, when one executes a query on lets say 2000 records requiring a groupby and some other calculations, does the query get executed on the server and only the results sent over to the client or is it all sent over to the client and then executed?
This using SQL Server.
I'm looking into this, as I'm going to be starting a project where there will be loads of queries required on a huge database and want to know if this will produce a significant load on the network, if using the Entity Framework.
I would think all database querying is done on the server side (where the database is!) and the results are passed over. However, in Linq you have what's known as Delayed Execution (lazily loaded) so your information isn't actually retrieved until you try to access it e.g. calling ToList() or accessing a property (related table).
You have the option to use the LoadWith to do eager loading if you require it.
So in terms of performance if you only really want to make 1 trip to the Database for your query (which has related tables) I would advise using the LoadWith options. However, it does really depend on the particular situation.
It's always executed on SQL Server. This also means sometimes you have to change this:
from q in ctx.Bar
where q.Id == new Guid(someString)
select q
to
Guid g = new Guid(someString);
from q in ctx.Bar
where q.Id == g
select q
This is because the constructor call cannot be translated to SQL.
Sql's groupby and linq's groupby return differently shaped results.
Sql's groupby returns keys and aggregates (no group members)
Linq's groupby returns keys and group members.
If you use those group members, they must be (re-)fetched by the grouping key. This can result in +1 database roundtrip per group.
well, i had the same question some time ago.
basically: your linq-statement is converted to a sql-statement. however: some groups will get translated, others not - depending on how you write your statement.
so yes - both is possible
example:
var a = (from entity in myTable where entity.Property == 1 select entity).ToList();
versus
var a = (from entity in myTable.ToList() where entity.Property == 1 select entity).ToList();

Categories