I have a IList<User> that contains objects with a pair of value: Name and Surname.
On the database I have a table that contains rows with Name and Surname field. I want on codebehind to return the list of the rows that match my List, so let say have Name and Surname (respectively) equals.
My actual code is:
utenti = (from User utente in db.User.AsEnumerable()
join amico in amiciParsed
on new { utente.Nome, utente.Cognome } equals
new { Nome = amico.first_name, Cognome = amico.last_name }
select utente).OrderBy(p => p.Nome)
.OrderBy(p => p.Cognome)
.OrderBy(p => p.Nickname)
.ToList();
but this it is not good for two reasons:
It will download the whole records of the DB on the client;
I can't match Name and Surname as case sensitive (example Marco cordi != Marco Cordi); and on DB I have every kind of up/down chars.
As suggested on a previously question, seems that this answer can't help me, since I have to do a join (and also because the first problem it is not related).
What's the way to resolve this problem?
I don't know if this will work in your situation, but you might give it a try.
First, create a new list of strings:
List<string> amici = aimiciParsed.Select(x => x.first_name + "|" + x.last_name).ToList();
Then, select the users from DB, based on this list
var utenti = db.User.AsEnumerable().Where(utente =>
amici.Contains(utente.Nome + "|" + utente.Cognome)).ToList();
It sends the list of strings to the DB as a list of parameters and translates it into a query like
SELECT * FROM User WHERE User.Nome + "|" + User.Cognome IN (#p1, #p2, #p3 ...)
Unfortunately, there is no way to call Contains with something like StringComparison.OrdinalIgnoreCase, so you might have to change the collation of your columns.
This could be done with PredicateBuilder:
using LinqKit;
var predicate = PredicateBuilder.False<User>();
foreach(var amico in amiciParsed)
{
var a1 = amico; // Prevent modified closure (pre .Net 4.5)
predicate = predicate.Or(user => user.Nome == a1.first_name
&& user.Cognome == a1.last_name);
}
var query = db.User.Where(predicate.Expand())
.OrderBy(p => p.Nome)
...
The advantage is that indexes on Nome and Cognome can be used (which is impossible if you search on a concatenated value). On the other hand, the number of OR clauses can get very large, which may hit certain limits in SQL Server (https://stackoverflow.com/a/1869810/861716). You'll have to stress-test this (although the same goes for IN clauses).
When asking a question here on SO, you may want to translate it to English - don't expect people to know what "uente", "amico" or "Cognome" are.
One question: Why do you use ..in db.User.AsEnumerable() and not just ..in db.User?
Let everything in your query stay IQueryable (instead of IEnumerable). This lets Linq2Sql create SQLs that are as optimized as possible, instead of downloading all the records and joining the records client-side. This may also be the reason your search turns case-sensitive. Client-side in-memory string comparison will always be case-sensitive, while string comparison in SQL depends on the database's configuration.
Try ditching the .AsEnumerable() and see if you get better results:
utenti = (from User foo in db.User
join bar in amiciParsed
...
Related
I have a linq query which seems to be reversing one column of several in some rows of an earlier query:
var dataSet = from fb in ds.Feedback_Answers
where fb.Feedback_Questions.Feedback_Questionnaires.QuestionnaireID == criteriaType
&& fb.UpdatedDate >= dateFeedbackFrom && fb.UpdatedDate <= dateFeedbackTo
select new
{
fb.Feedback_Questions.Feedback_Questionnaires.QuestionnaireID,
fb.QuestionID,
fb.Feedback_Questions.Text,
fb.Answer,
fb.UpdatedBy
};
Gets the first dataset and is confirmed working.
This is then grouped like this:
var groupedSet = from row in dataSet
group row by row.UpdatedBy
into grp
select new
{
Survey = grp.Key,
QuestionID = grp.Select(i => i.QuestionID),
Question = grp.Select(q => q.Text),
Answer = grp.Select(a => a.Answer)
};
While grouping, the resulting returnset (of type: string, list int, list string, list int) sometimes, but not always, turns the question order back to front, without inverting answer or questionID, which throws it off.
i.e. if the set is questionID 1,2,3 and question A,B,C it sometimes returns 1,2,3 and C,B,A
Can anyone advise why it may be doing this? Why only on the one column? Thanks!
edit: Got it thanks all! In case it helps anyone in future, here is the solution used:
var groupedSet = from row in dataSet
group row by row.UpdatedBy
into grp
select new
{
Survey = grp.Key,
QuestionID = grp.OrderBy(x=>x.QuestionID).Select(i => i.QuestionID),
Question = grp.OrderBy(x=>x.QuestionID).Select(q => q.Text),
Answer = grp.OrderBy(x=>x.QuestionID).Select(a => a.Answer)
};
Reversal of a grouped order is a coincidence: IQueryable<T>'s GroupBy returns groups in no particular order. Unlike in-memory GroupBy, which specifies the order of its groups, queries performed in RDBMS depend on implementation:
The query behavior that occurs as a result of executing an expression tree that represents calling GroupBy<TSource,TKey,TElement>(IQueryable<TSource>, Expression<Func<TSource,TKey>>, Expression<Func<TSource,TElement>>) depends on the implementation of the type of the source parameter.`
If you would like to have your rows in a specific order, you need to add OrderBy to your query to force it.
How I do it and maintain the relative list order, rather than apply an order to the resulting set?
One approach is to apply grouping to your data after bringing it into memory. Apply ToList() to dataSet at the end to bring data into memory. After that, the order of subsequent GrouBy query will be consistent with dataSet. A drawback is that the grouping is no longer done in RDBMS.
Suppose I have a list of {City, State}. It originally came from the database, and I have LocationID, but by now I loaded it into memory. Suppose I also have a table of fast food restaurants that has City and State as part of the record. I need to get a list of establishments that match city and state.
NOTE: I try to describe a simplified scenario; my business domain is completely different.
I came up with the following LINQ solution:
var establishments = from r in restaurants
from l in locations
where l.LocationId == id &&
l.City == r.City &&
l.State == r.State
select r
and I feel there must be something better. For starters, I already have City/State in memory - so to go back to the database only to have a join seems very inefficient. I am looking for some way to say {r.City, r.State} match Any(MyList) where MyList is my collection of City/State.
UPDATE
I tried to update based on suggestion below:
List<CityState> myCityStates = ...;
var establishments =
from r in restaurants
join l in myCityStates
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
and I got the following compile error:
Error CS1941 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
UPDATE 2
Compiler didn't like anonymous class in the join. I made it explicit and it stopped complaining. I'll see if it actually works in the morning...
It seems to me that you need this:
var establishments =
from r in restaurants
join l in locations.Where(x => x.LocationId == id)
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
Well, there isn't a lot more that you can do, as long as you rely on a table lookup, the only thing you can do to speed up things is to put an index on City and State.
The linq statement has to translate into a valid SQL Statement, where "Any" would translate to something like :
SELECT * FROM Restaurants where City in ('...all cities')
I dont know if other ORM's give better performance for these types of scenarios that EF, but it might be worth investigating. EF has never had a rumor for being fast on reads.
Edit: You can also do this:
List<string> names = new List { "John", "Max", "Pete" };
bool has = customers.Any(cus => names.Contains(cus.FirstName));
this will produce the necessary IN('value1', 'value2' ...) functionality that you were looking for
Because of a poor design on our database I have to write a complex query to get my data.
I need to get all valid data from a table_1. In other works I need to get each valid row of my table_1. I don't have a simple valid or invalid column in my table_1. This information is stored in a table_2. Table_2 contains all invalid row with the error message.
Let say the data I need to retrieve are orders. For my example notice that OrderNo is the number of an order. So I can have multiple line, version, of this order in table_1 and I can also have multiple line of error on this order in table_2. So I will also have to use a version number.
I already tried this:
table_1.Where(y => (y.OrderNo == "1234"));
table_2.Where(y => (y.OrderNo == "1234")).Select(y => y.Version).Distinct();
And I think I need to do something like this:
var errorList = table_2.Where(y => (y.OrderNo == "1234")).Select(y => y.Version).Distinct();
table_1.Where(y => (y.OrderNo == "1234" && y.Version.NOT_IN(erriList)));
Could you help me?
I suppose you are searching for Contains function with ! symbol (logical negation operator). Like this:
var errorList = table_2.Where(y => y.OrderNo == "1234")
.Select(y => y.Version);
var res = table_1.Where(y => y.OrderNo == "1234"
//here you get only rows that doesn't represent in errorList
&& !errorList.Contains(y.Version));
to get data from a table but not if in another table
This is called antijoin. While you can use Contains and Any based approaches presented in the other answers, usually you'll get the best performance by using the classic SQL approach - LEFT OUTER JOIN combined with checking the right side for NULL.
Which in LINQ looks like this:
var query =
from t1 in table_1
//where t1.OrderNo == "1234"
join t2 in table_2 on t1.OrderNo equals t2.OrderNo into t2group
from t2 in t2group.DefaultIfEmpty()
where t2 == null
select t1;
Actually when you use OrderNo filter, most probably there will not be a noticeable speed difference between this and other queries. The main benefit of the above would be if you remove that filter, although many nowadays SQL query optimizers are able to derive one and the same execution plan regardless of whether the query uses JOIN / IN / EXISTS constructs.
How about this:
var validRows = table1
.Where(t1 => !table2
.Any(t2 => t1.OrderNo == t2.OrderNo &&
t1.Version == t2.Version));
Note that this is far more efficient in SQL unless you're using something fancy that translates the expression to SQL.
I have a table with data about cities:
In a search input field the user types an arbitrary number of chars then presses "search" button.
There is also another field where the user can enter the state via a dropdownlist.
Pressing the search button triggers an ajax call to a server controller which receives both the starting chars of the name and the two chars of the state.
The controller should return the list of cities with name starting with the supplied chars.
If the state is passed, the list should contain only those cities whose name starts with the supplied chars AND that are situated in the state.
If the state is not passed all matching cities are returned regardless of the state.
I cannot find a way to write a single Linq statement, because I do not know how to write the part of the query for the state:
Currently I do this:
public ActionResult selectCity(string searchString, string stateCode)
{
List<CityViewModel> mylist = new List<CityViewModel>();
IQueryable<City> mycities = null;
if (stateCode == "")
{
mylist = (from c in db.Cities
where c.name.StartsWith(searchString)
select c);
}
else
{
mylist = (from c in db.Cities
where ((c.name.StartsWith(searchString)) &&
(c.stateCode == stateCode))
select c);
}
<snip>
.....
.....
</snip>
return PartialView("_selComune",elenco);
}
I can't believe that there isn't a way to do this with a single query.
Yes, there is.
You want to say: if stateCode is empty or it matches City.stateCode then include it in result moreover City.Name must always begin with searchString. In code:
var mylist =
from c in db.Cities
where
c.name.StartsWith(searchString)
&& (stateCode == "" || c.stateCode == stateCode)
select c;
Roughly equivalent to this SQL (assuming parameters with proper syntax are provided):
SELECT * FROM Cities
WHERE name LIKE #searchString AND (#stateCode = '' OR stateCode = #stateCode)
SQL Server will optimize second comparison away if first condition is always satisfied (just check Query Execution Plan).
I think it's a typo writing code example but myList isn't List<City> unless you also add ToList() to your LINQ query.
Adriano Repetti's answer allows you to write a single query to handle "nullable" parameters but sometimes it has performance drawbacks, translated in SQL the kind of query may prevent indexes to work.
Please note that the following will also work and build the exact needed query on sql side, with only a bit of more code on LINQ side :
mylist = (from c in db.Cities
where c.name.StartsWith(searchString)
select c);
if (stateCode != "")
{
mylist = mylist.Where(c.stateCode == stateCode);
}
This kind of construct won't replace previous "where" content, it will add any new terms combining them with a "and" operator.
IQeryable is made to allow you to built it in many different lines, not a single one like you did in your code sample.
In SQL one might sometimes write something like
DELETE FROM table WHERE column IS NULL
or
UPDATE table SET column1=value WHERE column2 IS NULL
or any other criterion that might apply to multiple rows.
As far as I can tell, the best EntityFramework can do is something like
foreach (var entity in db.Table.Where(row => row.Column == null))
db.Table.Remove(entity); // or entity.Column2 = value;
db.SaveChanges();
But of course that will retrieve all the entities, and then run a separate DELETE query for each. Surely that must be much slower if there are many entities that satisfy the criterion.
So, cut a long story short, is there any support in EntityFramework for updating or deleting multiple entities in a single query?
EF doesn't have support for batch updates or deletes but you can simply do:
db.Database.ExecuteSqlCommand("DELETE FROM ...", someParameter);
Edit:
People who really want to stick with LINQ queries sometimes use workaround where they first create select SQL query from LINQ query:
string query = db.Table.Where(row => row.Column == null).ToString();
and after that find the first occurrence of FROM and replace the beginning of the query with DELETE and execute result with ExecuteSqlCommand. The problem with this approach is that it works only in basic scenarios. It will not work with entity splitting or some inheritance mapping where you need to delete two or more records per entity.
Take a look to Entity Framework Extensions (Multiple entity updates). This project allow set operations using lambda expressions. Samples from doc:
this.Container.Devices.Delete(o => o.Id == 1);
this.Container.Devices.Update(
o => new Device() {
LastOrderRequest = DateTime.Now,
Description = o.Description + "teste"
},
o => o.Id == 1);
Digging EFE project source code you can see how automatize #Ladislav Mrnka second approach also adding setting operations:
public override string GetDmlCommand()
{
//Recover Table Name
StringBuilder updateCommand = new StringBuilder();
updateCommand.Append("UPDATE ");
updateCommand.Append(MetadataAccessor.GetTableNameByEdmType(
typeof(T).Name));
updateCommand.Append(" ");
updateCommand.Append(setParser.ParseExpression());
updateCommand.Append(whereParser.ParseExpression());
return updateCommand.ToString();
}
Edited 3 years latter
Take a look to this great answer: https://stackoverflow.com/a/12751429
Entity Framework Extended Library helps to do this.
Delete
//delete all users where FirstName matches
context.Users.Delete(u => u.FirstName == "firstname");
Update
//update all tasks with status of 1 to status of 2
context.Tasks.Update(
t => t.StatusId == 1,
t2 => new Task {StatusId = 2});
//example of using an IQueryable as the filter for the update
var users = context.Users.Where(u => u.FirstName == "firstname");
context.Users.Update(users, u => new User {FirstName = "newfirstname"});
https://github.com/loresoft/EntityFramework.Extended