Linq: using StringComparer with GroupBy/Distinct in query syntax - c#

I have this (XLinq) query and was wondering how to convert it to the query syntax:
var grouped = doc.Descendants()
.GroupBy(t => t.Element(ns + "GroupingAttr").Value, StringComparer.OrdinalIgnoreCase);
This is the query syntax without the StringComparer:
var grouped = from t in doc.Descendants()
group t by t.Element(ns + "GroupingAttr").Value into group
select group
My groupby is a little more complicated than this, so I prefer to use the key of the group instead of introducing a new property.
This is what I tried, but doesn't work because the let "key" is not available in the context of the select (I've uses my more complicated key definition to illustrate the fact I don't want to repeat this in the select):
var grouped = from t in doc.Descendants()
let key = ((t.Name != ns + "SomeElementName") ? t.Element(ns + "SomeAttribute") : t.Element(ns + "SomeOtherAttribute")).ElementValueOrDefault("Empty group")
group t by key.ToUpper() into g
select new { Name = key, Items = g };
In the end, case-sensitivity was not important because I could presume that all casings were the same...
Related question: LINQ Distinct operator ignore case?

I don't think you can use the comparer within the query syntax, however you could call ToUpper on your value. This will then ignore case for you. As a side note using ToUpper is more efficient than using ToLower, so ToUpper would be the way to go.
The C# team were very sparse with what they introduced into the query syntax, so for anything like this you'll have to use the extension methods syntax.

var grouped = from t in doc.Descendants()
group t by t.Element(ns + "GroupingAttr").Value into MyGroup
select MyGroup.Key

Related

Dynamic Linq 'contains' clause without using placeholder

The syntax given for contains clause is
ids = new int[] {1,2,3,4};
dataContext.Table.Where("#0.Contains(id)", ids);
But what I want is
dataContext.Table.Where("{1,2,3,4}.Contains(id)"); //getting exception here
[ERROR] Expression expected (at index 0)
I need this because the where clause my or may not use the contains method. it depends on how user acts.
so I got the answer for this after tinkering for sometime. So posting the answer here.
dataContext.Table.Where("new Int[]{1,2,3,4}.Contains(id)");
You can use whatever datatype you need. I use reflection to find datatype and use that accordingly.
try code:
int[] ids= {1,2,3,4};
dataContext.Table.Where(c=>c.ids.Contains(t.id)).ToList();
Or
var result= (from p in dataContext.Table.AsEnumerable()
join q in ids on p.id equals q
select p).Distinct() .ToList();

What is the linq equivalent of the below sql query

select Productid from categories where `categoryname` in `('abc','def','ghi')`;
I have tried this:
var res = from catg in db.Categories where catg.CategoryId.ToString().Contains(SelectedProducts) select catg;
But this doesnt seem to work...
Assuming SelectedProducts is an array of product ids (integers):
var cats = db.Categories.Where(o => SelectedProducts.Contains(o.CategoryId));
var pids = cats.Select(o => o.ProductId);
Reason: SQL IN operator is implemented oppositely in LINQ to SQL. The question highlights a common mistake in LINQ developers trying to translate from SQL, expecting an [attribute] [operator] [set] syntax.
Using an abstract set language we can highlight syntax differences
SQL uses a "Element is included in Set" syntax
LINQ uses a "Set contains Element" syntax
So any IN clause must be reverted using the Contains operator. It will translate to attribute IN (SET) anyways.
You need to use Contains on SelectedProducts
var res = from catg in db.Categories where
SelectedProducts.Contains(catg.categoryname) select catg.Productid;
Using method notation
var res = db.Categories.Where(catg => SelectedProducts
.Contains(catg.categoryname)).Select(catg.Productid);
The equivalence of a SQL IN with IEnumerable.Contains():
var res = from catg in db.Categories
where new[] {"abc","def","ghi"}.Contains(catg.categoryname)
select catg.Productid
Or lambda
db.Categories.Where(x => new[] {"abc","def","ghi"}.Contains(x.categoryname)).Select(c => c.ProductId);

LINQ grouping - get other, not grouped properties

I have little problem with my LINQ query (nHibernate)
I need to have count of objects znak with equal property Symbol
My query:
var tmp = (from znak in sesja.Query<Znak>()
group znak by znak.Symbol into r
select new { Name= r.Key.Name, SUM= r.Count() });
This query works, but I need to make object contains other properties of znak class.
In this case: select new { Name= r.Key.Name, SUM= r.Count() }); i can make new objects only from r.Key, (Symbol property). But I need other properties in my new object.
Is it possible ?
I recommend using lambda Linq syntax:
var items = sesja.Query<Znak().AsEnumerable();
var newList = items.GroupBy(x=>x.Symbol).Select(
x=> new { Name=x.Key.Name, Count = x.Count(), Items = x.ToList() });
read more about Linq syntax LINQ: Dot Notation vs Query Expression
I think that lambda syntax is more readable and looks much cleaner in code because it's more c# style not sql style.
Of course there will be no difference in IL code, always you can install tools like resharper, they can convert lambda syntax to sql-like syntax.
Try something like
var tmp = (from znak in sesja.Query<Znak>()
group znak by znak.Symbol into r
select new { Name= r.Key.Name, SUM= r.Count(), Items = r.ToList() });
Items property will contain actual objects in the group.

Mixing LINQ to SQL with properties of objects in a generic list

I am trying to accomplish something like this query:
var query = from a in DatabaseTable
where listOfObjects.Any(x => x.Id == a.Id)
select a;
Basically, I want to filter the results where a.Id equals a property of one of the objects in the generic list "listOfObjects". I'm getting the error "Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator."
Any ideas on how to filter this in an easily readable way using "contains" or another method?
Thanks in advance.
Just project your local list into a list of the specific items you need to filter on:
var listOfIds = listOfObjects.Select(o => o.Id);
var query =
from a in DatabaseTable
where listOfIds.Contains(a.Id)
select a;
var listOfIds = listOfObjects.Select(x => x.Id).ToList();
var query = from a in DatabaseTable
where listOfIds.Contains(a.Id)
select a;

How can I combine these linq queries into one?

Being new to LINQ, I created a couple queries and would like to combine them into one, but I am not sure how to do it. Here they are:
var u = dc.Users.Where(w => w.UserName == userName).SingleOrDefault();
var m = dc.Memberships.Where(w => w.UserId == u.UserId).SingleOrDefault();
m.PasswordQuestion = securityQuestion;
m.PasswordAnswer = securityAnswer;
dc.SubmitChanges();
dc.Users is the aspnet_Users table
dc.Membership is the aspnet_Membership table
Also, What is the difference between SingleOrDefault and FirstOrDefault?
Not sure if they have a relationship (they should). If they do, then the linq-to-sql designer will give you a User property on the membership object (or the other way around). Then you can write something like this:
var membership = dc.Memberships.Where(x => x.User.UserName == userName).SingleOrDefault();
If they don't have a relationship you can write something like this:
var membership = (from m in dc.Membership join u in dc.Users on u.UserId equals m.UserId
where u.UserName == userName
select u).SingleOrDefault();
The difference between SingleOrDefault() and FirstOrDefault() is that SingleOrDefault() assumes that there is no more then one item that matches the query. If two items match the query then a exception will be thrown. While if you use FirstOrDefault() and there is two items that match the query then the first one will be selected.
SingleOrDefault means "if there are no elements, give me the default, if there is one, give me it, otherwise, throw an exception". FirstOrDefault means "if there are no elements then give me the default, otherwise give me the first one".
As for your question about combining the queries -- why would you? If the code is working well as written, why change it?
To answer a question you didn't ask: the usual way of combining queries is to use a query continuation. A common pattern is:
var q1 = from y in something
somequeryclauses;
var q2 = from x in q1
someotherquerqyclauses;
You could write this as one big query like this:
var q2 = from x in (from y in something
somequeryclauses)
someotherqueryclauses;
which gets hard to read. You can combine the two queries with a query continuation:
var q2 = from y in something
somequeryclauses
into x
someotherqueryclauses;
Make sense?
SingleOrDefault will throw an error if your sequence contains more than one element, FirstOrDefault does not. In this case, you'd probably want FirstOrDefault, because users can have more than one membership.
var m = dc.MemberShips where(w => w.UserId.UserName == userName).FirstOrDefault();
I don't have Visual Studio in front of me, but it would be something like this:
var m = dc.Memberships.Where(w => w.Users.UserName == userName).SingleOrDefault();
m.PasswordQuestion = securityQuestion;
m.PasswordAnswer = securityAnswer;
dc.SubmitChanges();
w.Users allows you to follow the foreign key link between the membership and users tables.
SingleOrDefault will throw an exception if more than one result is returned. FirstOrDefault returns the first record only.

Categories

Resources